From cfc5d7784c556856ab31538dbb6392f07a40dece Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 21 Jan 2021 21:44:00 -0500
Subject: [PATCH 0001/1276] Resolved [#2479] v1 Create Company Issue (#2512)
- Resolved issue in creating a v1 company - issue was related to None
value for signing entity name when creating record
- Updated logging and types for request_corporate_signature
Signed-off-by: David Deal
---
cla-backend/cla/controllers/company.py | 10 +++++-----
cla-backend/cla/controllers/signing.py | 18 +++++++++---------
cla-backend/cla/models/docusign_models.py | 23 +++++++++++++----------
cla-backend/cla/routes.py | 13 +++++++------
4 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/cla-backend/cla/controllers/company.py b/cla-backend/cla/controllers/company.py
index 1f9424d78..6535eddf2 100644
--- a/cla-backend/cla/controllers/company.py
+++ b/cla-backend/cla/controllers/company.py
@@ -83,10 +83,10 @@ def get_company(company_id: str):
def create_company(auth_user: AuthUser,
company_name: str = None,
signing_entity_name: str = None,
- company_manager_id=None,
- company_manager_user_name=None,
- company_manager_user_email=None,
- user_id=None,
+ company_manager_id: str = None,
+ company_manager_user_name: str = None,
+ company_manager_user_email: str = None,
+ user_id: str = None,
response=None):
"""
Creates an company and returns the newly created company in dict format.
@@ -118,7 +118,7 @@ def create_company(auth_user: AuthUser,
"company_id": company.get("company_id")}
}
- cla.log.debug(f'{fn} - creating company with name: {company_name}')
+ cla.log.debug(f'{fn} - creating company with name: {company_name} with signing entity name: {signing_entity_name}')
company = Company()
company.set_company_id(str(uuid.uuid4()))
company.set_company_name(company_name)
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index f283bf6a4..4cd846b3d 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -45,14 +45,14 @@ def request_individual_signature(project_id, user_id, return_url_type, return_ur
def request_corporate_signature(auth_user,
- project_id,
- company_id,
+ project_id: str,
+ company_id: str,
signing_entity_name: str = None,
- send_as_email=False,
- authority_name=None,
- authority_email=None,
- return_url_type=None,
- return_url=None):
+ send_as_email: bool = False,
+ authority_name: str = None,
+ authority_email: str = None,
+ return_url_type: str = None,
+ return_url: str = None):
"""
Creates CCLA signature object that represents a company signing a CCLA.
@@ -80,8 +80,8 @@ def request_corporate_signature(auth_user,
"""
return get_signing_service().request_corporate_signature(
auth_user=auth_user,
- project_id=str(project_id),
- company_id=str(company_id),
+ project_id=project_id,
+ company_id=company_id,
signing_entity_name=signing_entity_name,
send_as_email=send_as_email,
signatory_name=authority_name,
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index e0b67edd6..c712a7dd5 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -780,14 +780,14 @@ def handle_signing_new_corporate_signature(self, signature, project, company, us
return response_model
def request_corporate_signature(self, auth_user: object,
- project_id: object,
- company_id: object,
+ project_id: str,
+ company_id: str,
signing_entity_name: str = None,
- send_as_email: object = False,
- signatory_name: object = None,
- signatory_email: object = None,
- return_url_type: object = None,
- return_url: object = None) -> object:
+ send_as_email: bool = False,
+ signatory_name: str = None,
+ signatory_email: str = None,
+ return_url_type: str = None,
+ return_url: str = None) -> object:
fn = 'models.docusign_models.request_corporate_signature'
cla.log.debug(f'{fn} - '
@@ -820,7 +820,8 @@ def request_corporate_signature(self, auth_user: object,
cla.log.debug(f'{fn} - loading user {auth_user.username}')
users_list = User().get_user_by_username(auth_user.username)
if users_list is None:
- cla.log.debug(f'{fn} - unable to load auth_user by username: {auth_user.username} from the EasyCLA database.')
+ cla.log.debug(f'{fn} - unable to load auth_user by username: {auth_user.username} '
+ 'from the EasyCLA database.')
# Lookup user in the platform user service...
us = UserService
# If found, create user record in our EasyCLA database
@@ -831,7 +832,8 @@ def request_corporate_signature(self, auth_user: object,
'Returning an error response')
return {'errors': {'user_error': 'user does not exist'}}
if len(platform_users) > 1:
- cla.log.warning(f'{fn} - more than one user with same username: {auth_user.username} - using first record.')
+ cla.log.warning(f'{fn} - more than one user with same username: {auth_user.username} - '
+ 'using first record.')
# Grab the first user from the list - should only be one that matches the search query parameters
platform_user = platform_users[0]
@@ -980,7 +982,8 @@ def request_corporate_signature(self, auth_user: object,
signatory_name=signatory_name, signatory_email=signatory_email,
send_as_email=send_as_email, return_url_type=return_url_type, return_url=return_url)
- cla.log.debug(f'{fn} - Previous unsigned CCLA signatures on file for project: {project_id}, company: {company_id}')
+ cla.log.debug(f'{fn} - Previous unsigned CCLA signatures on file for project: {project_id},'
+ f'company: {company_id}')
# TODO: should I delete all but one?
return self.handle_signing_new_corporate_signature(
signature=signatures[0], project=project, company=company, user=cla_manager_user,
diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py
index 351e1ba1c..5a3f371d8 100755
--- a/cla-backend/cla/routes.py
+++ b/cla-backend/cla/routes.py
@@ -667,6 +667,7 @@ def post_company(
auth_user,
company_name=company_name,
company_manager_id=company_manager_id,
+ signing_entity_name=company_name,
company_manager_user_name=company_manager_user_name,
company_manager_user_email=company_manager_user_email,
response=response,
@@ -1264,14 +1265,14 @@ def request_corporate_signature(
# staff_verify(user) or company_manager_verify(user, company_id)
return cla.controllers.signing.request_corporate_signature(
auth_user=auth_user,
- project_id=project_id,
- company_id=company_id,
+ project_id=str(project_id),
+ company_id=str(company_id),
signing_entity_name=signing_entity_name,
send_as_email=send_as_email,
- authority_name=authority_name,
- authority_email=authority_email,
- return_url_type=return_url_type,
- return_url=return_url,
+ authority_name=str(authority_name),
+ authority_email=str(authority_email),
+ return_url_type=str(return_url_type),
+ return_url=str(return_url),
)
From 363ea8db92c427d5bb130c0c6728012c0cf8c4bf Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 22 Jan 2021 18:41:35 +0300
Subject: [PATCH 0002/1276] [#2492] Bug/GitHub Redirect (#2513)
- Added # to GitHub redirect sign URL when appending version
Signed-off-by: wanyaland
---
cla-backend/cla/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index fa2b71628..78079d689 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -778,7 +778,7 @@ def get_full_sign_url(repository_service, installation_id, github_repository_id,
:type project_version: string
"""
- base_url = '{}/v2/repository-provider/{}/sign/{}/{}/{}'.format(cla.conf['API_BASE_URL'], repository_service,
+ base_url = '{}/v2/repository-provider/{}/sign/{}/{}/{}/#/'.format(cla.conf['API_BASE_URL'], repository_service,
str(installation_id),
str(github_repository_id),
str(change_request_id))
From 1dbb528de88ee1ccbca25b6d886883548f5af031 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 22 Jan 2021 22:01:35 +0300
Subject: [PATCH 0003/1276] [#2462] Feature/ Domain lookup (#2514)
- Leveraged org lookup (org-service) when updating/creating organizatin based on domain
Signed-off-by: wanyaland
---
cla-backend-go/v2/company/service.go | 2 +-
.../v2/organization-service/client.go | 37 +++++++++++++------
2 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 62776c054..498db2319 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1332,7 +1332,7 @@ func (s *service) GetCompanyLookup(ctx context.Context, orgName string, websiteN
}
orgClient := orgService.GetClient()
log.WithFields(f).Debug("Looking up organization by name and website")
- org, err := orgClient.SearchOrgLookup(orgName, websiteName)
+ org, err := orgClient.SearchOrgLookup(ctx, &orgName, &websiteName)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to lookup organization by name or website")
return nil, err
diff --git a/cla-backend-go/v2/organization-service/client.go b/cla-backend-go/v2/organization-service/client.go
index 39867f6f8..2d62e74a3 100644
--- a/cla-backend-go/v2/organization-service/client.go
+++ b/cla-backend-go/v2/organization-service/client.go
@@ -543,16 +543,21 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
signingEntityName = companyName
}
- // Search for an existing record by website
- existingRecords, lookupErr := osc.SearchOrganization(ctx, "", companyWebsite, "")
+ //Lookup Org based on domain
+ lookupOrg, lookupErr := osc.SearchOrgLookup(ctx, nil, &companyWebsite)
if lookupErr != nil {
log.WithFields(f).WithError(lookupErr).Warn("unable to search for existing company using company website value")
return nil, lookupErr
}
- // If we have an existing record... should only be one record if any
- if len(existingRecords) > 0 {
- updatedModel, updateErr := osc.UpdateOrg(ctx, existingRecords[0], signingEntityName)
+ if lookupOrg.Payload.ID != "" {
+ // Get org based on ID
+ existingOrg, existingOrgErr := osc.GetOrganization(ctx, lookupOrg.Payload.ID)
+ if existingOrgErr != nil {
+ log.WithFields(f).WithError(existingOrgErr).Warnf("unable to get organization : %s ", lookupOrg.Payload.ID)
+ return nil, existingOrgErr
+ }
+ updatedModel, updateErr := osc.UpdateOrg(ctx, existingOrg, signingEntityName)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warn("unable to update for existing company")
return nil, updateErr
@@ -685,12 +690,17 @@ func (osc *Client) ListOrg(ctx context.Context, orgName string) (*models.Organiz
}
// SearchOrgLookup returns organization
-func (osc *Client) SearchOrgLookup(orgName string, websiteName string) (*organizations.LookupOK, error) {
+func (osc *Client) SearchOrgLookup(ctx context.Context, orgName, websiteName *string) (*organizations.LookupOK, error) {
f := logrus.Fields{
"functionName": "organization_service.Lookup",
- "orgName": orgName,
- "websiteName": websiteName,
}
+ if orgName != nil {
+ f["orgName"] = *orgName
+ }
+ if websiteName != nil {
+ f["websiteName"] = *websiteName
+ }
+
tok, err := token.GetToken()
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to fetch token")
@@ -699,9 +709,13 @@ func (osc *Client) SearchOrgLookup(orgName string, websiteName string) (*organiz
clientAuth := runtimeClient.BearerToken(tok)
params := &organizations.LookupParams{
- Name: aws.String(orgName),
- Domain: aws.String(websiteName),
- Context: context.TODO(),
+ Context: ctx,
+ }
+ if orgName != nil {
+ params.Name = orgName
+ }
+ if websiteName != nil {
+ params.Domain = websiteName
}
result, err := osc.cl.Organizations.Lookup(params, clientAuth)
if err != nil {
@@ -710,4 +724,5 @@ func (osc *Client) SearchOrgLookup(orgName string, websiteName string) (*organiz
}
return result, nil
+
}
From 9b45372bd42133ebcbdd0d12f3200183771cfab2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 22 Jan 2021 18:22:22 -0500
Subject: [PATCH 0004/1276] Updated Permissions Checks for Create CLA Group
(#2516)
- Updated checks - check both foundation and project SFID against permissions model
- Added context and debug to common permission check functions
Signed-off-by: David Deal
---
.../utils/utils_user_auth_lambda.go | 57 +++++++++++++++--
.../utils/utils_user_auth_standalone.go | 64 +++++++++++++++----
cla-backend-go/v2/cla_groups/handlers.go | 60 ++++++++++-------
cla-backend-go/v2/company/handlers.go | 8 +--
cla-backend-go/v2/events/handlers.go | 8 +--
cla-backend-go/v2/gerrits/handlers.go | 6 +-
.../v2/github_organizations/handlers.go | 8 +--
cla-backend-go/v2/project/handlers.go | 10 +--
cla-backend-go/v2/repositories/handlers.go | 10 +--
cla-backend-go/v2/signatures/handlers.go | 26 ++++----
10 files changed, 179 insertions(+), 78 deletions(-)
diff --git a/cla-backend-go/utils/utils_user_auth_lambda.go b/cla-backend-go/utils/utils_user_auth_lambda.go
index 007687d37..8e8570386 100644
--- a/cla-backend-go/utils/utils_user_auth_lambda.go
+++ b/cla-backend-go/utils/utils_user_auth_lambda.go
@@ -6,7 +6,12 @@
package utils
import (
+ "context"
+ "strings"
+
"github.com/LF-Engineering/lfx-kit/auth"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/sirupsen/logrus"
)
const (
@@ -32,36 +37,74 @@ func IsUserAuthorizedForOrganization(user *auth.User, companySFID string, adminS
}
// IsUserAuthorizedForProjectTree helper function for determining if the user is authorized for this project hierarchy/tree
-func IsUserAuthorizedForProjectTree(user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectTree",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorized(auth.Project, projectSFID, true)
+ log.WithFields(f).Debug("checking scope...")
+ val := user.IsUserAuthorized(auth.Project, projectSFID, true)
+ log.WithFields(f).Debugf("user allowed: %T", val)
+ return val
}
// IsUserAuthorizedForProject helper function for determining if the user is authorized for this project
-func IsUserAuthorizedForProject(user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProject(ctx context.Context, user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProject",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorizedForProjectScope(projectSFID)
+ log.WithFields(f).Debug("checking scope...")
+ val := user.IsUserAuthorizedForProjectScope(projectSFID)
+ log.WithFields(f).Debugf("user allowed: %t", val)
+ return val
}
// IsUserAuthorizedForAnyProjects helper function for determining if the user is authorized for any of the specified projects
-func IsUserAuthorizedForAnyProjects(user *auth.User, projectSFIDs []string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForAnyProjects(ctx context.Context, user *auth.User, projectSFIDs []string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForAnyProjects",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFIDs": strings.Join(projectSFIDs, ","),
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
for _, projectSFID := range projectSFIDs {
- if IsUserAuthorizedForProjectTree(user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("checking project tree scope for: %s...", projectSFID)
+ if IsUserAuthorizedForProjectTree(ctx, user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("project tree scope check passed for: %s...", projectSFID)
return true
}
- if IsUserAuthorizedForProject(user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("checking project scope for: %s...", projectSFID)
+ if IsUserAuthorizedForProject(ctx, user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("project scope check passed for: %s...", projectSFID)
return true
}
}
+ log.WithFields(f).Debugf("project scope checks failed for: %s...", strings.Join(projectSFIDs, ","))
return false
}
diff --git a/cla-backend-go/utils/utils_user_auth_standalone.go b/cla-backend-go/utils/utils_user_auth_standalone.go
index a66b2fb81..09820aebf 100644
--- a/cla-backend-go/utils/utils_user_auth_standalone.go
+++ b/cla-backend-go/utils/utils_user_auth_standalone.go
@@ -6,8 +6,12 @@
package utils
import (
+ "context"
"os"
"strconv"
+ "strings"
+
+ "github.com/sirupsen/logrus"
"github.com/LF-Engineering/lfx-kit/auth"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -56,53 +60,91 @@ func IsUserAuthorizedForOrganization(user *auth.User, companySFID string, adminS
}
// IsUserAuthorizedForProjectTree helper function for determining if the user is authorized for this project hierarchy/tree
-func IsUserAuthorizedForProjectTree(user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectTree",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
return true
}
- // Previously, we checked for user.Admin - admins should be in a separate role
- // Previously, we checked for user.Allowed, which is currently not used (future flag that is currently not implemented)
- return user.IsUserAuthorized(auth.Project, projectSFID, true)
+ log.WithFields(f).Debug("checking scope...")
+ val := user.IsUserAuthorized(auth.Project, projectSFID, true)
+ log.WithFields(f).Debugf("user allowed: %T", val)
+ return val
}
// IsUserAuthorizedForProject helper function for determining if the user is authorized for this project
-func IsUserAuthorizedForProject(user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProject(ctx context.Context, user *auth.User, projectSFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProject",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
return true
}
- // Previously, we checked for user.Admin - admins should be in a separate role
- // Previously, we checked for user.Allowed, which is currently not used (future flag that is currently not implemented)
- return user.IsUserAuthorizedForProjectScope(projectSFID)
+ log.WithFields(f).Debug("checking scope...")
+ val := user.IsUserAuthorizedForProjectScope(projectSFID)
+ log.WithFields(f).Debugf("user allowed: %T", val)
+ return val
}
// IsUserAuthorizedForAnyProjects helper function for determining if the user is authorized for any of the specified projects
-func IsUserAuthorizedForAnyProjects(user *auth.User, projectSFIDs []string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForAnyProjects(ctx context.Context, user *auth.User, projectSFIDs []string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForAnyProjects",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFIDs": strings.Join(projectSFIDs, ","),
+ "adminScopeAllowed": adminScopeAllowed,
+ }
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
for _, projectSFID := range projectSFIDs {
- if IsUserAuthorizedForProjectTree(user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("checking project tree scope for: %s...", projectSFID)
+ if IsUserAuthorizedForProjectTree(ctx, user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("project tree scope check passed for: %s...", projectSFID)
return true
}
- if IsUserAuthorizedForProject(user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("checking project scope for: %s...", projectSFID)
+ if IsUserAuthorizedForProject(ctx, user, projectSFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("project scope check passed for: %s...", projectSFID)
return true
}
}
+ log.WithFields(f).Debugf("project scope checks failed for: %s...", strings.Join(projectSFIDs, ","))
return false
}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 226412854..3ce030747 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -41,7 +41,11 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
f := logrus.Fields{
"functionName": "ClaGroupCreateClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "claGroupName": aws.StringValue(params.ClaGroupInput.ClaGroupName),
+ "claGroupName": utils.StringValue(params.ClaGroupInput.ClaGroupName),
+ "foundationSFID": utils.StringValue(params.ClaGroupInput.FoundationSfid),
+ "cclaEnabled": utils.BoolValue(params.ClaGroupInput.CclaEnabled),
+ "iclaEnabled": utils.BoolValue(params.ClaGroupInput.IclaEnabled),
+ "cclaRequiresIcla": utils.BoolValue(params.ClaGroupInput.CclaRequiresIcla),
"claGroupDescription": params.ClaGroupInput.ClaGroupDescription,
"projectSFIDList": strings.Join(params.ClaGroupInput.ProjectSfidList, ","),
"authUsername": params.XUSERNAME,
@@ -49,7 +53,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, aws.StringValue(params.ClaGroupInput.FoundationSfid), projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProject(ctx, authUser, utils.StringValue(params.ClaGroupInput.FoundationSfid), params.ClaGroupInput.ProjectSfidList, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s does not have access to create a CLA Group with project scope of: %s", authUser.UserName, aws.StringValue(params.ClaGroupInput.FoundationSfid))
log.WithFields(f).Warn(msg)
return cla_group.NewCreateClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -117,7 +121,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, []string{claGroupModel.ProjectExternalID}, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s does not have access to update an existing CLA Group with project scope of: %s", authUser.UserName, claGroupModel.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewUpdateClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -190,7 +194,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, []string{claGroupModel.ProjectExternalID}, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s does not have access to delete the CLA Group with project scope of: %s", authUser.UserName, claGroupModel.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewDeleteClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -255,7 +259,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, []string{cg.ProjectExternalID}, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s does not have access to enroll projects with project scope of: %s", authUser.UserName, cg.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewEnrollProjectsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -351,7 +355,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, []string{cg.ProjectExternalID}, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s does not have access to unenroll projects with project scope of: %s", authUser.UserName, cg.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewUnenrollProjectsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -425,7 +429,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Check permissions
log.WithFields(f).Debugf("checking permissions for %s", strings.Join(projectSFIDs, ","))
- if !utils.IsUserAuthorizedForAnyProjects(authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to list projects with project scope of: %s", authUser.UserName, params.ProjectSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewListClaGroupsUnderFoundationForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -498,22 +502,34 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// isUserHaveAccessToCLAProject is a helper function to determine if the user has access to the specified project
-func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, projectSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
+func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, parentProjectSFID string, projectSFIDs []string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAProject",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "userName": authUser.UserName,
- "userEmail": authUser.Email,
+ "functionName": "isUserHaveAccessToCLAProject",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "parentProjectSFID": parentProjectSFID,
+ "projectSFIDs": strings.Join(projectSFIDs, ","),
+ "userName": authUser.UserName,
+ "userEmail": authUser.Email,
}
- log.WithFields(f).Debug("testing if user has access to project SFID")
- if utils.IsUserAuthorizedForProject(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ // Check the parent project SFID
+ log.WithFields(f).Debug("testing if user has access to the parent project SFID")
+ if utils.IsUserAuthorizedForProject(ctx, authUser, parentProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user has access to the parent project SFID: %s", parentProjectSFID)
return true
}
+ log.WithFields(f).Debugf("user does not have access to the parent project SFID: %s", parentProjectSFID)
- log.WithFields(f).Debug("user doesn't have direct access to the projectSFID - loading CLA Group from project id...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ // Check the project SFIDs
+ log.WithFields(f).Debug("testing if user has access to any of the provided project SFIDs")
+ if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user has access at least one of the provided project SFIDs: %s", strings.Join(projectSFIDs, ","))
+ return true
+ }
+ log.WithFields(f).Debugf("user does not have access any of the provided project SFID: %s", projectSFIDs)
+
+ log.WithFields(f).Debug("user doesn't have direct access to the parentProjectSFID or the provided projects SFIDs - loading CLA Group from project id...")
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(parentProjectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
return false
@@ -525,11 +541,11 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation tree...")
return true
}
- if utils.IsUserAuthorizedForProject(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation...")
return true
}
@@ -543,10 +559,10 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
return false
}
- projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
- f["projectIDs"] = strings.Join(projectSFIDs, ",")
+ mappedProjectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
+ f["mappedProjectSFIDs"] = strings.Join(mappedProjectSFIDs, ",")
log.WithFields(f).Debug("testing if user has access to any projects")
- if utils.IsUserAuthorizedForAnyProjects(authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, mappedProjectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to at least of of the projects...")
return true
}
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 8bc0580f3..393a92f85 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -545,13 +545,13 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
}
log.WithFields(f).Debug("testing if user has access to project SFID...")
- if utils.IsUserAuthorizedForProject(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID...")
return true
}
log.WithFields(f).Debug("testing if user has access to project SFID tree...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID tree...")
return true
}
@@ -591,12 +591,12 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Check the foundation permissions
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProject(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation...")
return true
}
log.WithFields(f).Debug("testing if user has access to parent foundation truee...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation tree...")
return true
}
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index e5625b761..c210a045c 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -89,7 +89,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
log.WithFields(f).Debug("checking permission...")
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get Foundation Events for foundation %s.", authUser.UserName, params.FoundationSFID)
log.WithFields(f).Warn(msg)
return WriteResponse(http.StatusForbidden, runtime.JSONMime, runtime.JSONProducer(), utils.ErrorResponseForbidden(reqID, msg))
@@ -120,7 +120,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
log.WithFields(f).Debug("checking permission...")
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get Foundation Events for foundation %s.", authUser.UserName, params.FoundationSFID)
log.WithFields(f).Warn(msg)
return events.NewGetRecentEventsForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -165,7 +165,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
log.WithFields(f).Debug("checking permission...")
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get Project Events for foundation %s.", authUser.UserName, params.ProjectSFID)
log.WithFields(f).Warn(msg)
return WriteResponse(http.StatusForbidden, runtime.JSONMime, runtime.JSONProducer(), &models.ErrorResponse{
@@ -217,7 +217,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
log.WithFields(f).Debug("checking permission...")
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get Project Events for foundation %s.", authUser.UserName, params.ProjectSFID)
log.WithFields(f).Warn(msg)
return events.NewGetRecentEventsForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index 7a2f62d0f..1dc419ab2 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -49,7 +49,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
})
}
// verify user have access to the project
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
return gerrits.NewDeleteGerritForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to DeleteGerrit with Project scope of %s",
@@ -84,7 +84,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
// verify user have access to the project
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
return gerrits.NewAddGerritForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to AddGerrit with Project scope of %s",
@@ -150,7 +150,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
// verify user have access to the project
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
return gerrits.NewListGerritsForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to ListGerrits with Project scope of %s",
diff --git a/cla-backend-go/v2/github_organizations/handlers.go b/cla-backend-go/v2/github_organizations/handlers.go
index 116bfee01..514f224a0 100644
--- a/cla-backend-go/v2/github_organizations/handlers.go
+++ b/cla-backend-go/v2/github_organizations/handlers.go
@@ -37,7 +37,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get Project GitHub Organizations with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -77,7 +77,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Add Project GitHub Organizations with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -146,7 +146,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
return github_organizations.NewDeleteProjectGithubOrganizationForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete Project GitHub Organizations with Project scope of %s",
@@ -185,7 +185,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
return github_organizations.NewUpdateProjectGithubOrganizationConfigForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Update Project GitHub Organizations with Project scope of %s",
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 74aeb669d..1eb1eeda1 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -73,7 +73,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewGetProjectByIDNotFound().WithXRequestID(reqID)
}
- if !utils.IsUserAuthorizedForProjectTree(user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
return project.NewGetProjectByIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Project By ID with Project scope of %s",
@@ -94,7 +94,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForProjectTree(user, params.ExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, user, params.ExternalID, utils.ALLOW_ADMIN_SCOPE) {
return project.NewGetProjectsByExternalIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Projects By External ID with Project scope of %s",
@@ -142,7 +142,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewGetProjectByNameNotFound().WithXRequestID(reqID)
}
- if !utils.IsUserAuthorizedForProjectTree(user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
return project.NewGetProjectByNameForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Project By Name with Project scope of %s",
@@ -179,7 +179,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewDeleteProjectByIDBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
- if !utils.IsUserAuthorizedForProjectTree(user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
return project.NewDeleteProjectByIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete Project By ID with Project scope of %s",
@@ -217,7 +217,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
}
return project.NewUpdateProjectNotFound().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
- if !utils.IsUserAuthorizedForProjectTree(user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
return project.NewUpdateProjectForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Update Project By ID with Project scope of %s",
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 21d9137e0..19380a3c4 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -42,7 +42,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Get GitHub Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -90,7 +90,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Add GitHub Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -147,7 +147,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"repositoryID": params.RepositoryID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Delete GitHub Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -205,7 +205,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"repositoryID": params.RepositoryID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Query Protected Branch GitHub Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
@@ -259,7 +259,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"repositoryID": params.RepositoryID,
}
- if !utils.IsUserAuthorizedForProjectTree(authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Update Protected Branch GitHub Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 1f8078e2b..fd9973597 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -1240,14 +1240,14 @@ func isUserHaveAccessOfSignedSignaturePDF(ctx context.Context, authUser *auth.Us
f["foundationSFID"] = foundationID
// First, check for PM access
- if utils.IsUserAuthorizedForProjectTree(authUser, foundationID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, foundationID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user is authorized for %s scope for foundation ID: %s", utils.ProjectScope, foundationID)
return true, nil
}
// In case the project tree didn't pass, let's check the project list individually - if any has access, we return true
for _, proj := range projects {
- if utils.IsUserAuthorizedForProject(authUser, proj.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, proj.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user is authorized for %s scope for project ID: %s", utils.ProjectScope, proj.ProjectSFID)
return true, nil
}
@@ -1349,11 +1349,11 @@ func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User
foundationSFID := projectCLAGroupModels[0].FoundationSFID
f["foundationSFID"] = foundationSFID
log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProjectTree(authUser, foundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, foundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation tree...")
return true
}
- if utils.IsUserAuthorizedForProject(authUser, foundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, foundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation...")
return true
}
@@ -1362,7 +1362,7 @@ func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User
projectSFIDs := getProjectIDsFromModels(f, foundationSFID, projectCLAGroupModels)
f["projectIDs"] = strings.Join(projectSFIDs, ",")
log.WithFields(f).Debug("testing if user has access to any projects")
- if utils.IsUserAuthorizedForAnyProjects(authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to at least of of the projects...")
return true
}
@@ -1382,7 +1382,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
}
log.WithFields(f).Debug("testing if user has access to project SFID")
- if utils.IsUserAuthorizedForProject(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
return true
}
@@ -1399,11 +1399,11 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation tree...")
return true
}
- if utils.IsUserAuthorizedForProject(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation...")
return true
}
@@ -1420,7 +1420,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
f["projectIDs"] = strings.Join(projectSFIDs, ",")
log.WithFields(f).Debug("testing if user has access to any projects")
- if utils.IsUserAuthorizedForAnyProjects(authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to at least of of the projects...")
return true
}
@@ -1441,13 +1441,13 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
}
log.WithFields(f).Debug("testing if user has access to project SFID...")
- if utils.IsUserAuthorizedForProject(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID...")
return true
}
log.WithFields(f).Debug("testing if user has access to project SFID tree...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID tree...")
return true
}
@@ -1487,12 +1487,12 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Check the foundation permissions
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProject(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation...")
return true
}
log.WithFields(f).Debug("testing if user has access to parent foundation tree...")
- if utils.IsUserAuthorizedForProjectTree(authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to parent foundation tree...")
return true
}
From 05d3d7dc9853c33b50fd4053febf6222c276bc4e Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Sun, 24 Jan 2021 23:00:50 +0300
Subject: [PATCH 0005/1276] [#2515] Feature/Company Sign status (#2517)
- Refactored GET for company, project CLAs by leveraging signing entity name
- Updated response with multiple companies
Signed-off-by: wanyaland
---
cla-backend-go/go.sum | 1 +
cla-backend-go/swagger/cla.v2.yaml | 36 +++++++++-
cla-backend-go/v2/company/service.go | 103 +++++++++++++++++----------
3 files changed, 101 insertions(+), 39 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index ad6e7314b..0574d172f 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -66,6 +66,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/communitybridge/easycla v1.0.99 h1:PkmkMV7cLH2Q2YNSFiGGmlyrHBXVYdsWMwbXNuMAyqw=
+github.com/communitybridge/easycla v1.0.106 h1:NLYUZUZtp9DQ0dHEQkhz9h9EMzLRmuh9udsPZk8oLoQ=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index def1edb62..480d7009d 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3486,6 +3486,12 @@ parameters:
description: the Salesforce ID of the Foundation
in: query
type: string
+ signingEntityName:
+ name: signingEntityName
+ description: The signing entity name of a Company (Salesforce and EasyCLA)
+ in: query
+ type: string
+ required: true
path-claGroupID:
name: claGroupID
description: ID of the CLA Group
@@ -4428,8 +4434,16 @@ definitions:
description: CLA Manager email
name:
type: string
-
+
company-project-cla-list:
+ type: object
+ properties:
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/company-project-cla'
+
+ company-project-cla:
type: object
properties:
signed_cla_list:
@@ -4470,6 +4484,16 @@ definitions:
title: unsigned project
description: details of unsigned project
properties:
+ company_name:
+ type: string
+ description: The company name
+ x-omitempty: false
+ example: "The Linux Foundation"
+ signing_entity_name:
+ type: string
+ description: The company signing entity name
+ x-omitempty: false
+ example: "The Linux Foundation Subsidiary 1"
cla_group_id:
type: string
x-omitempty: false
@@ -4505,6 +4529,16 @@ definitions:
title: Active CLA of the company
description: Details of the active CLA Group
properties:
+ company_name:
+ type: string
+ description: The company name
+ x-omitempty: false
+ example: "The Linux Foundation"
+ signing_entity_name:
+ type: string
+ description: The company signing entity name
+ x-omitempty: false
+ example: "The Linux Foundation Subsidiary 1"
signed_on:
type: string
x-omitempty: false
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 498db2319..09026f158 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -309,7 +309,7 @@ func (s *service) GetCompanyProjectActiveCLAs(ctx context.Context, companyID str
activeCla := &models.ActiveCla{}
out.List = append(out.List, activeCla)
go func(swg *sync.WaitGroup, signature *v1Models.Signature, acla *models.ActiveCla) {
- s.fillActiveCLA(swg, signature, acla, claGroups)
+ s.fillActiveCLA(ctx, swg, signature, acla, claGroups, companyID)
}(&wg, sig, activeCla)
}
wg.Wait()
@@ -756,12 +756,11 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
// Attempt to locate the company model in our database
log.WithFields(f).Debug("locating company by SF ID")
var companyModel *v1Models.Company
- companyModel, companyErr := s.companyRepo.GetCompanyByExternalID(ctx, companySFID)
+ companies, companyErr := s.companyRepo.GetCompaniesByExternalID(ctx, companySFID)
if companyErr != nil {
// If we were unable to find the company/org in our local database, try to auto-create based
// on the existing SF record
if companyErr == company.ErrCompanyDoesNotExist {
-
log.WithFields(f).Debug("company not found in EasyCLA database - attempting to auto-create from platform organization service record")
var createCompanyErr error
companyModel, createCompanyErr = s.autoCreateCompany(ctx, companySFID)
@@ -786,40 +785,51 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
return nil, err
}
- activeCLAList, err := s.GetCompanyProjectActiveCLAs(ctx, companyModel.CompanyID, projectSFID)
- if err != nil {
- log.WithFields(f).Warnf("problem fetching company project active CLAs, error: %+v", err)
- return nil, err
- }
-
- resp := &models.CompanyProjectClaList{
- SignedClaList: activeCLAList.List,
- UnsignedProjectList: make([]*models.UnsignedProject, 0),
- }
-
- for _, activeCLA := range activeCLAList.List {
- // remove cla groups for which we have signed cla
- log.WithFields(f).Debugf("removing CLA Groups with active CLA, CLA Group: %+v, error: %+v", activeCLA, err)
- delete(claGroups, activeCLA.ProjectID)
- }
-
- // fill details for not signed cla
- for claGroupID, claGroup := range claGroups {
- unsignedProject := &models.UnsignedProject{
- CanSign: canSign,
- ClaGroupID: claGroupID,
- ClaGroupName: claGroup.ClaGroupName,
- ProjectName: claGroup.ProjectName,
- ProjectSfid: claGroup.ProjectSFID,
- SubProjects: claGroup.SubProjects,
- IclaEnabled: claGroup.IclaEnabled,
- CclaEnabled: claGroup.CclaEnabled,
+ var companyProjectClaList = make([]*models.CompanyProjectCla, 0)
+ for _, company := range companies {
+ activeCLAList, err := s.GetCompanyProjectActiveCLAs(ctx, company.CompanyID, projectSFID)
+ if err != nil {
+ log.WithFields(f).Warnf("problem fetching company project active CLAs, error: %+v", err)
+ return nil, err
+ }
+ var companyProjectCLA = &models.CompanyProjectCla{
+ SignedClaList: activeCLAList.List,
+ UnsignedProjectList: make([]*models.UnsignedProject, 0),
+ }
+ for _, activeCLA := range activeCLAList.List {
+ // remove cla groups for which we have signed cla
+ log.WithFields(f).Debugf("removing CLA Groups with active CLA, CLA Group: %+v, error: %+v", activeCLA, err)
+ delete(claGroups, activeCLA.ProjectID)
}
- log.WithFields(f).Debugf("adding unsigned CLA Group: %+v, error: %+v", unsignedProject, err)
- resp.UnsignedProjectList = append(resp.UnsignedProjectList, unsignedProject)
+ // Get Company details
+ company, compErr := s.GetCompanyByID(ctx, company.CompanyID)
+ if compErr != nil {
+ log.WithFields(f).WithError(compErr).Warnf("unable to fetch company by ID: %s ", company.CompanyID)
+ return nil, compErr
+ }
+ // fill details for not signed cla
+ for claGroupID, claGroup := range claGroups {
+ unsignedProject := &models.UnsignedProject{
+ CompanyName: company.CompanyName,
+ SigningEntityName: company.SigningEntityName,
+ CanSign: canSign,
+ ClaGroupID: claGroupID,
+ ClaGroupName: claGroup.ClaGroupName,
+ ProjectName: claGroup.ProjectName,
+ ProjectSfid: claGroup.ProjectSFID,
+ SubProjects: claGroup.SubProjects,
+ IclaEnabled: claGroup.IclaEnabled,
+ CclaEnabled: claGroup.CclaEnabled,
+ }
+ log.WithFields(f).Debugf("adding unsigned CLA Group: %+v, error: %+v", unsignedProject, err)
+ companyProjectCLA.UnsignedProjectList = append(companyProjectCLA.UnsignedProjectList, unsignedProject)
+ }
+ companyProjectClaList = append(companyProjectClaList, companyProjectCLA)
}
- return resp, nil
+ return &models.CompanyProjectClaList{
+ List: companyProjectClaList,
+ }, nil
}
// GetCompanyCLAGroupManagers when provided the internal company ID and CLA Groups ID, this routine returns the list of
@@ -1086,7 +1096,10 @@ func fillProjectInfo(claManagers []*models.CompanyClaManager, claGroups map[stri
}
}
-func (s *service) fillActiveCLA(wg *sync.WaitGroup, sig *v1Models.Signature, activeCla *models.ActiveCla, claGroups map[string]*claGroupModel) {
+func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1Models.Signature, activeCla *models.ActiveCla, claGroups map[string]*claGroupModel, companyID string) {
+ f := logrus.Fields{
+ "functionName": "fillActiveCLA",
+ }
defer wg.Done()
cg, ok := claGroups[sig.ProjectID]
if !ok {
@@ -1094,7 +1107,20 @@ func (s *service) fillActiveCLA(wg *sync.WaitGroup, sig *v1Models.Signature, act
return
}
+ // Get Company details
+ company, compErr := s.GetCompanyByID(ctx, companyID)
+ if compErr != nil {
+ log.WithFields(f).WithError(compErr).Warnf("unable to fetch company by ID: %s ", companyID)
+ return
+ }
+
// fill details from dynamodb
+ activeCla.CompanyName = company.CompanyName
+ if company.SigningEntityName == "" {
+ activeCla.SigningEntityName = company.CompanyName
+ } else {
+ activeCla.SigningEntityName = company.SigningEntityName
+ }
activeCla.ProjectID = sig.ProjectID
if sig.SignedOn == "" {
activeCla.SignedOn = sig.SignatureCreated
@@ -1133,13 +1159,13 @@ func (s *service) fillActiveCLA(wg *sync.WaitGroup, sig *v1Models.Signature, act
}
usc := v2UserService.GetClient()
if len(sig.SignatureACL) == 0 {
- log.Warnf("signature : %s have empty signature_acl", sig.SignatureID)
+ log.WithFields(f).Warnf("signature : %s have empty signature_acl", sig.SignatureID)
return
}
lfUsername := sig.SignatureACL[0].LfUsername
user, err := usc.GetUserByUsername(lfUsername)
if err != nil {
- log.Warnf("unable to get user with lf username : %s", lfUsername)
+ log.WithFields(f).WithError(err).Warnf("unable to get user with lf username : %s", lfUsername)
return
}
signatoryName = user.Name
@@ -1310,7 +1336,8 @@ func (s service) autoCreateCompany(ctx context.Context, companySFID string) (*v1
companyModel, companyCreateErr := s.companyRepo.CreateCompany(ctx, &v1Models.Company{
CompanyExternalID: companySFID,
CompanyName: sfOrgModel.Name,
- Note: "created on-demand by v4 service based on SF Organization Service record",
+
+ Note: "created on-demand by v4 service based on SF Organization Service record",
})
if companyCreateErr != nil || companyModel == nil {
From 65311b5ca0ea09f8d6cc154363255f335cb5f38e Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 25 Jan 2021 16:33:44 +0300
Subject: [PATCH 0006/1276] [#2462] Bug/Add org
- Resolved bug caused by 404 on company lookup
Signed-off-by: wanyaland
---
.../v2/organization-service/client.go | 96 ++++++++++---------
1 file changed, 51 insertions(+), 45 deletions(-)
diff --git a/cla-backend-go/v2/organization-service/client.go b/cla-backend-go/v2/organization-service/client.go
index 2d62e74a3..a0276e7c8 100644
--- a/cla-backend-go/v2/organization-service/client.go
+++ b/cla-backend-go/v2/organization-service/client.go
@@ -532,6 +532,8 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
"companyWebsite": companyWebsite,
}
+ var org *models.Organization
+
tok, tokenErr := token.GetToken()
if tokenErr != nil {
log.WithFields(f).WithError(tokenErr).Warn("unable to fetch token")
@@ -547,45 +549,60 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
lookupOrg, lookupErr := osc.SearchOrgLookup(ctx, nil, &companyWebsite)
if lookupErr != nil {
log.WithFields(f).WithError(lookupErr).Warn("unable to search for existing company using company website value")
- return nil, lookupErr
+ if _, ok := lookupErr.(*organizations.LookupNotFound); !ok {
+ return nil, lookupErr
+ }
}
- if lookupOrg.Payload.ID != "" {
+ if lookupOrg != nil && lookupOrg.Payload.ID != "" {
// Get org based on ID
+ var updateErr error
existingOrg, existingOrgErr := osc.GetOrganization(ctx, lookupOrg.Payload.ID)
if existingOrgErr != nil {
log.WithFields(f).WithError(existingOrgErr).Warnf("unable to get organization : %s ", lookupOrg.Payload.ID)
return nil, existingOrgErr
}
- updatedModel, updateErr := osc.UpdateOrg(ctx, existingOrg, signingEntityName)
+ org, updateErr = osc.UpdateOrg(ctx, existingOrg, signingEntityName)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warn("unable to update for existing company")
return nil, updateErr
}
- return updatedModel, nil
- }
- // use linux foundation logo as default
- linuxFoundation, err := osc.SearchOrganization(ctx, utils.TheLinuxFoundation, "", "")
- if err != nil || len(linuxFoundation) == 0 {
- log.WithFields(f).WithError(err).Warn("unable to search Linux Foundation organization")
- return nil, err
- }
+ } else {
+ // use linux foundation logo as default
+ linuxFoundation, err := osc.SearchOrganization(ctx, utils.TheLinuxFoundation, "", "")
+ if err != nil || len(linuxFoundation) == 0 {
+ log.WithFields(f).WithError(err).Warn("unable to search Linux Foundation organization")
+ return nil, err
+ }
- clientAuth := runtimeClient.BearerToken(tok)
- description := "No Description"
- f["description"] = description
- companyType := "Customer"
- f["type"] = companyType
- f["companyType"] = companyType
- companySource := "No Source"
- industry := "No Industry"
- f["industry"] = industry
- logoURL := linuxFoundation[0].LogoURL
- f["logoURL"] = logoURL
-
- params := &organizations.CreateOrgParams{
- Org: &models.CreateOrg{
+ clientAuth := runtimeClient.BearerToken(tok)
+ description := "No Description"
+ f["description"] = description
+ companyType := "Customer"
+ f["type"] = companyType
+ f["companyType"] = companyType
+ companySource := "No Source"
+ industry := "No Industry"
+ f["industry"] = industry
+ logoURL := linuxFoundation[0].LogoURL
+ f["logoURL"] = logoURL
+
+ params := &organizations.CreateOrgParams{
+ Org: &models.CreateOrg{
+ Description: &description,
+ Name: &companyName,
+ Website: &companyWebsite,
+ Industry: &industry,
+ Source: &companySource,
+ Type: &companyType,
+ LogoURL: &logoURL,
+ SigningEntityName: []string{signingEntityName},
+ },
+ Context: ctx,
+ }
+
+ log.WithFields(f).Debugf("Creating organization with params: %+v", models.CreateOrg{
Description: &description,
Name: &companyName,
Website: &companyWebsite,
@@ -594,28 +611,17 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
Type: &companyType,
LogoURL: &logoURL,
SigningEntityName: []string{signingEntityName},
- },
- Context: ctx,
- }
+ })
+ result, err := osc.cl.Organizations.CreateOrg(params, clientAuth)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("Failed to create salesforce Company :%s , err: %+v ", companyName, err)
+ return nil, err
+ }
+ log.WithFields(f).Infof("Company: %s successfuly created ", companyName)
- log.WithFields(f).Debugf("Creating organization with params: %+v", models.CreateOrg{
- Description: &description,
- Name: &companyName,
- Website: &companyWebsite,
- Industry: &industry,
- Source: &companySource,
- Type: &companyType,
- LogoURL: &logoURL,
- SigningEntityName: []string{signingEntityName},
- })
- result, err := osc.cl.Organizations.CreateOrg(params, clientAuth)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("Failed to create salesforce Company :%s , err: %+v ", companyName, err)
- return nil, err
+ org = result.Payload
}
- log.WithFields(f).Infof("Company: %s successfuly created ", companyName)
-
- return result.Payload, err
+ return org, nil
}
// UpdateOrg updates the company record based on the provided name, signingEntityName, and website
From 997dd967a1235818b303d7a6ded40e077df1f981 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 25 Jan 2021 20:11:48 +0200
Subject: [PATCH 0007/1276] adding the # for the frontend to work properly
(#2520)
Signed-off-by: makkalot
---
cla-backend/cla/models/github_models.py | 1 +
cla-backend/cla/utils.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index d94d053bf..01c530c4a 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -1033,6 +1033,7 @@ def update_pull_request(installation_id, github_repository_id, pull_request, rep
# specified default value per issue #166
context, body = cla.utils.assemble_cla_status(context_name, signed=True)
sign_url = cla.conf["CLA_LANDING_PAGE"] # Remove this once signature detail page ready.
+ sign_url = os.path.join(sign_url, "#/")
sign_url = append_project_version_to_url(address=sign_url, project_version=project_version)
cla.log.debug(f'Creating new CLA {state} status - {len(signed)} passed, {missing}, signing url: {sign_url}')
create_commit_status(pull_request, last_commit.sha, state, sign_url, body, context)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 78079d689..e12f04edc 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -827,6 +827,7 @@ def get_comment_badge(repository_type, all_signed, sign_url, project_version, mi
if all_signed:
badge_url = f'{CLA_LOGO_URL}/cla-signed.svg'
badge_hyperlink = cla.conf["CLA_LANDING_PAGE"]
+ badge_hyperlink = os.path.join(badge_hyperlink, "#/")
badge_hyperlink = append_project_version_to_url(address=badge_hyperlink, project_version=project_version)
alt = "CLA Signed"
else:
From 8742a10f8aeb6a7ae370447b59c18ce5674ee4e1 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 26 Jan 2021 00:59:30 +0300
Subject: [PATCH 0008/1276] [#2515] Feature/ Company Project CLA (#2521)
- Added company_id and signatureACL to response
Signed-off-by: wanyaland
---
cla-backend-go/go.sum | 1 +
cla-backend-go/swagger/cla.v2.yaml | 6 +++++
.../swagger/common/signature-acl.yaml | 10 ++++++++
cla-backend-go/v2/company/service.go | 23 +++++++++++++++++++
4 files changed, 40 insertions(+)
create mode 100644 cla-backend-go/swagger/common/signature-acl.yaml
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 0574d172f..4807b6946 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -67,6 +67,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/communitybridge/easycla v1.0.99 h1:PkmkMV7cLH2Q2YNSFiGGmlyrHBXVYdsWMwbXNuMAyqw=
github.com/communitybridge/easycla v1.0.106 h1:NLYUZUZtp9DQ0dHEQkhz9h9EMzLRmuh9udsPZk8oLoQ=
+github.com/communitybridge/easycla v1.0.107 h1:dktHAji1yJ1nMEu54z4paPWOM4Q7A9rryc0OCADfAcY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 480d7009d..37b390f4f 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -4494,6 +4494,8 @@ definitions:
description: The company signing entity name
x-omitempty: false
example: "The Linux Foundation Subsidiary 1"
+ signing_entity_id:
+ $ref: './common/properties/internal-id.yaml'
cla_group_id:
type: string
x-omitempty: false
@@ -4539,6 +4541,10 @@ definitions:
description: The company signing entity name
x-omitempty: false
example: "The Linux Foundation Subsidiary 1"
+ signing_entity_id:
+ $ref: './common/properties/internal-id.yaml'
+ signature_acl:
+ $ref: './common/signature-acl.yaml'
signed_on:
type: string
x-omitempty: false
diff --git a/cla-backend-go/swagger/common/signature-acl.yaml b/cla-backend-go/swagger/common/signature-acl.yaml
new file mode 100644
index 000000000..4d245e56b
--- /dev/null
+++ b/cla-backend-go/swagger/common/signature-acl.yaml
@@ -0,0 +1,10 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+title: signature acl list representing cla managers access list
+properties:
+ username_list:
+ type: array
+ items:
+ type: string
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 09026f158..4db52ef49 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -812,6 +812,7 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
unsignedProject := &models.UnsignedProject{
CompanyName: company.CompanyName,
SigningEntityName: company.SigningEntityName,
+ SigningEntityID: company.CompanyID,
CanSign: canSign,
ClaGroupID: claGroupID,
ClaGroupName: claGroup.ClaGroupName,
@@ -825,6 +826,13 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
companyProjectCLA.UnsignedProjectList = append(companyProjectCLA.UnsignedProjectList, unsignedProject)
}
companyProjectClaList = append(companyProjectClaList, companyProjectCLA)
+
+ // refresh clagroups for next company instance
+ claGroups, err = s.getCLAGroupsUnderProjectOrFoundation(ctx, projectSFID)
+ if err != nil {
+ log.WithFields(f).Warnf("problem fetching CLA Groups under project or foundation, error: %+v", err)
+ return nil, err
+ }
}
return &models.CompanyProjectClaList{
@@ -1114,6 +1122,17 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
return
}
+ // Update acl
+ var acl = make([]string, 0)
+ if len(sig.SignatureACL) > 0 {
+ log.WithFields(f).Debugf("updating signature acl: %+v list for lfusernames...", sig.SignatureACL)
+ for _, manager := range sig.SignatureACL {
+ if manager.LfUsername != "" {
+ acl = append(acl, manager.LfUsername)
+ }
+ }
+ }
+
// fill details from dynamodb
activeCla.CompanyName = company.CompanyName
if company.SigningEntityName == "" {
@@ -1127,6 +1146,10 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
} else {
activeCla.SignedOn = sig.SignedOn
}
+ activeCla.SigningEntityID = companyID
+ activeCla.SignatureACL = &models.ActiveClaSignatureACL{
+ UsernameList: acl,
+ }
activeCla.ClaGroupName = cg.ClaGroupName
activeCla.SignatureID = sig.SignatureID.String()
From 4ce9b750dc0c5c293594a42a258bc1650a95f7b7 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 26 Jan 2021 03:47:01 +0300
Subject: [PATCH 0009/1276] [#2515] Feature/ Get Events Signing Entity Name
(#2522)
- Factored in internal companyID based on signing entity name to fetch corresponding events
Signed-off-by: wanyaland
---
cla-backend-go/events/mockrepo.go | 4 ++--
cla-backend-go/events/repository.go | 32 +++++++++++++++++++---------
cla-backend-go/events/service.go | 12 +++++------
cla-backend-go/swagger/cla.v2.yaml | 4 ++--
cla-backend-go/v2/events/handlers.go | 17 ++++++++++-----
5 files changed, 44 insertions(+), 25 deletions(-)
diff --git a/cla-backend-go/events/mockrepo.go b/cla-backend-go/events/mockrepo.go
index f74ae1373..4f82ab047 100644
--- a/cla-backend-go/events/mockrepo.go
+++ b/cla-backend-go/events/mockrepo.go
@@ -19,11 +19,11 @@ func (repo *mockRepository) AddDataToEvent(eventID, foundationSFID, projectSFID,
panic("implement me")
}
-func (repo *mockRepository) GetCompanyFoundationEvents(companySFID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+func (repo *mockRepository) GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
panic("implement me")
}
-func (repo *mockRepository) GetCompanyClaGroupEvents(companySFID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+func (repo *mockRepository) GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
panic("implement me")
}
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index e0204a371..01a825b73 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -59,8 +59,8 @@ type Repository interface {
SearchEvents(params *eventOps.SearchEventsParams, pageSize int64) (*models.EventList, error)
GetRecentEvents(pageSize int64) (*models.EventList, error)
- GetCompanyFoundationEvents(companySFID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
- GetCompanyClaGroupEvents(companySFID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
+ GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
+ GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
GetCompanyEvents(companyID, eventType string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
GetFoundationEvents(foundationSFID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error)
GetClaGroupEvents(claGroupID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error)
@@ -308,7 +308,7 @@ func (repo *repository) SearchEvents(params *eventOps.SearchEventsParams, pageSi
}
// queryEventsTable queries events table on index
-func (repo *repository) queryEventsTable(indexName string, condition expression.KeyConditionBuilder, nextKey *string, pageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
+func (repo *repository) queryEventsTable(indexName string, condition expression.KeyConditionBuilder, filter *expression.ConditionBuilder, nextKey *string, pageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
f := logrus.Fields{
"functionName": "events.queryEventsTable",
"indexName": indexName,
@@ -320,10 +320,14 @@ func (repo *repository) queryEventsTable(indexName string, condition expression.
log.WithFields(f).Debug("querying events table")
builder := expression.NewBuilder() // .WithProjection(buildProjection())
+
// The table we're interested in
tableName := fmt.Sprintf("cla-%s-events", repo.stage)
builder = builder.WithKeyCondition(condition)
+ if filter != nil {
+ builder = builder.WithFilter(*filter)
+ }
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
@@ -464,17 +468,25 @@ func buildNextKey(indexName string, event *models.Event) (string, error) {
}
// GetCompanyFoundationEvents returns the list of events for foundation and company
-func (repo *repository) GetCompanyFoundationEvents(companySFID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
key := fmt.Sprintf("%s#%s", companySFID, foundationSFID)
keyCondition := expression.Key("company_sfid_foundation_sfid").Equal(expression.Value(key))
- return repo.queryEventsTable(CompanySFIDFoundationSFIDEpochIndex, keyCondition, nextKey, paramPageSize, all, nil)
+ var filter expression.ConditionBuilder
+ if companyID != "" {
+ filter = expression.Name("company_id").Equal(expression.Value(companyID))
+ }
+ return repo.queryEventsTable(CompanySFIDFoundationSFIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
// GetCompanyClaGroupEvents returns the list of events for cla group and the company
-func (repo *repository) GetCompanyClaGroupEvents(companySFID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+func (repo *repository) GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
key := fmt.Sprintf("%s#%s", companySFID, claGroupID)
keyCondition := expression.Key("company_sfid_project_id").Equal(expression.Value(key))
- return repo.queryEventsTable(CompanySFIDProjectIDEpochIndex, keyCondition, nextKey, paramPageSize, all, nil)
+ var filter expression.ConditionBuilder
+ if companyID != "" {
+ filter = expression.Name("company_id").Equal(expression.Value(companyID))
+ }
+ return repo.queryEventsTable(CompanySFIDProjectIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
// GetCompanyEvents returns the list of events for given company id and event types
@@ -482,19 +494,19 @@ func (repo *repository) GetCompanyEvents(companyID, eventType string, nextKey *s
keyCondition := expression.Key("company_id").Equal(expression.Value(companyID)).And(
expression.Key("event_type").Equal(expression.Value(eventType)))
- return repo.queryEventsTable(CompanyIDEventTypeIndex, keyCondition, nextKey, paramPageSize, all, nil)
+ return repo.queryEventsTable(CompanyIDEventTypeIndex, keyCondition, nil, nextKey, paramPageSize, all, nil)
}
// GetFoundationEvents returns the list of foundation events
func (repo *repository) GetFoundationEvents(foundationSFID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
keyCondition := expression.Key("event_foundation_sfid").Equal(expression.Value(foundationSFID))
- return repo.queryEventsTable(EventFoundationSFIDEpochIndex, keyCondition, nextKey, paramPageSize, all, searchTerm)
+ return repo.queryEventsTable(EventFoundationSFIDEpochIndex, keyCondition, nil, nextKey, paramPageSize, all, searchTerm)
}
// GetClaGroupEvents returns the list of cla-group events
func (repo *repository) GetClaGroupEvents(claGroupID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
keyCondition := expression.Key("event_project_id").Equal(expression.Value(claGroupID))
- return repo.queryEventsTable(EventProjectIDEpochIndex, keyCondition, nextKey, paramPageSize, all, searchTerm)
+ return repo.queryEventsTable(EventProjectIDEpochIndex, keyCondition, nil, nextKey, paramPageSize, all, searchTerm)
}
// toString encodes the map as a string
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 622fdf056..34d0439d7 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -30,8 +30,8 @@ type Service interface {
GetFoundationEvents(foundationSFID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error)
GetClaGroupEvents(claGroupID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error)
- GetCompanyFoundationEvents(companySFID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
- GetCompanyClaGroupEvents(companySFID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
+ GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
+ GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
GetCompanyEvents(companyID, eventType string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error)
}
@@ -91,13 +91,13 @@ func (s *service) GetClaGroupEvents(projectSFDC string, nextKey *string, paramPa
}
// GetCompanyFoundationEvents returns list of events for company and foundation
-func (s *service) GetCompanyFoundationEvents(companySFID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
- return s.repo.GetCompanyFoundationEvents(companySFID, foundationSFID, nextKey, paramPageSize, all)
+func (s *service) GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+ return s.repo.GetCompanyFoundationEvents(companySFID, companyID, foundationSFID, nextKey, paramPageSize, all)
}
// GetCompanyClaGroupEvents returns list of events for company and cla group
-func (s *service) GetCompanyClaGroupEvents(companySFID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
- return s.repo.GetCompanyClaGroupEvents(companySFID, claGroupID, nextKey, paramPageSize, all)
+func (s *service) GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
+ return s.repo.GetCompanyClaGroupEvents(companySFID, companyID, claGroupID, nextKey, paramPageSize, all)
}
func (s *service) GetCompanyEvents(companyID, eventType string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 37b390f4f..51f1cf4d9 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1068,7 +1068,7 @@ paths:
tags:
- events
- /company/{companySFID}/project/{projectSFID}/events:
+ /company/{companyID}/project/{projectSFID}/events:
get:
summary: Get recent events of company and project
description: Returns list of events of company and project
@@ -1080,7 +1080,7 @@ paths:
- $ref: "#/parameters/x-email"
- $ref: '#/parameters/pageSize'
- $ref: '#/parameters/path-projectSFID'
- - $ref: '#/parameters/path-companySFID'
+ - $ref: '#/parameters/path-companyID'
- $ref: '#/parameters/nextKey'
- $ref: '#/parameters/returnAllEvents'
produces:
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index c210a045c..365bad29f 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -279,13 +279,20 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
}
- if !utils.IsUserAuthorizedForOrganization(authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
+
+ v1Company, compErr := v1CompanyRepo.GetCompany(ctx, params.CompanyID)
+ if compErr != nil {
+ log.WithFields(f).Warnf("unable to fetch company by ID:%s ", params.CompanyID)
+ return events.NewGetCompanyProjectEventsBadRequest().WithPayload(errorResponse(reqID, compErr))
+ }
+
+ if !utils.IsUserAuthorizedForOrganization(authUser, v1Company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
return events.NewGetCompanyProjectEventsForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to GetCompanyProject Events with Organization scope of %s",
- authUser.UserName, params.CompanySFID),
+ authUser.UserName, v1Company.CompanyExternalID),
XRequestID: reqID,
})
}
@@ -300,7 +307,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
var result *v1Models.EventList
if projectDetails.ProjectType == utils.ProjectTypeProjectGroup {
- result, err = service.GetCompanyFoundationEvents(params.CompanySFID, params.ProjectSFID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
+ result, err = service.GetCompanyFoundationEvents(v1Company.CompanyExternalID, params.CompanyID, params.ProjectSFID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
} else {
pm, perr := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
if perr != nil {
@@ -314,7 +321,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
log.WithFields(f).WithError(perr).Warnf("problem determining CLA Group for project SFID: %s", params.ProjectSFID)
return events.NewGetCompanyProjectEventsInternalServerError().WithPayload(errorResponse(reqID, perr))
}
- result, err = service.GetCompanyClaGroupEvents(params.CompanySFID, pm.ClaGroupID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
+ result, err = service.GetCompanyClaGroupEvents(v1Company.CompanyExternalID, params.CompanyID, pm.ClaGroupID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
}
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading events")
From 4f585d252bbaf7a376219dbe4aeb517d89f513ab Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 25 Jan 2021 20:52:28 -0500
Subject: [PATCH 0010/1276] Updated v2 Corporate Console APIs - Changed SFID to
Internal ID (#2524)
- Updated many API calls to switch from SFID to internal ID due to our
support of signing entity names. As a result of this introduction, we
now can have zero, one or more than 1 internal company records matching
a single SF parent record. As a result, the APIs were updated to force
the client to provide the interal ID.
- Updated logging and permission checks to lookup SFID to check for
platform permissions
Signed-off-by: David Deal
---
cla-backend-go/cmd/server.go | 66 ++---
cla-backend-go/swagger/cla.v2.yaml | 149 +++++++----
.../company-signing-entity-name.yaml | 2 +-
cla-backend-go/utils/constants.go | 3 +
cla-backend-go/utils/responses.go | 18 ++
cla-backend-go/v2/cla_manager/errors.go | 39 +++
cla-backend-go/v2/cla_manager/handlers.go | 249 +++++++++---------
cla-backend-go/v2/cla_manager/service.go | 202 +++++++++-----
cla-backend-go/v2/company/handlers.go | 150 ++++++-----
cla-backend-go/v2/company/service.go | 112 +++++---
10 files changed, 611 insertions(+), 379 deletions(-)
create mode 100644 cla-backend-go/v2/cla_manager/errors.go
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 6ac014d60..9242288c8 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -68,7 +68,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/auth"
- "github.com/communitybridge/easycla/cla-backend-go/company"
+ v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/docraptor"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/restapi"
@@ -120,7 +120,7 @@ func init() {
type combinedRepo struct {
users.UserRepository
- company.IRepository
+ v1Company.IRepository
project.ProjectRepository
}
@@ -232,8 +232,8 @@ func server(localMode bool) http.Handler {
gerritRepo := gerrits.NewRepository(awsSession, stage)
templateRepo := template.NewRepository(awsSession, stage)
approvalListRepo := approval_list.NewRepository(awsSession, stage)
- companyRepo := company.NewRepository(awsSession, stage)
- signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo)
+ v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo)
projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
projectRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
eventsRepo := events.NewRepository(awsSession, stage)
@@ -244,7 +244,7 @@ func server(localMode bool) http.Handler {
// Our service layer handlers
eventsService := events.NewService(eventsRepo, combinedRepo{
usersRepo,
- companyRepo,
+ v1CompanyRepo,
projectRepo,
})
@@ -259,23 +259,23 @@ func server(localMode bool) http.Handler {
usersService := users.NewService(usersRepo, eventsService)
healthService := health.New(Version, Commit, Branch, BuildDate)
templateService := template.NewService(stage, templateRepo, docraptorClient, awsSession)
- projectService := project.NewService(projectRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo)
- v2ProjectService := v2Project.NewService(projectService, projectRepo, projectClaGroupRepo)
- companyService := company.NewService(companyRepo, configFile.CorporateConsoleURL, userRepo, usersService)
- v2CompanyService := v2Company.NewService(companyService, signaturesRepo, projectRepo, usersRepo, companyRepo, projectClaGroupRepo, eventsService)
- v2SignService := sign.NewService(configFile.ClaV1ApiURL, companyRepo, projectRepo, projectClaGroupRepo, companyService)
- signaturesService := signatures.NewService(signaturesRepo, companyService, usersService, eventsService, githubOrgValidation)
- v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, projectService, companyService, signaturesService, projectClaGroupRepo)
- v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, companyService, projectService, usersService, signaturesService, eventsService, configFile.CorporateConsoleURL)
- repositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
+ v1ProjectService := project.NewService(projectRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo)
+ v2ProjectService := v2Project.NewService(v1ProjectService, projectRepo, projectClaGroupRepo)
+ v1CompanyService := v1Company.NewService(v1CompanyRepo, configFile.CorporateConsoleURL, userRepo, usersService)
+ v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, projectRepo, usersRepo, v1CompanyRepo, projectClaGroupRepo, eventsService)
+ v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, projectRepo, projectClaGroupRepo, v1CompanyService)
+ v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
+ v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, projectClaGroupRepo)
+ v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, configFile.CorporateConsoleURL)
+ v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
- v2ClaManagerService := v2ClaManager.NewService(companyService, projectService, v1ClaManagerService, usersService, repositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
- approvalListService := approval_list.NewService(approvalListRepo, usersRepo, companyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleURL, http.DefaultClient)
+ v2ClaManagerService := v2ClaManager.NewService(v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
+ v1ApprovalListService := approval_list.NewService(approvalListRepo, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleURL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
- autoEnableService := dynamo_events.NewAutoEnableService(repositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, projectService)
+ autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, eventsService, autoEnableService)
gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{
LfBaseURL: configFile.LFGroup.ClientURL,
@@ -283,7 +283,7 @@ func server(localMode bool) http.Handler {
ClientSecret: configFile.LFGroup.ClientSecret,
RefreshToken: configFile.LFGroup.RefreshToken,
})
- v2ClaGroupService := cla_groups.NewService(projectService, templateService, projectClaGroupRepo, v1ClaManagerService, signaturesService, metricsRepo, gerritService, repositoriesService, eventsService)
+ v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, projectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
sessionStore, err := dynastore.New(dynastore.Path("/"), dynastore.HTTPOnly(), dynastore.TableName(configFile.SessionStoreTableName), dynastore.DynamoDB(dynamodb.New(awsSession)))
if err != nil {
@@ -298,35 +298,35 @@ func server(localMode bool) http.Handler {
// Setup our API handlers
users.Configure(api, usersService, eventsService)
- project.Configure(api, projectService, eventsService, gerritService, repositoriesService, signaturesService)
- v2Project.Configure(v2API, projectService, v2ProjectService, eventsService)
+ project.Configure(api, v1ProjectService, eventsService, gerritService, v1RepositoriesService, v1SignaturesService)
+ v2Project.Configure(v2API, v1ProjectService, v2ProjectService, eventsService)
health.Configure(api, healthService)
v2Health.Configure(v2API, healthService)
template.Configure(api, templateService, eventsService)
v2Template.Configure(v2API, templateService, eventsService)
github.Configure(api, configFile.Github.ClientID, configFile.Github.ClientSecret, configFile.Github.AccessToken, sessionStore)
- signatures.Configure(api, signaturesService, sessionStore, eventsService)
- v2Signatures.Configure(v2API, projectService, projectRepo, companyService, signaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
- approval_list.Configure(api, approvalListService, sessionStore, signaturesService, eventsService)
- company.Configure(api, companyService, usersService, companyUserValidation, eventsService)
+ signatures.Configure(api, v1SignaturesService, sessionStore, eventsService)
+ v2Signatures.Configure(v2API, v1ProjectService, projectRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
+ approval_list.Configure(api, v1ApprovalListService, sessionStore, v1SignaturesService, eventsService)
+ v1Company.Configure(api, v1CompanyService, usersService, companyUserValidation, eventsService)
docs.Configure(api)
v2Docs.Configure(v2API)
version.Configure(api, Version, Commit, Branch, BuildDate)
v2Version.Configure(v2API, Version, Commit, Branch, BuildDate)
events.Configure(api, eventsService)
- v2Events.Configure(v2API, eventsService, companyRepo, projectClaGroupRepo)
- v2Metrics.Configure(v2API, v2MetricsService, companyRepo)
+ v2Events.Configure(v2API, eventsService, v1CompanyRepo, projectClaGroupRepo)
+ v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
- repositories.Configure(api, repositoriesService, eventsService)
+ repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
- gerrits.Configure(api, gerritService, projectService, eventsService)
- v2Gerrits.Configure(v2API, gerritService, projectService, eventsService, projectClaGroupRepo)
- v2Company.Configure(v2API, v2CompanyService, companyRepo, projectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleURL)
- cla_manager.Configure(api, v1ClaManagerService, companyService, projectService, usersService, signaturesService, eventsService, configFile.CorporateConsoleURL)
- v2ClaManager.Configure(v2API, v2ClaManagerService, configFile.LFXPortalURL, projectClaGroupRepo, userRepo)
+ gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
+ v2Gerrits.Configure(v2API, gerritService, v1ProjectService, eventsService, projectClaGroupRepo)
+ v2Company.Configure(v2API, v2CompanyService, projectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleURL)
+ cla_manager.Configure(api, v1ClaManagerService, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, configFile.CorporateConsoleURL)
+ v2ClaManager.Configure(v2API, v2ClaManagerService, v1CompanyService, configFile.LFXPortalURL, projectClaGroupRepo, userRepo)
sign.Configure(v2API, v2SignService)
- cla_groups.Configure(v2API, v2ClaGroupService, projectService, projectClaGroupRepo, eventsService)
+ cla_groups.Configure(v2API, v2ClaGroupService, v1ProjectService, projectClaGroupRepo, eventsService)
v2GithubActivity.Configure(v2API, v2GithubActivityService)
userCreaterMiddleware := func(next http.Handler) http.Handler {
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 51f1cf4d9..819e63cc9 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -2211,7 +2211,7 @@ paths:
$ref: '#/responses/internal-server-error'
tags:
- signatures
-
+
/company/{companySFID}/user/{userLFID}/claGroupID/{claGroupID}/is-cla-manager-designee:
get:
summary: Checks cla-manager-designee role
@@ -2405,7 +2405,7 @@ paths:
tags:
- company
- /company/{companySFID}/project/{projectSFID}/cla-manager/requests:
+ /company/{companyID}/project/{projectSFID}/cla-manager/requests:
post:
summary: Adds a CLA Manager Designee to the specified Company and Project
description: User proposes a CLA Manager making the proposed user CLA Manager Designee
@@ -2415,7 +2415,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-projectSFID"
- name: body
in: body
@@ -2467,7 +2467,7 @@ paths:
- cla-manager
- /company/{companySFID}/project/{projectSFID}/cla-manager:
+ /company/{companyID}/project/{projectSFID}/cla-manager:
post:
summary: Adds a new CLA Manager to the specified Company and Project
description: Allows an existing CLA Manager to add another CLA Manager to the specified Company and Project.
@@ -2478,7 +2478,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- name: body
in: body
schema:
@@ -2542,9 +2542,9 @@ paths:
tags:
- company
- /company/{companySFID}/project/{projectSFID}/cla-manager/{userLFID}:
+ /company/{companyID}/project/{projectSFID}/cla-manager/{userLFID}:
delete:
- summary: Removes the CLA Manager from ACL for specified Company and Project
+ summary: Deletes the CLA Manager from CLA Manager list for specified Company and Project
description: Allows an existing CLA Manager to remove another CLA Manager from the specified Company and Project.
operationId: deleteCLAManager
parameters:
@@ -2553,7 +2553,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-userLFID"
responses:
'204':
@@ -2573,9 +2573,7 @@ paths:
tags:
- cla-manager
-
-
- /company/{companySFID}/claGroup/{claGroupID}/cla-manager-designee:
+ /company/{companyID}/claGroup/{claGroupID}/cla-manager-designee:
post:
summary: Assigns CLA Manager designee
description: Assigns CLA Manager designee to a given user
@@ -2586,7 +2584,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-claGroupID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- name: body
in: body
schema:
@@ -2620,7 +2618,7 @@ paths:
tags:
- cla-manager
- /company/{companySFID}/project/{projectSFID}/cla-manager-designee:
+ /company/{companyID}/project/{projectSFID}/cla-manager-designee:
post:
summary: Assigns CLA Manager designee
description: Assigns CLA Manager designee to a given user
@@ -2631,7 +2629,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- name: body
in: body
schema:
@@ -2940,6 +2938,41 @@ paths:
tags:
- company
+ /company/entityname/{signingEntityName}:
+ get:
+ summary: Gets the company by name
+ description: Returns the matching company by name
+ operationId: getCompanyBySigningEntityName
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: '#/parameters/path-signingEntityName'
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/company'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - company
+
/company/id/{companyID}:
delete:
summary: Deletes the company by ID
@@ -2972,7 +3005,7 @@ paths:
$ref: '#/responses/internal-server-error'
tags:
- company
-
+
/company/lookup:
get:
summary: Search companies from organization service
@@ -3047,7 +3080,7 @@ paths:
tags:
- company
- /company/{companySFID}/project/{projectSFID}/cla-managers:
+ /company/{companyID}/project/{projectSFID}/cla-managers:
get:
summary: Get CLA manager of company for particular project/foundation
description: Returns list CLA managers of the company for project/foundation
@@ -3057,7 +3090,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-projectSFID"
responses:
'200':
@@ -3109,7 +3142,7 @@ paths:
tags:
- company
- /company/{companySFID}/project/{projectSFID}/active-cla-list:
+ /company/{companyID}/project/{projectSFID}/active-cla-list:
get:
summary: Get active CLA list of company for particular project/foundation
description: Returns list active CLA of the company under particular project/foundation
@@ -3119,7 +3152,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-projectSFID"
responses:
'200':
@@ -3140,6 +3173,7 @@ paths:
$ref: '#/responses/not-found'
tags:
- company
+
/company/{companySFID}/project/{projectSFID}/contributors:
get:
summary: Get corporate contributors for project
@@ -3565,6 +3599,16 @@ parameters:
type: string
required: true
pattern: '^([\w\d\s\-\,\./]+){2,255}$'
+ path-signingEntityName:
+ name: signingEntityName
+ type: string
+ description: Signing Entity Name of the Company
+ # Pattern aligns with UI and other platform services including Org Service
+ pattern: '[^<>]*' # allow everything except greater than and less than symbols
+ minLength: 2
+ maxLength: 100
+ in: path
+ required: true
path-signatureID:
name: signatureID
description: id of the CLA signature
@@ -3973,19 +4017,19 @@ definitions:
Source:
type: string
description: >-
- The company account source, such as "Google Natural Search", "Event-Promo", "Direct Mail", or "Tradeshow".
- If the information was found in clearbit, this value will be "clearbit".
+ The company account source, such as "Google Natural Search", "Event-Promo", "Direct Mail", or "Tradeshow".
+ If the information was found in clearbit, this value will be "clearbit".
example: "clearbit"
Industry:
type: string
description: >-
- The company industry, such as "Banking" or "Communications"
+ The company industry, such as "Banking" or "Communications"
example: "Education"
pattern: '^([\w\d\s\-\,\.]+){2,40}$'
Sector:
type: string
description: >-
- The company industry sector, such as "Information Technology"
+ The company industry sector, such as "Information Technology"
example: "Information Technology"
Employees:
type: string
@@ -4127,8 +4171,8 @@ definitions:
items:
$ref:
'#/definitions/cla-manager-designee'
-
-
+
+
user-role-status:
type: object
title: User Role status
@@ -4249,20 +4293,20 @@ definitions:
assigned_on:
type: string
x-omitempty: false
+ company_id:
+ $ref: './common/properties/internal-id.yaml'
+ description: 'the Company/Organization internal ID'
+ x-omitempty: false
company_sfid:
- type: string
- description: 'the Organization SalesForce ID'
+ $ref: './common/properties/external-id.yaml'
+ description: 'the Company/Organization SalesForce ID'
x-omitempty: false
- example: 'abc134234adsdf43'
project_sfid:
- type: string
+ $ref: './common/properties/external-id.yaml'
description: 'the project SalesForce ID'
x-omitempty: false
- example: 'a2g17000000hyxNAAA'
project_name:
- type: string
- description: 'name of the salesforce project'
- example: 'Appium'
+ $ref: './common/properties/project-name.yaml'
x-omitempty: false
company-cla-manager:
@@ -4294,31 +4338,32 @@ definitions:
type: string
x-omitempty: false
project_id:
- type: string
+ $ref: './common/properties/internal-id.yaml'
description: "The Project ID"
x-omitempty: false
- example: "e1e30240-a722-4c82-a648-121681d959c7"
project_sfid:
- type: string
+ $ref: './common/properties/external-id.yaml'
description: "The Project SalesForce ID"
x-omitempty: false
- example: "a2g17000000hyxNAAA"
project_name:
- type: string
- description: "The name of the SalesForce project"
- example: "Appium"
+ $ref: './common/properties/cla-group-name.yaml'
x-omitempty: false
cla_group_name:
$ref: './common/properties/cla-group-name.yaml'
organization_name:
- type: string
- description: "The name of Salesforce organization"
+ $ref: './common/properties/company-name.yaml'
+ x-omitempty: false
+ signing_entity_name:
+ $ref: './common/properties/company-signing-entity-name.yaml'
+ x-omitempty: false
+ organization_id:
+ $ref: './common/properties/internal-id.yaml'
+ description: "The internal organization ID"
x-omitempty: false
- example: "Intel Corporation"
organization_sfid:
- type: string
+ $ref: './common/properties/external-id.yaml'
+ description: "The Salesforce organization ID"
x-omitempty: false
- example: "00117000015vpjXAAQ"
cla-manager-user:
type: object
@@ -4412,7 +4457,7 @@ definitions:
notify-cla-manager-list:
type: object
- title: Cla Manager list and contributor userID for given company and Project
+ title: CLA Manager list and contributor userID for given company and Project
description: list of CLA Manager emails and contributor userID
properties:
list:
@@ -4423,6 +4468,8 @@ definitions:
type: string
companyName:
$ref: './common/properties/company-name.yaml'
+ signingEntityName:
+ $ref: './common/properties/company-signing-entity-name.yaml'
claGroupName:
$ref: './common/properties/cla-group-name.yaml'
@@ -4434,7 +4481,7 @@ definitions:
description: CLA Manager email
name:
type: string
-
+
company-project-cla-list:
type: object
properties:
@@ -4536,6 +4583,14 @@ definitions:
description: The company name
x-omitempty: false
example: "The Linux Foundation"
+ company_id:
+ $ref: './common/properties/internal-id.yaml'
+ description: 'the Company ID'
+ x-omitempty: false
+ company_sfid:
+ $ref: './common/properties/external-id.yaml'
+ description: 'the Company/Organization SalesForce ID'
+ x-omitempty: false
signing_entity_name:
type: string
description: The company signing entity name
diff --git a/cla-backend-go/swagger/common/properties/company-signing-entity-name.yaml b/cla-backend-go/swagger/common/properties/company-signing-entity-name.yaml
index 1448d6981..6d3497e3f 100644
--- a/cla-backend-go/swagger/common/properties/company-signing-entity-name.yaml
+++ b/cla-backend-go/swagger/common/properties/company-signing-entity-name.yaml
@@ -3,7 +3,7 @@
example: "Linux Foundation"
type: string
-description: Name of the company
+description: Signing Entity Name of the Company
# Pattern aligns with UI and other platform services including Org Service
pattern: '[^<>]*' # allow everything except greater than and less than symbols
minLength: 2
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 37b3b9caf..3d2fd9d53 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -27,6 +27,9 @@ const EasyCLA403Forbidden = "EasyCLA - 403 Forbidden"
// EasyCLA404NotFound common string for handler not found error messages
const EasyCLA404NotFound = "EasyCLA - 404 Not Found"
+// EasyCLA409Conflict common string for handler conflict error messages
+const EasyCLA409Conflict = "EasyCLA - 409 Conflict"
+
// EasyCLA500InternalServerError common string for handler internal server error messages
const EasyCLA500InternalServerError = "EasyCLA - 500 Internal Server Error"
diff --git a/cla-backend-go/utils/responses.go b/cla-backend-go/utils/responses.go
index df251d996..fd8ac3add 100644
--- a/cla-backend-go/utils/responses.go
+++ b/cla-backend-go/utils/responses.go
@@ -73,6 +73,24 @@ func ErrorResponseNotFoundWithError(reqID, msg string, err error) *models.ErrorR
}
}
+// ErrorResponseConflict Helper function to generate a conflict error response
+func ErrorResponseConflict(reqID, msg string) *models.ErrorResponse {
+ return &models.ErrorResponse{
+ Code: String409,
+ Message: fmt.Sprintf("%s - %s", EasyCLA409Conflict, msg),
+ XRequestID: reqID,
+ }
+}
+
+// ErrorResponseConflictWithError Helper function to generate a conflict error message
+func ErrorResponseConflictWithError(reqID, msg string, err error) *models.ErrorResponse {
+ return &models.ErrorResponse{
+ Code: String409,
+ Message: fmt.Sprintf("%s - %s - error: %+v", EasyCLA409Conflict, msg, err),
+ XRequestID: reqID,
+ }
+}
+
// ErrorResponseInternalServerError Helper function to generate an internal server error response
func ErrorResponseInternalServerError(reqID, msg string) *models.ErrorResponse {
return &models.ErrorResponse{
diff --git a/cla-backend-go/v2/cla_manager/errors.go b/cla-backend-go/v2/cla_manager/errors.go
new file mode 100644
index 000000000..13bf369d6
--- /dev/null
+++ b/cla-backend-go/v2/cla_manager/errors.go
@@ -0,0 +1,39 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package cla_manager
+
+import (
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/cla_manager"
+)
+
+// buildErrorMessageCreate helper function to build an error message
+func buildErrorMessageCreate(params cla_manager.CreateCLAManagerParams, err error) string {
+ return fmt.Sprintf("problem creating new CLA Manager using company ID: %s, project SFID: %s, firstName: %s, lastName: %s, user email: %s, error: %+v",
+ params.CompanyID, params.ProjectSFID, *params.Body.FirstName, *params.Body.LastName, *params.Body.UserEmail, err)
+}
+
+// buildErrorMessage helper function to build an error message
+func buildErrorMessageDelete(params cla_manager.DeleteCLAManagerParams, err error) string {
+ return fmt.Sprintf("problem deleting new CLA Manager Request using company ID: %s, project SFID: %s, user ID: %s, error: %+v",
+ params.CompanyID, params.ProjectSFID, params.UserLFID, err)
+}
+
+// buildErrorStatusCode helper function to build an error statusCodes
+func buildErrorStatusCode(err error) string {
+ if err == ErrNoOrgAdmins || err == ErrCLACompanyNotFound || err == ErrClaGroupNotFound || err == ErrCLAUserNotFound {
+ return NotFound
+ }
+ // Check if user is already assigned scope/role
+ if err == ErrRoleScopeConflict {
+ return Conflict
+ }
+ // Check if user does exists
+ if err == ErrNoLFID {
+ return Accepted
+ }
+ // Return Bad Request
+ return BadRequest
+}
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 8eb699b1e..0a67e154c 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -15,6 +15,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
+ v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/cla_manager"
@@ -36,34 +37,47 @@ const (
)
// Configure is the API handler routine for CLA Manager routes
-func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string, projectClaGroupRepo projects_cla_groups.Repository, easyCLAUserRepo v1User.RepositoryService) {
+func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1Company.IService, LfxPortalURL string, projectClaGroupRepo projects_cla_groups.Repository, easyCLAUserRepo v1User.RepositoryService) { // nolint
api.ClaManagerCreateCLAManagerHandler = cla_manager.CreateCLAManagerHandlerFunc(func(params cla_manager.CreateCLAManagerParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, params.CompanySFID, utils.DISALLOW_ADMIN_SCOPE) {
- return cla_manager.NewCreateCLAManagerForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to CreateCLAManager with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID),
- XRequestID: reqID,
- })
+
+ f := logrus.Fields{
+ "functionName": "cla_manager.handlers.ClaManagerCreateCLAManagerHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "CompanyID": params.CompanyID,
+ "ProjectSFID": params.ProjectSFID,
+ "authUser": *params.XUSERNAME,
+ }
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := v1CompanyService.GetCompany(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ log.WithFields(f).Debug("checking permissions...")
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to DeleteCLAManager with Project|Organization scope of %s | %s", authUser.UserName, params.ProjectSFID, params.CompanyID)
+ log.WithFields(f).Warn(msg)
+ return cla_manager.NewCreateCLAManagerForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
+
+ log.WithFields(f).Debug("looking up CLA Group for projectSFID...")
cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(params.ProjectSFID)
if err != nil {
if err == projects_cla_groups.ErrProjectNotAssociatedWithClaGroup {
- return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: BadRequest,
- Message: fmt.Sprintf("EasyCLA - 400 Bad Request - No cla group associated with this project: %s", params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("no CLA Group associated with this project: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- return cla_manager.NewCreateCLAManagerInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error. error = %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_manager.NewCreateCLAManagerInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, err.Error(), err))
}
+
compCLAManager, errorResponse := service.CreateCLAManager(ctx, cginfo.ClaGroupID, params, authUser.UserName)
if errorResponse != nil {
if errorResponse.Code == BadRequest {
@@ -80,21 +94,36 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, params.CompanySFID, utils.DISALLOW_ADMIN_SCOPE) {
- return cla_manager.NewDeleteCLAManagerForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to DeleteCLAManager with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID),
- XRequestID: reqID,
- })
+ f := logrus.Fields{
+ "functionName": "cla_manager.handlers.ClaManagerDeleteCLAManagerHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "CompanyID": params.CompanyID,
+ "ProjectSFID": params.ProjectSFID,
+ "userLFID": params.UserLFID,
+ "authUser": *params.XUSERNAME,
+ }
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := v1CompanyService.GetCompany(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ log.WithFields(f).Debug("checking permissions...")
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to DeleteCLAManager with Project|Organization scope of %s | %s", authUser.UserName, params.ProjectSFID, params.CompanyID)
+ log.WithFields(f).Warn(msg)
+ return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
+
cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(params.ProjectSFID)
if err != nil {
- return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: BadRequest,
- Message: fmt.Sprintf("EasyCLA - Bad Request. No Cla Group associated with ProjectSFID: %s ", params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("no CLA Group associated with this project: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
errResponse := service.DeleteCLAManager(ctx, cginfo.ClaGroupID, params)
@@ -108,35 +137,35 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
api.ClaManagerCreateCLAManagerDesigneeHandler = cla_manager.CreateCLAManagerDesigneeHandlerFunc(func(params cla_manager.CreateCLAManagerDesigneeParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaManagerCreateCLAManagerDesigneeHandler",
+ "functionName": "cla_manager.handlers.ClaManagerCreateCLAManagerDesigneeHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "CompanySFID": params.CompanySFID,
+ "CompanyID": params.CompanyID,
"ProjectSFID": params.ProjectSFID,
"authUser": *params.XUSERNAME,
}
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := v1CompanyService.GetCompany(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewCreateCLAManagerDesigneeBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
// Note: anyone create assign a CLA manager designee...no permissions checks
- log.WithFields(f).Debugf("processing CLA Manager Desginee request")
+ log.WithFields(f).Debugf("processing create CLA Manager Desginee request")
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- claManagerDesignee, err := service.CreateCLAManagerDesignee(ctx, params.CompanySFID, params.ProjectSFID, params.Body.UserEmail.String())
+ claManagerDesignee, err := service.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, params.ProjectSFID, params.Body.UserEmail.String())
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
msg := fmt.Sprintf("Conflict assigning cla manager role for Project SFID: %s ", params.ProjectSFID)
- return cla_manager.NewCreateCLAManagerDesigneeByGroupConflict().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: Conflict,
- XRequestID: reqID,
- })
+ return cla_manager.NewCreateCLAManagerDesigneeByGroupConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
}
msg := fmt.Sprintf("user :%s, error: %+v ", authUser.Email, err)
- return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: BadRequest,
- XRequestID: reqID,
- })
+ return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
log.Debugf("CLA Manager designee created : %+v", claManagerDesignee)
@@ -147,13 +176,14 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
func(params cla_manager.CreateCLAManagerDesigneeByGroupParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaManagerCreateCLAManagerDesigneeByGroupHandler",
+ "functionName": "cla_manager.handlers.ClaManagerCreateCLAManagerDesigneeByGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "CompanySFID": params.CompanySFID,
+ "CompanySFID": params.CompanyID,
"ClaGroupID": params.ClaGroupID,
"Email": params.Body.UserEmail.String(),
- "authUser": *params.XUSERNAME,
+ "authUser": utils.StringValue(params.XUSERNAME),
}
// Note: anyone create assign a CLA manager designee...no permissions checks
@@ -171,41 +201,28 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
XRequestID: reqID,
})
}
+
log.WithFields(f).Debugf("found %d project IDs for CLA group", len(projectCLAGroups))
if len(projectCLAGroups) == 0 {
msg := fmt.Sprintf("no projects associated with CLA Group: %s", params.ClaGroupID)
log.WithFields(f).Warn(msg)
- return cla_manager.NewCreateCLAManagerDesigneeByGroupNotFound().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: BadRequest,
- XRequestID: reqID,
- })
-
+ return cla_manager.NewCreateCLAManagerDesigneeByGroupNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- designeeScopes, msg, err := service.CreateCLAManagerDesigneeByGroup(ctx, params, projectCLAGroups, f)
+ designeeScopes, msg, err := service.CreateCLAManagerDesigneeByGroup(ctx, params, projectCLAGroups)
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
- return cla_manager.NewCreateCLAManagerDesigneeByGroupConflict().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: Conflict,
- XRequestID: reqID,
- })
+ return cla_manager.NewCreateCLAManagerDesigneeByGroupConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
}
log.WithFields(f).Warn(msg)
- return cla_manager.NewCreateCLAManagerDesigneeByGroupBadRequest().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: BadRequest,
- XRequestID: reqID,
- })
+ return cla_manager.NewCreateCLAManagerDesigneeByGroupBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
+
return cla_manager.NewCreateCLAManagerDesigneeByGroupOK().WithXRequestID(reqID).WithPayload(&models.ClaManagerDesignees{
List: designeeScopes,
})
})
+
api.ClaManagerIsCLAManagerDesigneeHandler = cla_manager.IsCLAManagerDesigneeHandlerFunc(func(params cla_manager.IsCLAManagerDesigneeParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
@@ -227,16 +244,12 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
api.ClaManagerInviteCompanyAdminHandler = cla_manager.InviteCompanyAdminHandlerFunc(func(params cla_manager.InviteCompanyAdminParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+
// Get Contributor details
user, userErr := easyCLAUserRepo.GetUser(params.UserID)
if userErr != nil {
msg := fmt.Sprintf("Problem getting user by ID : %s, error: %+v ", params.UserID, userErr)
- return cla_manager.NewInviteCompanyAdminBadRequest().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Code: BadRequest,
- Message: msg,
- XRequestID: reqID,
- })
+ return cla_manager.NewInviteCompanyAdminBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, userErr))
}
claManagerDesignees, err := service.InviteCompanyAdmin(ctx, params.Body.ContactAdmin, params.Body.CompanyID, *params.Body.ClaGroupID, params.Body.UserEmail.String(), params.Body.Name, &user, LfxPortalURL)
@@ -288,25 +301,36 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
api.ClaManagerCreateCLAManagerRequestHandler = cla_manager.CreateCLAManagerRequestHandlerFunc(func(params cla_manager.CreateCLAManagerRequestParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaManagerCreateCLAManagerRequestHandler",
+ "functionName": "cla_manager.handlers.ClaManagerCreateCLAManagerRequestHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "CompanySFID": params.CompanySFID,
+ "CompanyID": params.CompanyID,
"ProjectSFID": params.ProjectSFID,
+ "contactAdmin": params.Body.ContactAdmin,
+ "userFullName": utils.StringValue(params.Body.FullName),
+ "userEmail": params.Body.UserEmail.String(),
"authUser": *params.XUSERNAME,
}
- log.WithFields(f).Debugf("processing CLA Manager request")
- utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForOrganization(authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
- return cla_manager.NewCreateCLAManagerRequestForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to CreateCLAManagerRequest with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID),
- XRequestID: reqID,
- })
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := v1CompanyService.GetCompany(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return cla_manager.NewCreateCLAManagerRequestBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- claManagerDesignee, err := service.CreateCLAManagerRequest(ctx, params.Body.ContactAdmin, params.CompanySFID, params.ProjectSFID, params.Body.UserEmail.String(),
+ // Check perms...
+ if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to CreateCLAManagerRequest with Project|Organization scope of %s | %s",
+ authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
+ log.WithFields(f).Warn(msg)
+ return cla_manager.NewCreateCLAManagerRequestForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ claManagerDesignee, err := service.CreateCLAManagerRequest(ctx, params.Body.ContactAdmin, v1CompanyModel.CompanyExternalID, params.ProjectSFID, params.Body.UserEmail.String(),
*params.Body.FullName, authUser, LfxPortalURL)
if err != nil {
@@ -347,48 +371,29 @@ func Configure(api *operations.EasyclaAPI, service Service, LfxPortalURL string,
func(params cla_manager.NotifyCLAManagersParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "cla_manager.handlers.ClaManagerNotifyCLAManagersHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyName": params.Body.CompanyName,
+ "signingEntityName": params.Body.SigningEntityName,
+ "userID": params.Body.UserID,
+ "claGroupName": params.Body.ClaGroupName,
+ }
+ log.WithFields(f).Debug("notifying CLA managers...")
err := service.NotifyCLAManagers(ctx, params.Body, LfxPortalURL)
if err != nil {
if err == ErrCLAUserNotFound {
+ msg := fmt.Sprintf("unable to notify cla managers - user not found: %s", params.Body.UserID)
+ log.WithFields(f).WithError(err).Warn(err)
return cla_manager.NewNotifyCLAManagersNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFound(
- reqID,
- fmt.Sprintf("unable to notify cla managers - user not found: %s", params.Body.UserID)))
+ utils.ErrorResponseNotFound(reqID, msg))
}
- return cla_manager.NewNotifyCLAManagersBadRequest().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to notify cla managers - cla group: %s, company: %s", params.Body.ClaGroupName, params.Body.CompanyName), err))
+
+ msg := fmt.Sprintf("unable to notify cla managers - cla group: %s, company: %s", params.Body.ClaGroupName, params.Body.CompanyName)
+ log.WithFields(f).WithError(err).Warn(err)
+ return cla_manager.NewNotifyCLAManagersBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
return cla_manager.NewNotifyCLAManagersNoContent().WithXRequestID(reqID)
})
-
-}
-
-// buildErrorMessageCreate helper function to build an error message
-func buildErrorMessageCreate(params cla_manager.CreateCLAManagerParams, err error) string {
- return fmt.Sprintf("problem creating new CLA Manager using company SFID: %s, project SFID: %s, firstName: %s, lastName: %s, user email: %s, error: %+v",
- params.CompanySFID, params.ProjectSFID, *params.Body.FirstName, *params.Body.LastName, *params.Body.UserEmail, err)
-}
-
-// buildErrorMessage helper function to build an error message
-func buildErrorMessageDelete(params cla_manager.DeleteCLAManagerParams, err error) string {
- return fmt.Sprintf("problem deleting new CLA Manager Request using company SFID: %s, project SFID: %s, user ID: %s, error: %+v",
- params.CompanySFID, params.ProjectSFID, params.UserLFID, err)
-}
-
-// buildErrorStatusCode helper function to build an error statusCodes
-func buildErrorStatusCode(err error) string {
- if err == ErrNoOrgAdmins || err == ErrCLACompanyNotFound || err == ErrClaGroupNotFound || err == ErrCLAUserNotFound {
- return NotFound
- }
- // Check if user is already assigned scope/role
- if err == ErrRoleScopeConflict {
- return Conflict
- }
- // Check if user does not exiss
- if err == ErrNoLFID {
- return Accepted
- }
- // Return Bad Request
- return BadRequest
}
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index d22ed03fb..9f9dff0bd 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -95,9 +95,9 @@ type Service interface {
DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse
InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, lFxPortalURL string) ([]*models.ClaManagerDesignee, error)
CreateCLAManagerDesignee(ctx context.Context, companyID string, projectID string, userEmail string) (*models.ClaManagerDesignee, error)
- CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
+ CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companySFID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, LfxPortalURL string) error
- CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup, f logrus.Fields) ([]*models.ClaManagerDesignee, string, error)
+ CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup) ([]*models.ClaManagerDesignee, string, error)
IsCLAManagerDesignee(ctx context.Context, companySFID, claGroupID, userLFID string) (*models.UserRoleStatus, error)
}
@@ -120,20 +120,20 @@ func NewService(compService company.IService, projService project.Service, mgrSe
// CreateCLAManager creates Cla Manager
func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse) {
f := logrus.Fields{
- "functionName": "CreateCLAManager",
+ "functionName": "cla_manager.service.CreateCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
"authUsername": authUsername,
"xUserName": params.XUSERNAME,
"xEmail": params.XEMAIL,
}
- // Search for salesForce Company aka external Company
- log.WithFields(f).Debugf("Getting company by external ID : %s", params.CompanySFID)
- companyModel, companyErr := s.companyService.GetCompanyByExternalID(ctx, params.CompanySFID)
- if companyErr != nil || companyModel == nil {
+ // Search for Company by internal ID
+ log.WithFields(f).Debugf("Getting company by ID: %s", params.CompanyID)
+ v1CompanyModel, companyErr := s.companyService.GetCompany(ctx, params.CompanyID)
+ if companyErr != nil || v1CompanyModel == nil {
msg := buildErrorMessage("company lookup error", claGroupID, params, companyErr)
log.WithFields(f).Warn(msg)
return nil, &models.ErrorResponse{
@@ -141,6 +141,8 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
Code: "400",
}
}
+ f["companySFID"] = v1CompanyModel.CompanyExternalID
+ f["companyName"] = v1CompanyModel.CompanyName
claGroup, err := s.projectService.GetCLAGroupByID(ctx, claGroupID)
if err != nil || claGroup == nil {
@@ -151,6 +153,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
Code: "400",
}
}
+
// Get user by email
userServiceClient := v2UserService.GetClient()
// Get Manager lf account by username. Used for email content
@@ -180,7 +183,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
}
// Check if user exists in easyCLA DB, if not add User
- log.WithFields(f).Debugf("Checking user: %+v in easyCLA records", user)
+ log.WithFields(f).Debugf("Checking user: %+v in EasyCLA database...", user)
claUser, claUserErr := s.easyCLAUserService.GetUserByLFUserName(user.Username)
if claUserErr != nil {
msg := fmt.Sprintf("Problem getting claUser by :%s, error: %+v ", user.Username, claUserErr)
@@ -197,7 +200,8 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
userName := fmt.Sprintf("%s %s", *params.Body.FirstName, *params.Body.LastName)
_, currentTimeString := utils.CurrentTime()
claUserModel := &v1Models.User{
- UserExternalID: params.CompanySFID,
+ CompanyID: v1CompanyModel.CompanyID,
+ UserExternalID: v1CompanyModel.CompanyExternalID,
LfEmail: *user.Emails[0].EmailAddress,
Admin: true,
LfUsername: user.Username,
@@ -231,7 +235,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
}
// Add CLA Manager to Database
- signature, addErr := s.managerService.AddClaManager(ctx, companyModel.CompanyID, claGroupID, user.Username)
+ signature, addErr := s.managerService.AddClaManager(ctx, v1CompanyModel.CompanyID, claGroupID, user.Username)
if addErr != nil {
msg := buildErrorMessageCreate(params, addErr)
log.WithFields(f).Warn(msg)
@@ -241,7 +245,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
}
}
if signature == nil {
- sigMsg := fmt.Sprintf("Signature not found for project: %s and company: %s ", claGroupID, companyModel.CompanyID)
+ sigMsg := fmt.Sprintf("Signature not found for project: %s and company: %s ", claGroupID, v1CompanyModel.CompanyID)
log.WithFields(f).Warn(sigMsg)
return nil, &models.ErrorResponse{
Message: sigMsg,
@@ -258,35 +262,26 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
ClaGroupName: claGroup.ProjectName,
ProjectID: claGroupID,
ProjectName: projectSF.Name,
- OrganizationName: companyModel.CompanyName,
- OrganizationSfid: params.CompanySFID,
+ OrganizationName: v1CompanyModel.CompanyName,
+ OrganizationSfid: v1CompanyModel.CompanyExternalID,
+ OrganizationID: v1CompanyModel.CompanyID,
Name: fmt.Sprintf("%s %s", user.FirstName, user.LastName),
}
+
return claCompanyManager, nil
}
func (s *service) DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse {
f := logrus.Fields{
- "functionName": "DeleteCLAManager",
+ "functionName": "cla_manager.service.DeleteCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
"xUserName": params.XUSERNAME,
"xEmail": params.XEMAIL,
}
- // Search for salesForce Company aka external Company
- companyModel, companyErr := s.companyService.GetCompanyByExternalID(ctx, params.CompanySFID)
- if companyErr != nil || companyModel == nil {
- msg := buildErrorMessageDelete(params, companyErr)
- log.WithFields(f).Warn(msg)
- return &models.ErrorResponse{
- Message: msg,
- Code: "400",
- }
- }
-
- signature, deleteErr := s.managerService.RemoveClaManager(ctx, companyModel.CompanyID, claGroupID, params.UserLFID)
+ signature, deleteErr := s.managerService.RemoveClaManager(ctx, params.CompanyID, claGroupID, params.UserLFID)
if deleteErr != nil {
msg := buildErrorMessageDelete(params, deleteErr)
@@ -296,8 +291,9 @@ func (s *service) DeleteCLAManager(ctx context.Context, claGroupID string, param
Code: "400",
}
}
+
if signature == nil {
- msg := fmt.Sprintf("Not found signature for project: %s and company: %s ", claGroupID, companyModel.CompanyID)
+ msg := fmt.Sprintf("CCLA signature not found for project: %s and company: %s ", claGroupID, params.CompanyID)
log.WithFields(f).Warn(msg)
return &models.ErrorResponse{
Message: msg,
@@ -309,11 +305,11 @@ func (s *service) DeleteCLAManager(ctx context.Context, claGroupID string, param
}
//CreateCLAManagerDesignee creates designee for cla manager prospect
-func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID string, projectSFID string, userEmail string) (*models.ClaManagerDesignee, error) {
+func (s *service) CreateCLAManagerDesignee(ctx context.Context, companyID string, projectSFID string, userEmail string) (*models.ClaManagerDesignee, error) {
f := logrus.Fields{
- "functionName": "CreateCLAManagerDesignee",
+ "functionName": "cla_manager.service.CreateCLAManagerDesignee",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "companySFID": companySFID,
+ "companyID": companyID,
"projectSFID": projectSFID,
"userEmail": userEmail,
}
@@ -323,12 +319,14 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
orgClient := v2OrgService.GetClient()
projectClient := v2ProjectService.GetClient()
- log.WithFields(f).Debugf("loading company by external ID...")
- v1CompanyModel, companyErr := s.companyService.GetCompanyByExternalID(ctx, companySFID)
+ log.WithFields(f).Debugf("loading company by ID...")
+ v1CompanyModel, companyErr := s.companyService.GetCompany(ctx, companyID)
if companyErr != nil {
log.WithFields(f).Warnf("company not found, error: %+v", companyErr)
return nil, companyErr
}
+ f["companySFID"] = v1CompanyModel.CompanyExternalID
+ f["companyName"] = v1CompanyModel.CompanyName
log.WithFields(f).Debugf("checking if company/project is signed with CLA managers...")
isSigned, signedErr := s.isSigned(ctx, v1CompanyModel, projectSFID)
@@ -357,7 +355,7 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
log.WithFields(f).Debugf("checking if user has %s role scope...", utils.CLADesigneeRole)
// Check if user is already CLA Manager designee of project|organization scope
- hasRoleScope, hasRoleScopeErr := orgClient.IsUserHaveRoleScope(ctx, utils.CLADesigneeRole, lfxUser.ID, companySFID, projectSFID)
+ hasRoleScope, hasRoleScopeErr := orgClient.IsUserHaveRoleScope(ctx, utils.CLADesigneeRole, lfxUser.ID, v1CompanyModel.CompanyExternalID, projectSFID)
if hasRoleScopeErr != nil {
// Skip 404 for ListOrgUsrServiceScopes endpoint
if _, ok := hasRoleScopeErr.(*organizations.ListOrgUsrServiceScopesNotFound); !ok {
@@ -385,18 +383,18 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
}
log.WithFields(f).Debugf("creating user role organization scope for user: %s, with role: %s with role ID: %s using project|org: %s|%s...",
- userEmail, utils.CLADesigneeRole, roleID, projectSFID, companySFID)
- scopeErr := orgClient.CreateOrgUserRoleOrgScopeProjectOrg(ctx, userEmail, projectSFID, companySFID, roleID)
+ userEmail, utils.CLADesigneeRole, roleID, projectSFID, v1CompanyModel.CompanyExternalID)
+ scopeErr := orgClient.CreateOrgUserRoleOrgScopeProjectOrg(ctx, userEmail, projectSFID, v1CompanyModel.CompanyExternalID, roleID)
if scopeErr != nil {
// Ignore conflict - role has already been assigned - otherwise, return error
if _, ok := scopeErr.(*organizations.CreateOrgUsrRoleScopesConflict); !ok {
- log.Warn(fmt.Sprintf("Problem creating projectOrg scope for email: %s , projectSFID: %s, companyID: %s", userEmail, projectSFID, companySFID))
+ log.Warn(fmt.Sprintf("problem creating projectOrg scope for email: %s , projectSFID: %s, companySFID: %s", userEmail, projectSFID, v1CompanyModel.CompanyExternalID))
return nil, scopeErr
}
}
log.WithFields(f).Debugf("created user role organization scope for user: %s, with role: %s with role ID: %s using project|org: %s|%s...",
- userEmail, utils.CLADesigneeRole, roleID, projectSFID, companySFID)
+ userEmail, utils.CLADesigneeRole, roleID, projectSFID, v1CompanyModel.CompanyExternalID)
// Log Event
s.eventService.LogEvent(
@@ -409,7 +407,7 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
UserModel: &v1Models.User{LfUsername: lfxUser.Username, UserID: lfxUser.ID},
EventData: &events.AssignRoleScopeData{
Role: "cla-manager-designee",
- Scope: fmt.Sprintf("%s|%s", projectSFID, companySFID),
+ Scope: fmt.Sprintf("%s|%s", projectSFID, v1CompanyModel.CompanyExternalID),
},
})
@@ -420,7 +418,8 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
AssignedOn: time.Now().String(),
Email: strfmt.Email(userEmail),
ProjectSfid: projectSFID,
- CompanySfid: companySFID,
+ CompanySfid: v1CompanyModel.CompanyExternalID,
+ CompanyID: v1CompanyModel.CompanyID,
ProjectName: projectSF.Name,
}
@@ -428,11 +427,12 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companySFID stri
}
func (s *service) IsCLAManagerDesignee(ctx context.Context, companySFID, claGroupID, userLFID string) (*models.UserRoleStatus, error) {
-
f := logrus.Fields{
- "functionName": "IsCLAManagerDesignee",
- "claGroupID": claGroupID,
- "userLFID": userLFID,
+ "functionName": "cla_manager.service.IsCLAManagerDesignee",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companySFID": companySFID,
+ "claGroupID": claGroupID,
+ "userLFID": userLFID,
}
// Get LF User
@@ -545,7 +545,15 @@ func (s *service) IsCLAManagerDesignee(ctx context.Context, companySFID, claGrou
}
//CreateCLAManagerDesigneeByGroup creates designee by group for cla manager prospect
-func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup, f logrus.Fields) ([]*models.ClaManagerDesignee, string, error) {
+func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup) ([]*models.ClaManagerDesignee, string, error) {
+ f := logrus.Fields{
+ "functionName": "cla_manager.service.CreateCLAManagerDesigneeByGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": params.ClaGroupID,
+ "companyID": params.CompanyID,
+ "userEmail": params.Body.UserEmail.String(),
+ }
+
var designeeScopes []*models.ClaManagerDesignee
userEmail := params.Body.UserEmail.String()
@@ -556,9 +564,20 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
return nil, msg, signedErr
}
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := s.companyService.GetCompany(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return nil, msg, err
+ }
+ f["companySFID"] = v1CompanyModel.CompanyExternalID
+ f["companyName"] = v1CompanyModel.CompanyName
+
if signedAtFoundationLevel {
if foundationSFID != "" {
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, params.CompanySFID, foundationSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, foundationSFID, userEmail)
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
msg := fmt.Sprintf("Conflict assigning cla manager role for Foundation SFID: %s ", foundationSFID)
@@ -589,7 +608,7 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
go func(swg *sync.WaitGroup, pcg *projects_cla_groups.ProjectClaGroup, designeeChannel chan *result) {
defer swg.Done()
log.WithFields(f).Debugf("creating CLA Manager Designee for Project SFID: %s", pcg.ProjectSFID)
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, params.CompanySFID, pcg.ProjectSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, pcg.ProjectSFID, userEmail)
var output result
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
@@ -631,7 +650,7 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
// CreateCLAManagerRequest service method
func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companySFID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error) {
f := logrus.Fields{
- "functionName": "CreateCLAManagerRequest",
+ "functionName": "cla_manager.service.CreateCLAManagerRequest",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"contactAdmin": contactAdmin,
"companySFID": companySFID,
@@ -652,6 +671,8 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
log.Warn(msg)
return nil, companyErr
}
+ f["companyID"] = v1CompanyModel.CompanyID
+ f["companyName"] = v1CompanyModel.CompanyName
// Determine if the CCLA is already signed or not
log.WithFields(f).Debugf("checking if company/project is signed with CLA managers...")
@@ -813,16 +834,18 @@ func (s *service) ValidateInviteCompanyAdminCheck(ctx context.Context, f logrus.
}
func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, LfxPortalURL string) ([]*models.ClaManagerDesignee, error) {
- orgService := v2OrgService.GetClient()
- projectService := v2ProjectService.GetClient()
- userService := v2UserService.GetClient()
f := logrus.Fields{
- "functionName": "InviteCompanyAdmin",
+ "functionName": "cla_manager.service.InviteCompanyAdmin",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"claGroupID": projectID,
"userEmail": userEmail,
- "name": name}
+ "name": name,
+ }
+
+ orgService := v2OrgService.GetClient()
+ projectService := v2ProjectService.GetClient()
+ userService := v2UserService.GetClient()
validateError := s.ValidateInviteCompanyAdminCheck(ctx, f, projectID, contactAdmin, userEmail, name, contributor)
if validateError != nil {
@@ -1045,24 +1068,42 @@ func validateInviteCompanyAdmin(contactAdmin bool, userEmail string, name string
}
func (s *service) NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, LfxPortalURL string) error {
+ f := logrus.Fields{
+ "functionName": "cla_manager.service.NotifyCLAManagers",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyName": notifyCLAManagers.CompanyName,
+ "signingEntityName": notifyCLAManagers.SigningEntityName,
+ "userID": notifyCLAManagers.UserID,
+ "claGroupName": notifyCLAManagers.ClaGroupName,
+ }
// Search for Easy CLA User
- log.Debugf("Getting user by ID: %s", notifyCLAManagers.UserID)
+ log.WithFields(f).Debugf("Getting user by ID: %s", notifyCLAManagers.UserID)
userModel, userErr := s.easyCLAUserService.GetUser(notifyCLAManagers.UserID)
if userErr != nil {
msg := fmt.Sprintf("Problem getting user by ID: %s ", notifyCLAManagers.UserID)
- log.Warn(msg)
+ log.WithFields(f).Warn(msg)
return ErrCLAUserNotFound
}
- log.Debugf("Sending notification emails to claManagers: %+v", notifyCLAManagers.List)
+ log.Debugf("Sending notification emails to CLA Managers: %+v", notifyCLAManagers.List)
for _, claManager := range notifyCLAManagers.List {
- sendEmailToCLAManager(claManager.Name, claManager.Email.String(), userModel, notifyCLAManagers.CompanyName, notifyCLAManagers.ClaGroupName, LfxPortalURL)
+ sendEmailToCLAManager(ctx, claManager.Name, claManager.Email.String(), userModel, notifyCLAManagers.CompanyName, notifyCLAManagers.SigningEntityName, notifyCLAManagers.ClaGroupName, LfxPortalURL)
}
return nil
}
-func sendEmailToCLAManager(manager string, managerEmail string, userModel *v1Models.User, company string, claGroupName string, lfxPortalURL string) {
+func sendEmailToCLAManager(ctx context.Context, manager string, managerEmail string, userModel *v1Models.User, company, signingEntityName, claGroupName, lfxPortalURL string) {
+ f := logrus.Fields{
+ "functionName": "cla_manager.service.sendEmailToCLAManager",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "manager": manager,
+ "managerEmail": managerEmail,
+ "user": userModel.Username,
+ "companyName": company,
+ "signingEntityName": signingEntityName,
+ "claGroupName": claGroupName,
+ }
subject := fmt.Sprintf("EasyCLA: Approval Request for contributor: %s", getBestUserName(userModel))
recipients := []string{managerEmail}
body := fmt.Sprintf(`
@@ -1070,18 +1111,20 @@ func sendEmailToCLAManager(manager string, managerEmail string, userModel *v1Mod
This is a notification email from EasyCLA regarding the organization %s.
The following contributor would like to submit a contribution to the %s CLA Group
and is requesting to be approved as a contributor for your organization:
-
%s
+
%s - Signing Entity Name: %s
Approval can be done at %s
Please notify the contributor once they are added so that they may complete the contribution process.
%s
%s`,
- manager, company, claGroupName, getFormattedUserDetails(userModel), lfxPortalURL,
+ manager, company, signingEntityName, claGroupName, getFormattedUserDetails(userModel), lfxPortalURL,
utils.GetEmailHelpContent(true), utils.GetEmailSignOffContent())
+
+ log.WithFields(f).Debugf("sending email with subject: %s to recipients: %+v...", subject, recipients)
err := utils.SendEmail(subject, body, recipients)
if err != nil {
- log.Warnf("problem sending email with subject: %s to recipients: %+v, error: %+v", subject, recipients, err)
+ log.WithFields(f).WithError(err).Warnf("problem sending email with subject: %s to recipients: %+v, error: %+v", subject, recipients, err)
} else {
- log.Debugf("sent email with subject: %s to recipients: %+v", subject, recipients)
+ log.WithFields(f).Debugf("sent email with subject: %s to recipients: %+v", subject, recipients)
}
}
@@ -1160,7 +1203,7 @@ func getFormattedUserDetails(model *v1Models.User) string {
// isSigned is a helper function to check if project/claGroup is signed
func (s *service) isSigned(ctx context.Context, companyModel *v1Models.Company, projectID string) (bool, error) {
f := logrus.Fields{
- "functionName": "isSigned",
+ "functionName": "cla_manager.service.isSigned",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyModel.CompanyID,
"companyName": companyModel.CompanyName,
@@ -1171,7 +1214,7 @@ func (s *service) isSigned(ctx context.Context, companyModel *v1Models.Company,
f["companyID"] = companyModel.CompanyID
f["companyName"] = companyModel.CompanyName
log.WithFields(f).Debug("loading CLA Managers for company/project")
- claManagers, err := s.v2CompanyService.GetCompanyProjectCLAManagers(ctx, companyModel.CompanyID, companyModel.CompanyExternalID, projectID)
+ claManagers, err := s.v2CompanyService.GetCompanyProjectCLAManagers(ctx, companyV1toV2(companyModel), projectID)
if err != nil {
msg := fmt.Sprintf("EasyCLA - 400 Bad Request : %v", err)
log.WithFields(f).Warn(msg)
@@ -1248,7 +1291,7 @@ func contributorEmailToOrgAdmin(adminEmail string, admin string, company string,
func sendEmailToCLAManagerDesigneeCorporate(ctx context.Context, corporateConsole string, companyName string, projectNames []string, designeeEmail string, designeeName string, senderEmail string, senderName string) {
f := logrus.Fields{
- "functionName": "sendEmailToCLAManagerDesigneeCorporate",
+ "functionName": "cla_manager.service.sendEmailToCLAManagerDesigneeCorporate",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"corporateConsole": corporateConsole,
"companyName": companyName,
@@ -1286,7 +1329,7 @@ Either you or someone whom to designate from your company can login to this port
func sendEmailToCLAManagerDesignee(ctx context.Context, corporateConsole string, companyName string, projectNames []string, designeeEmail string, designeeName string, contributorID string, contributorName string) {
f := logrus.Fields{
- "functionName": "sendEmailToCLAManagerDesignee",
+ "functionName": "cla_manager.service.sendEmailToCLAManagerDesignee",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"corporateConsole": corporateConsole,
"companyName": companyName,
@@ -1323,7 +1366,7 @@ func sendEmailToCLAManagerDesignee(ctx context.Context, corporateConsole string,
func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID, projectName string, projectID *string, role string) error {
f := logrus.Fields{
- "functionName": "sendDesigneeEmailToUserWithNoLFID",
+ "functionName": "cla_manager.service.sendDesigneeEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userWithNoLFIDName": userWithNoLFIDName,
"userWithNoLFIDEmail": userWithNoLFIDEmail,
@@ -1356,7 +1399,7 @@ func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, requesterUsername, r
// sendEmailToUserWithNoLFID helper function to send email to a given user with no LFID
func sendEmailToUserWithNoLFID(ctx context.Context, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error {
f := logrus.Fields{
- "functionName": "sendEmailToUserWithNoLFID",
+ "functionName": "cla_manager.service.sendEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectName": projectName,
"requesterUsername": requesterUsername,
@@ -1393,13 +1436,13 @@ Once complete, notify the user %s and they will be able to add you as a CLA Mana
// buildErrorMessage helper function to build an error message
func buildErrorMessage(errPrefix string, claGroupID string, params cla_manager.CreateCLAManagerParams, err error) string {
- return fmt.Sprintf("%s - problem creating new CLA Manager Request using company SFID: %s, project ID: %s, first name: %s, last name: %s, user email: %s, error: %+v",
- errPrefix, params.CompanySFID, claGroupID, *params.Body.FirstName, *params.Body.LastName, *params.Body.UserEmail, err)
+ return fmt.Sprintf("%s - problem creating new CLA Manager Request using company ID: %s, project ID: %s, first name: %s, last name: %s, user email: %s, error: %+v",
+ errPrefix, params.CompanyID, claGroupID, *params.Body.FirstName, *params.Body.LastName, *params.Body.UserEmail, err)
}
func (s *service) convertGHUserToContact(ctx context.Context, contributor *v1User.User) error {
f := logrus.Fields{
- "functionName": "convertGHUserToContact",
+ "functionName": "cla_manager.service.convertGHUserToContact",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
@@ -1437,3 +1480,18 @@ func (s *service) convertGHUserToContact(ctx context.Context, contributor *v1Use
}
return nil
}
+
+func companyV1toV2(v1CompanyModel *v1Models.Company) *models.Company {
+ return &models.Company{
+ CompanyACL: v1CompanyModel.CompanyACL,
+ CompanyID: v1CompanyModel.CompanyID,
+ CompanyExternalID: v1CompanyModel.CompanyExternalID,
+ CompanyName: v1CompanyModel.CompanyName,
+ SigningEntityName: v1CompanyModel.SigningEntityName,
+ CompanyManagerID: v1CompanyModel.CompanyManagerID,
+ Note: v1CompanyModel.Note,
+ Created: v1CompanyModel.Created,
+ Updated: v1CompanyModel.Updated,
+ Version: v1CompanyModel.Version,
+ }
+}
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 393a92f85..04c450fa7 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -26,48 +26,38 @@ import (
)
// Configure sets up the middleware handlers
-func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Company.IRepository, projectClaGroupRepo projects_cla_groups.Repository, LFXPortalURL, v1CorporateConsole string) { // nolint
+func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo projects_cla_groups.Repository, LFXPortalURL, v1CorporateConsole string) { // nolint
- const msgUnableToLoadCompany = "unable to load company external ID"
api.CompanyGetCompanyProjectClaManagersHandler = company.GetCompanyProjectClaManagersHandlerFunc(
func(params company.GetCompanyProjectClaManagersParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyGetCompanyProjectClaManagersHandler",
+ "functionName": "company.handlers.CompanyGetCompanyProjectClaManagersHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
}
- log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
- return company.NewGetCompanyProjectClaManagersForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Company Project CLA Managers with Organization scope of %s",
- authUser.UserName, params.CompanySFID),
- XRequestID: reqID,
- })
- }
- comp, err := v1CompanyRepo.GetCompanyByExternalID(ctx, params.CompanySFID)
- if err != nil {
- msg := "unable to load company by SFID"
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
- if err == v1Company.ErrCompanyDoesNotExist {
- return company.NewGetCompanyProjectClaManagersNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFoundWithError(reqID, msg, err))
- }
- return company.NewGetCompanyProjectClaManagersNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ return company.NewGetCompanyProjectClaManagersBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- if comp == nil {
- log.WithFields(f).WithError(err).Warn(msgUnableToLoadCompany)
- return company.NewGetCompanyProjectClaManagersNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFound(reqID, msgUnableToLoadCompany))
+
+ log.WithFields(f).Debug("checking permissions")
+ if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectClaManagers with Project|Organization scope of %s | %s",
+ authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectClaManagersForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.GetCompanyProjectCLAManagers(ctx, comp.CompanyID, params.CompanySFID, params.ProjectSFID)
+ result, err := service.GetCompanyProjectCLAManagers(ctx, v1CompanyModel, params.ProjectSFID)
if err != nil {
msg := "unable to load company project CLA managers"
log.WithFields(f).WithError(err).Warn(msg)
@@ -84,7 +74,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "CompanyGetCompanyCLAGroupManagersHandler",
+ "functionName": "company.handlers.CompanyGetCompanyCLAGroupManagersHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"companyID": params.CompanyID,
@@ -111,49 +101,43 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyGetCompanyProjectActiveClaHandler",
+ "functionName": "company.handlers.CompanyGetCompanyProjectActiveClaHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
}
- log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
- return company.NewGetCompanyProjectActiveClaForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to CreateCLAManager with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID),
- XRequestID: reqID,
- })
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- comp, err := v1CompanyRepo.GetCompanyByExternalID(ctx, params.CompanySFID)
- if err != nil {
- if err == v1Company.ErrCompanyDoesNotExist {
- return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("Company not found with given ID. [%s]", params.CompanySFID),
- XRequestID: reqID,
- })
- }
- }
- if comp == nil {
- log.WithFields(f).WithError(err).Warn(msgUnableToLoadCompany)
- return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFound(reqID, msgUnableToLoadCompany))
+ log.WithFields(f).Debug("checking permissions")
+ if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectActiveCla with Project|Organization scope of %s | %s",
+ authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectActiveClaForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.GetCompanyProjectActiveCLAs(ctx, comp.CompanyID, params.ProjectSFID)
+ log.WithFields(f).Debug("getting company project active CLAs...")
+ result, err := service.GetCompanyProjectActiveCLAs(ctx, v1CompanyModel.CompanyID, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
- return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("clagroup not found with given ID. [%s]", params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("CLA Group not found with given project SFID: %s", params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbiddenWithError(reqID, msg, err))
}
- return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+
+ msg := fmt.Sprintf("error looking up active project CLAs by internal company ID: %s and project SFID: %s", v1CompanyModel.CompanyID, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
+
return company.NewGetCompanyProjectActiveClaOK().WithXRequestID(reqID).WithPayload(result)
})
@@ -163,7 +147,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyGetCompanyProjectContributorsHandler",
+ "functionName": "company.handlers.CompanyGetCompanyProjectContributorsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companySFID": params.CompanySFID,
@@ -197,7 +181,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyGetCompanyProjectClaHandler",
+ "functionName": "company.handlers.CompanyGetCompanyProjectClaHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companySFID": params.CompanySFID,
@@ -235,7 +219,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "CompanyCreateCompanyHandler",
+ "functionName": "company.handlers.CompanyCreateCompanyHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": params.UserID,
"companyName": aws.StringValue(params.Input.CompanyName),
@@ -274,7 +258,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "CompanyGetCompanyByNameHandler",
+ "functionName": "company.handlers.CompanyGetCompanyByNameHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": params.CompanyName,
}
@@ -297,13 +281,43 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
return company.NewGetCompanyByNameOK().WithXRequestID(reqID).WithPayload(companyModel)
})
+ api.CompanyGetCompanyBySigningEntityNameHandler = company.GetCompanyBySigningEntityNameHandlerFunc(
+ func(params company.GetCompanyBySigningEntityNameParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "company.handlers.CompanyGetCompanyByNameHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "signingEntityName": params.SigningEntityName,
+ }
+
+ // Anyone can query for a company by signing entity name
+
+ log.WithFields(f).Debug("loading company by name")
+ companyModel, err := service.GetCompanyBySigningEntityName(ctx, params.SigningEntityName)
+ if err != nil {
+ msg := fmt.Sprintf("unable to locate company by signing entity name: %s", params.SigningEntityName)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyBySigningEntityNameBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ if companyModel == nil {
+ msg := fmt.Sprintf("unable to locate company by signing entity name: %s", params.SigningEntityName)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyBySigningEntityNameNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ return company.NewGetCompanyBySigningEntityNameOK().WithXRequestID(reqID).WithPayload(companyModel)
+ })
+
api.CompanyDeleteCompanyByIDHandler = company.DeleteCompanyByIDHandlerFunc(
func(params company.DeleteCompanyByIDParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyDeleteCompanyByIDHandler",
+ "functionName": "company.handlers.CompanyDeleteCompanyByIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": params.CompanyID,
}
@@ -355,7 +369,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "CompanyDeleteCompanyBySFIDHandler",
+ "functionName": "company.handlers.CompanyDeleteCompanyBySFIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
}
@@ -410,7 +424,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "CompanyContributorAssociationHandler",
+ "functionName": "company.handlers.CompanyContributorAssociationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
"userEmail": params.Body.UserEmail.String(),
@@ -436,7 +450,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "CompanyContributorAssociationHandler",
+ "functionName": "company.handlers.CompanyContributorAssociationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
}
@@ -536,7 +550,7 @@ func errorResponse(reqID string, err error) *models.ErrorResponse {
// isUserHaveAccessToCLAProjectOrganization is a helper function to determine if the user has access to the specified project and organization
func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *auth.User, projectSFID, organizationSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool {
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAProjectOrganization",
+ "functionName": "company.handlers.isUserHaveAccessToCLAProjectOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"organizationSFID": organizationSFID,
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 4db52ef49..375708015 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -85,12 +85,13 @@ const (
// Service functions for company
type Service interface {
- GetCompanyProjectCLAManagers(ctx context.Context, companyID, companySFID, projectSFID string) (*models.CompanyClaManagers, error)
+ GetCompanyProjectCLAManagers(ctx context.Context, v1CompanyModel *models.Company, projectSFID string) (*models.CompanyClaManagers, error)
GetCompanyProjectActiveCLAs(ctx context.Context, companyID string, projectSFID string) (*models.ActiveClaList, error)
GetCompanyProjectContributors(ctx context.Context, projectSFID string, companySFID string, searchTerm string) (*models.CorporateContributorList, error)
GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string) (*models.CompanyProjectClaList, error)
CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string) (*models.CompanyOutput, error)
GetCompanyByName(ctx context.Context, companyName string) (*models.Company, error)
+ GetCompanyBySigningEntityName(ctx context.Context, signingEntityName string) (*models.Company, error)
GetCompanyByID(ctx context.Context, companyID string) (*models.Company, error)
GetCompanyBySFID(ctx context.Context, companySFID string) (*models.Company, error)
DeleteCompanyByID(ctx context.Context, companyID string) error
@@ -125,13 +126,17 @@ func NewService(v1CompanyService v1Company.IService, sigRepo signatures.Signatur
}
}
-func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, companyID, companySFID, projectSFID string) (*models.CompanyClaManagers, error) {
+func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, v1CompanyModel *models.Company, projectSFID string) (*models.CompanyClaManagers, error) {
f := logrus.Fields{
- "functionName": "GetCompanyProjectCLAManagers",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "companyID": companyID,
+ "functionName": "GetCompanyProjectCLAManagers",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "companyID": v1CompanyModel.CompanyID,
+ "companySFID": v1CompanyModel.CompanyExternalID,
+ "companyName": v1CompanyModel.CompanyName,
+ "signingEntityName": v1CompanyModel.SigningEntityName,
}
+
log.WithFields(f).Debugf("locating CLA Group(s) under project or foundation...")
var err error
claGroups, err := s.getCLAGroupsUnderProjectOrFoundation(ctx, projectSFID)
@@ -142,9 +147,9 @@ func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, companyID, c
// get the org client for org info filling
orgClient := orgService.GetClient()
- orgModel, err := orgClient.GetOrganization(ctx, companySFID)
+ orgModel, err := orgClient.GetOrganization(ctx, v1CompanyModel.CompanyExternalID)
if err != nil {
- return nil, fmt.Errorf("fetching org model failed for companySFID : %s : %w", companySFID, err)
+ return nil, fmt.Errorf("fetching org model failed for companySFID : %s : %w", v1CompanyModel.CompanyExternalID, err)
}
signed, approved := true, true
@@ -157,7 +162,7 @@ func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, companyID, c
log.WithFields(f).Debugf("claGroupID missing for project : %s ", claGroup.ProjectSFID)
continue
}
- sig, sigErr := s.signatureRepo.GetProjectCompanySignature(ctx, companyID, claGroup.ClaGroupID, &signed, &approved, nil, &maxLoad)
+ sig, sigErr := s.signatureRepo.GetProjectCompanySignature(ctx, v1CompanyModel.CompanyID, claGroup.ClaGroupID, &signed, &approved, nil, &maxLoad)
if sigErr != nil {
log.WithFields(f).Warnf("problem fetching CLA signatures, error: %+v", sigErr)
return nil, sigErr
@@ -178,11 +183,13 @@ func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, companyID, c
for _, user := range sig.SignatureACL {
claManagers = append(claManagers, &models.CompanyClaManager{
// DB doesn't have approved_on value
- ApprovedOn: sig.SignatureCreated,
- LfUsername: user.LfUsername,
- ProjectID: sig.ProjectID,
- OrganizationSfid: companySFID,
- OrganizationName: orgModel.Name,
+ ApprovedOn: sig.SignatureCreated,
+ LfUsername: user.LfUsername,
+ ProjectID: sig.ProjectID,
+ OrganizationSfid: v1CompanyModel.CompanyExternalID,
+ OrganizationID: v1CompanyModel.CompanyID,
+ OrganizationName: orgModel.Name,
+ SigningEntityName: v1CompanyModel.SigningEntityName,
})
lfUsernames.Add(user.LfUsername)
}
@@ -205,9 +212,9 @@ func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, companyID, c
// fill project info
fillProjectInfo(claManagers, claGroups)
// fetch the cla_manager.added events so can fill the addedOn field
- claManagerAddedEvents, err := s.eventService.GetCompanyEvents(companyID, events.ClaManagerCreated, nil, aws.Int64(100), true)
+ claManagerAddedEvents, err := s.eventService.GetCompanyEvents(v1CompanyModel.CompanyID, events.ClaManagerCreated, nil, aws.Int64(100), true)
if err != nil {
- log.WithFields(f).Warnf("fetching events for companyID failed : %s : %v", companyID, err)
+ log.WithFields(f).Warnf("fetching events for companyID failed : %s : %v", v1CompanyModel.CompanyID, err)
return nil, err
}
// fill events info
@@ -472,10 +479,39 @@ func (s *service) GetCompanyByName(ctx context.Context, companyName string) (*mo
return &v2CompanyModel, nil
}
+// GetCompanyBySigningEntityName retrieves the company by signing entity name
+func (s *service) GetCompanyBySigningEntityName(ctx context.Context, signingEntityName string) (*models.Company, error) {
+ f := logrus.Fields{
+ "functionName": "company.service.GetCompanyBySigningEntityName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "signingEntityName": signingEntityName,
+ }
+
+ companyModel, err := s.companyRepo.GetCompanyBySigningEntityName(ctx, signingEntityName)
+ if err != nil {
+ return nil, err
+ }
+
+ if companyModel == nil {
+ log.WithFields(f).Debugf("search by company signing entity name: %s didn't locate the record", signingEntityName)
+ return nil, nil
+ }
+
+ // Convert from v1 to v2 model - use helper: Copy(toValue interface{}, fromValue interface{})
+ var v2CompanyModel v2Models.Company
+ copyErr := copier.Copy(&v2CompanyModel, &companyModel)
+ if copyErr != nil {
+ log.WithFields(f).Warnf("problem converting v1 company model to a v2 company model, error: %+v", copyErr)
+ return nil, copyErr
+ }
+
+ return &v2CompanyModel, nil
+}
+
// GetCompanyByID retrieves the company by internal ID
func (s *service) GetCompanyByID(ctx context.Context, companyID string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "GetCompanyByID",
+ "functionName": "company.service.GetCompanyByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
}
@@ -502,7 +538,7 @@ func (s *service) GetCompanyByID(ctx context.Context, companyID string) (*models
func (s *service) AssociateContributor(ctx context.Context, companySFID string, userEmail string) (*models.Contributor, error) {
f := logrus.Fields{
- "functionName": "AssociateContributor",
+ "functionName": "company.service.AssociateContributor",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"userEmail": userEmail,
@@ -549,7 +585,7 @@ func (s *service) AssociateContributor(ctx context.Context, companySFID string,
//CreateContributor creates contributor for contributor prospect
func (s *service) CreateContributor(ctx context.Context, companyID string, projectID string, userEmail string, ClaGroupID string) (*models.Contributor, error) {
f := logrus.Fields{
- "functionName": "CreateContributor",
+ "functionName": "company.service.CreateContributor",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectID": projectID,
@@ -639,7 +675,7 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
//AssociateContributorByGroup creates contributor by group for contributor prospect
func (s *service) AssociateContributorByGroup(ctx context.Context, companySFID, userEmail string, projectCLAGroups []*projects_cla_groups.ProjectClaGroup, ClaGroupID string) ([]*models.Contributor, string, error) {
f := logrus.Fields{
- "functionName": "AssociateContributorByGroup",
+ "functionName": "company.service.AssociateContributorByGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"ClaGroupID": ClaGroupID,
@@ -682,7 +718,7 @@ func (s *service) AssociateContributorByGroup(ctx context.Context, companySFID,
// GetCompanyBySFID retrieves the company by external SFID
func (s *service) GetCompanyBySFID(ctx context.Context, companySFID string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "GetCompanyBySFID",
+ "functionName": "company.service.GetCompanyBySFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
}
@@ -736,7 +772,7 @@ func (s *service) DeleteCompanyBySFID(ctx context.Context, companyID string) err
func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string) (*models.CompanyProjectClaList, error) {
f := logrus.Fields{
- "functionName": "GetCompanyProjectCLA",
+ "functionName": "company.service.GetCompanyProjectCLA",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
@@ -844,7 +880,7 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
// corresponding CLA managers
func (s *service) GetCompanyCLAGroupManagers(ctx context.Context, companyID, claGroupID string) (*models.CompanyClaManagers, error) {
f := logrus.Fields{
- "functionName": "GetCompanyCLAGroupManagers",
+ "functionName": "company.service.GetCompanyCLAGroupManagers",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"claGroupID": claGroupID,
@@ -1023,7 +1059,7 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, id s
func (s *service) getAllCCLASignatures(ctx context.Context, companyID string) ([]*v1Models.Signature, error) {
f := logrus.Fields{
- "functionName": "getAllCCLASignatures",
+ "functionName": "company.service.getAllCCLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
}
@@ -1066,7 +1102,7 @@ func getUsersInfo(lfUsernames []string) (map[string]*v2UserServiceModels.User, e
func fillUsersInfo(claManagers []*models.CompanyClaManager, usermap map[string]*v2UserServiceModels.User) {
f := logrus.Fields{
- "functionName": "fillUsersInfo",
+ "functionName": "company.service.fillUsersInfo",
}
log.WithFields(f).Debug("filling users info...")
@@ -1090,7 +1126,7 @@ func fillUsersInfo(claManagers []*models.CompanyClaManager, usermap map[string]*
func fillProjectInfo(claManagers []*models.CompanyClaManager, claGroups map[string]*claGroupModel) {
f := logrus.Fields{
- "functionName": "fillProjectInfo",
+ "functionName": "company.service.fillProjectInfo",
}
log.WithFields(f).Debug("filling project info...")
for _, claManager := range claManagers {
@@ -1106,7 +1142,9 @@ func fillProjectInfo(claManagers []*models.CompanyClaManager, claGroups map[stri
func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1Models.Signature, activeCla *models.ActiveCla, claGroups map[string]*claGroupModel, companyID string) {
f := logrus.Fields{
- "functionName": "fillActiveCLA",
+ "functionName": "v1CompanyModel.service.fillActiveCLA",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": companyID,
}
defer wg.Done()
cg, ok := claGroups[sig.ProjectID]
@@ -1116,9 +1154,9 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
}
// Get Company details
- company, compErr := s.GetCompanyByID(ctx, companyID)
+ v1CompanyModel, compErr := s.GetCompanyByID(ctx, companyID)
if compErr != nil {
- log.WithFields(f).WithError(compErr).Warnf("unable to fetch company by ID: %s ", companyID)
+ log.WithFields(f).WithError(compErr).Warnf("unable to fetch v1CompanyModel by ID: %s ", companyID)
return
}
@@ -1134,11 +1172,11 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
}
// fill details from dynamodb
- activeCla.CompanyName = company.CompanyName
- if company.SigningEntityName == "" {
- activeCla.SigningEntityName = company.CompanyName
+ activeCla.CompanyName = v1CompanyModel.CompanyName
+ if v1CompanyModel.SigningEntityName == "" {
+ activeCla.SigningEntityName = v1CompanyModel.CompanyName
} else {
- activeCla.SigningEntityName = company.SigningEntityName
+ activeCla.SigningEntityName = v1CompanyModel.SigningEntityName
}
activeCla.ProjectID = sig.ProjectID
if sig.SignedOn == "" {
@@ -1151,6 +1189,8 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
UsernameList: acl,
}
activeCla.ClaGroupName = cg.ClaGroupName
+ activeCla.CompanyID = companyID
+ activeCla.CompanySfid = v1CompanyModel.CompanyExternalID
activeCla.SignatureID = sig.SignatureID.String()
// fill details from project service
@@ -1264,7 +1304,7 @@ func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepos
func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, companySFID string, projectSFID string) ([]*v1Models.Signature, error) {
f := logrus.Fields{
- "functionName": "getAllCompanyProjectEmployeeSignatures",
+ "functionName": "company.service.getAllCompanyProjectEmployeeSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"projectSFID": projectSFID,
@@ -1290,7 +1330,7 @@ func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, co
// get company and project in parallel
func (s *service) getCompanyAndClaGroup(ctx context.Context, companySFID, projectSFID string) (*v1Models.Company, *v1Models.ClaGroup, error) {
f := logrus.Fields{
- "functionName": "getCompanyAndClaGroup",
+ "functionName": "company.service.getCompanyAndClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"projectSFID": projectSFID,
@@ -1333,7 +1373,7 @@ func (s *service) getCompanyAndClaGroup(ctx context.Context, companySFID, projec
// autoCreateCompany helper function to create a new company record based on the SF ID and underlying record in SF
func (s service) autoCreateCompany(ctx context.Context, companySFID string) (*v1Models.Company, error) {
f := logrus.Fields{
- "functionName": "autoCreateCompany",
+ "functionName": "company.service.autoCreateCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
}
From 276a005849955225ba96b29136640adf303a3369 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 26 Jan 2021 18:44:30 +0300
Subject: [PATCH 0011/1276] [#2515] Feature/Query for Company ID (#2525)
- Added query param for company ID for company project cla endpoint
Signed-off-by: wanyaland
---
cla-backend-go/swagger/cla.v2.yaml | 6 ++++++
cla-backend-go/v2/company/handlers.go | 2 +-
cla-backend-go/v2/company/service.go | 27 ++++++++++++++++++++++++---
3 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 819e63cc9..dd9422c30 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3240,6 +3240,7 @@ paths:
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-companySFID"
- $ref: "#/parameters/path-projectSFID"
+ - $ref: "#/parameters/companyID"
responses:
'200':
description: 'Success'
@@ -3526,6 +3527,11 @@ parameters:
in: query
type: string
required: true
+ companyID:
+ name: companyID
+ description: The internal company ID representing signing entity name instance (EasyCLA)
+ in: query
+ type: string
path-claGroupID:
name: claGroupID
description: ID of the CLA Group
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 04c450fa7..30953b775 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -195,7 +195,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("loading project company CLAs")
- result, err := service.GetCompanyProjectCLA(ctx, authUser, params.CompanySFID, params.ProjectSFID)
+ result, err := service.GetCompanyProjectCLA(ctx, authUser, params.CompanySFID, params.ProjectSFID, params.CompanyID)
if err != nil {
msg := "unable to load project company CLAs"
log.WithFields(f).WithError(err).Warn(msg)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 375708015..a3f2d9e8a 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -88,7 +88,7 @@ type Service interface {
GetCompanyProjectCLAManagers(ctx context.Context, v1CompanyModel *models.Company, projectSFID string) (*models.CompanyClaManagers, error)
GetCompanyProjectActiveCLAs(ctx context.Context, companyID string, projectSFID string) (*models.ActiveClaList, error)
GetCompanyProjectContributors(ctx context.Context, projectSFID string, companySFID string, searchTerm string) (*models.CorporateContributorList, error)
- GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string) (*models.CompanyProjectClaList, error)
+ GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string, companyID *string) (*models.CompanyProjectClaList, error)
CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string) (*models.CompanyOutput, error)
GetCompanyByName(ctx context.Context, companyName string) (*models.Company, error)
GetCompanyBySigningEntityName(ctx context.Context, signingEntityName string) (*models.Company, error)
@@ -770,7 +770,7 @@ func (s *service) DeleteCompanyBySFID(ctx context.Context, companyID string) err
return s.companyRepo.DeleteCompanyBySFID(ctx, companyID)
}
-func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string) (*models.CompanyProjectClaList, error) {
+func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string, companyID *string) (*models.CompanyProjectClaList, error) {
f := logrus.Fields{
"functionName": "company.service.GetCompanyProjectCLA",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -792,7 +792,9 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
// Attempt to locate the company model in our database
log.WithFields(f).Debug("locating company by SF ID")
var companyModel *v1Models.Company
- companies, companyErr := s.companyRepo.GetCompaniesByExternalID(ctx, companySFID)
+ var companies []*v1Models.Company
+ var companyErr error
+ companies, companyErr = s.companyRepo.GetCompaniesByExternalID(ctx, companySFID)
if companyErr != nil {
// If we were unable to find the company/org in our local database, try to auto-create based
// on the existing SF record
@@ -822,6 +824,16 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
}
var companyProjectClaList = make([]*models.CompanyProjectCla, 0)
+ if companyID != nil {
+ log.WithFields(f).Debugf("Filtering company for ID: %s ", *companyID)
+ index, found := findCompany(companies, *companyID)
+ if found {
+ log.WithFields(f).Debugf("Found company: %v ", companies[index])
+ companies = []*v1Models.Company{companies[index]}
+ } else {
+ companies = []*v1Models.Company{}
+ }
+ }
for _, company := range companies {
activeCLAList, err := s.GetCompanyProjectActiveCLAs(ctx, company.CompanyID, projectSFID)
if err != nil {
@@ -1567,3 +1579,12 @@ func validateRequestCompanyAdmin(userID string, claManagerName string, contribut
return nil
}
+
+func findCompany(companies []*v1Models.Company, companyID string) (int, bool) {
+ for index, company := range companies {
+ if company.CompanyID == companyID {
+ return index, true
+ }
+ }
+ return -1, false
+}
From 3ffa347f08727b121122fa103b5d612159e9e382 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Tue, 26 Jan 2021 18:15:01 +0200
Subject: [PATCH 0012/1276] companySFID -> companyID change for metrics
endpoints
Signed-off-by: makkalot
---
cla-backend-go/swagger/cla.v2.yaml | 10 +++-------
cla-backend-go/v2/metrics/handlers.go | 28 ++++++++++++++++++---------
2 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 819e63cc9..7fce303d2 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -164,11 +164,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - name: companyID
- description: the company ID
- in: path
- type: string
- required: true
+ - $ref: '#/parameters/path-companyID'
responses:
'200':
description: 'Success'
@@ -312,7 +308,7 @@ paths:
tags:
- metrics
- /metrics/company/{companySFID}/project/{projectSFID}:
+ /metrics/company/{companyID}/project/{projectSFID}:
get:
summary: List project metrics for a company
description: Returns list of project metrics for company
@@ -322,7 +318,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-projectSFID"
responses:
'200':
diff --git a/cla-backend-go/v2/metrics/handlers.go b/cla-backend-go/v2/metrics/handlers.go
index 05edfaaa1..60106acc4 100644
--- a/cla-backend-go/v2/metrics/handlers.go
+++ b/cla-backend-go/v2/metrics/handlers.go
@@ -7,11 +7,14 @@ import (
"context"
"fmt"
+ "github.com/sirupsen/logrus"
+
"github.com/LF-Engineering/lfx-kit/auth"
v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/metrics"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/runtime/middleware"
)
@@ -95,22 +98,29 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
func(params metrics.ListCompanyProjectMetricsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "MetricsListCompanyProjectMetricsHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": params.CompanyID,
+ }
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ company, compErr := v1CompanyRepo.GetCompany(ctx, params.CompanyID)
+ if compErr != nil {
+ log.WithFields(f).Warnf("unable to fetch company by ID:%s ", params.CompanyID)
+ return metrics.NewListCompanyProjectMetricsBadRequest().WithPayload(errorResponse(reqID, compErr))
+ }
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForOrganization(authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(authUser, company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
return metrics.NewListCompanyProjectMetricsForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to List Company Project Metrics with Organization scope of %s",
- authUser.UserName, params.CompanySFID),
+ authUser.UserName, company.CompanyExternalID),
XRequestID: reqID,
})
}
- comp, err := v1CompanyRepo.GetCompanyByExternalID(ctx, params.CompanySFID)
- if err != nil {
- if err == v1Company.ErrCompanyDoesNotExist {
- return metrics.NewListCompanyProjectMetricsNotFound().WithXRequestID(reqID)
- }
- }
- result, err := service.ListCompanyProjectMetrics(comp.CompanyID, params.ProjectSFID)
+
+ result, err := service.ListCompanyProjectMetrics(params.CompanyID, params.ProjectSFID)
if err != nil {
return metrics.NewListCompanyProjectMetricsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
From 7f04c3bfc6a1bbbacc34aef4eec03be11639a515 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 26 Jan 2021 14:41:48 -0500
Subject: [PATCH 0013/1276] Updated GitHub Organization v2 API Logging (#2528)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/handlers.go | 14 +--
.../v2/github_organizations/handlers.go | 96 +++++++++----------
2 files changed, 52 insertions(+), 58 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 3ce030747..f19ce4284 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -39,7 +39,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupCreateClaGroupHandler",
+ "functionName": "cla_groups.handlers.ClaGroupCreateClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupName": utils.StringValue(params.ClaGroupInput.ClaGroupName),
"foundationSFID": utils.StringValue(params.ClaGroupInput.FoundationSfid),
@@ -85,7 +85,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupUpdateClaGroupHandler",
+ "functionName": "cla_groups.handlers.ClaGroupUpdateClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -160,7 +160,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupDeleteClaGroupHandler",
+ "functionName": "cla_groups.handlers.ClaGroupDeleteClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -226,7 +226,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupEnrollProjectsHandler",
+ "functionName": "cla_groups.handlers.ClaGroupEnrollProjectsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ClaGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -323,7 +323,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupUnenrollProjectsHandler",
+ "functionName": "cla_groups.handlers.ClaGroupUnenrollProjectsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ClaGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -395,7 +395,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "ClaGroupListClaGroupsUnderFoundationHandler",
+ "functionName": "cla_groups.handlers.ClaGroupListClaGroupsUnderFoundationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"authUsername": params.XUSERNAME,
@@ -504,7 +504,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// isUserHaveAccessToCLAProject is a helper function to determine if the user has access to the specified project
func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, parentProjectSFID string, projectSFIDs []string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAProject",
+ "functionName": "cla_groups.handlers.isUserHaveAccessToCLAProject",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFIDs": strings.Join(projectSFIDs, ","),
diff --git a/cla-backend-go/v2/github_organizations/handlers.go b/cla-backend-go/v2/github_organizations/handlers.go
index 514f224a0..ac4f893c5 100644
--- a/cla-backend-go/v2/github_organizations/handlers.go
+++ b/cla-backend-go/v2/github_organizations/handlers.go
@@ -13,7 +13,6 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/github"
@@ -23,6 +22,7 @@ import (
// Configure setups handlers on api with service
func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+
api.GithubOrganizationsGetProjectGithubOrganizationsHandler = github_organizations.GetProjectGithubOrganizationsHandlerFunc(
func(params github_organizations.GetProjectGithubOrganizationsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
@@ -30,7 +30,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubOrganizationsGetProjectGithubOrganizationsHandler",
+ "functionName": "github_organizations.handlers.GitHubOrganizationsGetProjectGithubOrganizationsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -70,7 +70,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubOrganizationsAddProjectGithubOrganizationHandler",
+ "functionName": "github_organization.handlers.GitHubOrganizationsAddProjectGithubOrganizationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -145,26 +145,32 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "github_organization.handlers.GithubOrganizationsDeleteProjectGithubOrganizationHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "orgName": params.OrgName,
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ }
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- return github_organizations.NewDeleteProjectGithubOrganizationForbidden().WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete Project GitHub Organizations with Project scope of %s",
- authUser.UserName, params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("user %s does not have access to Delete Project GitHub Organizations with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewDeleteProjectGithubOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
err := service.DeleteGithubOrganization(ctx, params.ProjectSFID, params.OrgName)
if err != nil {
if strings.Contains(err.Error(), "getProjectNotFound") {
- return github_organizations.NewDeleteProjectGithubOrganizationNotFound().WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("project not found with given ID. [%s]", params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("project not found with given SFID: %s", params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewDeleteProjectGithubOrganizationNotFound().WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
- return github_organizations.NewDeleteProjectGithubOrganizationBadRequest().WithPayload(errorResponse(reqID, err))
+ msg := fmt.Sprintf("problem deleting GitHub Organization with project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewDeleteProjectGithubOrganizationBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
eventService.LogEvent(&events.LogEventArgs{
@@ -185,64 +191,52 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "github_organization.handlers.GithubOrganizationsUpdateProjectGithubOrganizationConfigHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "orgName": params.OrgName,
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- return github_organizations.NewUpdateProjectGithubOrganizationConfigForbidden().WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Update Project GitHub Organizations with Project scope of %s",
- authUser.UserName, params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("user %s does not have access to Update Project GitHub Organizations with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewUpdateProjectGithubOrganizationConfigForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
if params.Body.AutoEnabled == nil {
- return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "EasyCLA - 400 Bad Request - missing auto enable value in body",
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("missing auto enable value in request body for project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
- return github_organizations.NewAddProjectGithubOrganizationBadRequest().WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "EasyCLA - 400 Bad Request - AutoEnabledClaGroupID can't be empty when AutoEnabled",
- })
+ msg := fmt.Sprintf("AutoEnabledClaGroupID can't be empty when AutoEnabled flag is set to true - issue in request body for project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
err := service.UpdateGithubOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
if err != nil {
- return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(errorResponse(reqID, err))
+ msg := fmt.Sprintf("problem updating GitHub Organization for project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
+ log.WithFields(f).Debug(msg)
+ return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
+ // Log the event
eventService.LogEvent(&events.LogEventArgs{
LfUsername: authUser.UserName,
EventType: events.GitHubOrganizationUpdated,
ExternalProjectID: params.ProjectSFID,
EventData: &events.GitHubOrganizationUpdatedEventData{
GitHubOrganizationName: params.OrgName,
- AutoEnabled: *params.Body.AutoEnabled,
+ AutoEnabled: utils.BoolValue(params.Body.AutoEnabled),
},
})
return github_organizations.NewUpdateProjectGithubOrganizationConfigOK()
})
}
-
-type codedResponse interface {
- Code() string
-}
-
-func errorResponse(reqID string, err error) *models.ErrorResponse {
- code := ""
- if e, ok := err.(codedResponse); ok {
- code = e.Code()
- }
-
- e := models.ErrorResponse{
- Code: code,
- Message: err.Error(),
- XRequestID: reqID,
- }
-
- return &e
-}
From 105cc399badf2819537b8610bf9dc3fdee352dec Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 27 Jan 2021 17:55:25 +0200
Subject: [PATCH 0014/1276] signatures endpoints companySFID -> companyID
migration (#2530)
Signed-off-by: makkalot
---
cla-backend-go/swagger/cla.v2.yaml | 22 +--
cla-backend-go/v2/signatures/handlers.go | 192 ++++++++++++++---------
cla-backend-go/v2/signatures/service.go | 43 ++---
3 files changed, 139 insertions(+), 118 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 18b17c913..01647e697 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1641,7 +1641,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-claGroupID"
- - $ref: "#/parameters/companySFID"
+ - $ref: "#/parameters/companyID"
- $ref: '#/parameters/searchTerm'
responses:
'200':
@@ -1987,7 +1987,7 @@ paths:
# --------------------------------------------------------
# Employee CLA Endpoints - CSV Report Download
# --------------------------------------------------------
- /signatures/project/{claGroupID}/company/{companySFID}/employee/csv:
+ /signatures/project/{claGroupID}/company/{companyID}/employee/csv:
get:
summary: Downloads all employee CLA information as a CSV document for this project
description: Downloads the employee CLA information as a CSV document for this project
@@ -1998,7 +1998,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-claGroupID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
produces:
- text/json
- text/csv
@@ -2022,7 +2022,7 @@ paths:
tags:
- signatures
- /signatures/project/{projectSFID}/company/{companySFID}:
+ /signatures/project/{projectSFID}/company/{companyID}:
get:
summary: Get project company ccla signatures
description: Returns a list of ccla signature models when provided the project ID and company ID
@@ -2033,7 +2033,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: '#/parameters/sortOrder'
responses:
'200':
@@ -2055,7 +2055,7 @@ paths:
tags:
- signatures
- /signatures/company/{companySFID}:
+ /signatures/company/{companyID}:
get:
summary: Get company signatures
description: Returns a list of company signatures when provided the company ID
@@ -2065,7 +2065,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: '#/parameters/path-companySFID'
+ - $ref: '#/parameters/path-companyID'
- $ref: '#/parameters/signatureType'
- $ref: '#/parameters/pageSize'
- $ref: '#/parameters/nextKey'
@@ -2130,7 +2130,7 @@ paths:
tags:
- signatures
- /signatures/project/{projectSFID}/company/{companySFID}/employee:
+ /signatures/project/{projectSFID}/company/{companyID}/employee:
get:
summary: Get project company signatures for the employees
description: Returns a list of employee project signature models when provided the project ID and company ID
@@ -2141,7 +2141,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: '#/parameters/pageSize'
- $ref: '#/parameters/nextKey'
- $ref: '#/parameters/sortOrder'
@@ -2165,7 +2165,7 @@ paths:
tags:
- signatures
- /signatures/project/{projectSFID}/company/{companySFID}/clagroup/{claGroupID}/approval-list:
+ /signatures/project/{projectSFID}/company/{companyID}/clagroup/{claGroupID}/approval-list:
put:
summary: Updates the Project / Organization/Company Approval list
description: API to update the project and organization/company approval list.
@@ -2176,7 +2176,7 @@ paths:
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- $ref: "#/parameters/path-projectSFID"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- name: claGroupID
in: path
type: string
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index fd9973597..54548d646 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -99,13 +99,29 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
+ }
+
+ companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
+ if err != nil {
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
+ }
+ return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
+ })
}
// Must be in the Project|Organization Scope to see this - signature ACL is double-checked in the service level when the signature is loaded
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, params.CompanySFID, utils.DISALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, companyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to update Project Company Approval List with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID)
+ authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return signatures.NewUpdateApprovalListForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -118,15 +134,6 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
return validationError
}
- // Lookup the internal company ID when provided the external ID via the v1SignatureGService call
- log.WithFields(f).Debug("loading company by company SFID")
- companyModel, compErr := companyService.GetCompanyByExternalID(ctx, params.CompanySFID)
- if compErr != nil || companyModel == nil {
- msg := fmt.Sprintf("unable to locate company by external company ID: %s", params.CompanySFID)
- log.WithFields(f).Warn(msg)
- return signatures.NewUpdateApprovalListNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
- }
-
log.WithFields(f).Debug("loading CLA groups by projectSFID")
projectModels, projsErr := projectService.GetCLAGroupsByExternalSFID(ctx, params.ProjectSFID)
if projsErr != nil || projectModels == nil {
@@ -145,7 +152,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
// Convert the v2 input parameters to a v1 model
v1ApprovalList := v1Models.ApprovalList{}
- err := copier.Copy(&v1ApprovalList, params.Body)
+ err = copier.Copy(&v1ApprovalList, params.Body)
if err != nil {
msg := "unable to convert v1 to v2 approval list"
log.WithFields(f).Warn(msg)
@@ -435,25 +442,41 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
"functionName": "SignaturesGetProjectCompanySignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
+ }
+
+ companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
+ if err != nil {
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewGetProjectCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
+ }
+ return signatures.NewGetProjectCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
+ })
}
// Must be in the one of the above scopes to see this
// - if project scope (like a PM)
// - if project|organization scope (like CLA Manager, CLA Signatory)
// - if organization scope (like company admin)
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, params.CompanySFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s is not authorized to view project company signatures any scope of project: %s, organization %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID)
+ authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return signatures.NewGetProjectCompanySignaturesForbidden().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
}
log.WithFields(f).Debug("loading project company signatures...")
- projectSignatures, err := v2service.GetProjectCompanySignatures(ctx, params.CompanySFID, params.ProjectSFID)
+ projectSignatures, err := v2service.GetProjectCompanySignatures(ctx, params.CompanyID, companyModel.CompanyExternalID, params.ProjectSFID)
if err != nil {
- msg := fmt.Sprintf("error retrieving project signatures for project: %s, company: %s", params.ProjectSFID, params.CompanySFID)
+ msg := fmt.Sprintf("error retrieving project signatures for project: %s, company: %s", params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return signatures.NewGetProjectCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
@@ -470,55 +493,37 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
"functionName": "SignaturesGetProjectCompanyEmployeeSignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
"nextKey": aws.StringValue(params.NextKey),
"pageSize": aws.Int64Value(params.PageSize),
}
- // Try to load the company model - use both approaches - internal and external
- var companyModel *v1Models.Company
- var err error
- // Internal IDs are UUIDv4 - external are not
- if utils.IsUUIDv4(params.CompanySFID) {
- // Oops - not provided a SFID - but an internal ID - that'iclaNotSupported ok, we'll lookup via the internal ID
- log.WithFields(f).Debug("companySFID provided as internal ID - looking up record by internal ID")
- // Lookup the company model by internal ID
- companyModel, err = companyService.GetCompany(ctx, params.CompanySFID)
- if companyModel != nil && companyModel.CompanyExternalID == "" {
- msg := fmt.Sprintf("problem loading company - company external ID not defined - comapny ID: %s", params.CompanySFID)
- log.WithFields(f).WithError(err).Warn(msg)
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(
- reqID, msg))
- }
- } else {
- // Lookup the company model by external ID
- log.WithFields(f).Debug("companySFID provided as external ID - looking up record by external ID")
- companyModel, err = companyService.GetCompanyByExternalID(ctx, params.CompanySFID)
- }
+ companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
if err != nil {
- var companyDoesNotExistErr utils.CompanyDoesNotExist
- if errors.Is(err, &companyDoesNotExistErr) {
- msg := "problem loading company by ID"
- log.WithFields(f).WithError(err).Warn(msg)
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
}
-
- log.WithFields(f).WithError(err).Warnf("problem loading company by ID")
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("problem loading company by ID: %s", params.CompanySFID), err))
+ return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
+ })
}
if companyModel == nil {
- msg := fmt.Sprintf("problem loading company by ID: %s", params.CompanySFID)
+ msg := fmt.Sprintf("problem loading company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseNotFound(reqID, msg))
}
log.WithFields(f).Debug("checking access control permissions...")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, params.CompanySFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s is not authorized to view project company signatures any scope of project: %s, organization %s",
- authUser.UserName, params.ProjectSFID, params.CompanySFID)
+ authUser.UserName, params.ProjectSFID, params.CompanyID)
log.Warn(msg)
return signatures.NewGetProjectCompanyEmployeeSignaturesForbidden().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -548,9 +553,9 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
})
if err != nil {
log.WithFields(f).WithError(err).Warnf("error retrieving employee project signatures for project: %s, company: %s, error: %+v",
- params.ProjectSFID, params.CompanySFID, err)
+ params.ProjectSFID, params.CompanyID, err)
return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(
- reqID, fmt.Sprintf("unable to fetch employee signatures for project ID: %s and company: %s", params.ProjectSFID, params.CompanySFID), err))
+ reqID, fmt.Sprintf("unable to fetch employee signatures for project ID: %s and company: %s", params.ProjectSFID, params.CompanyID), err))
}
resp, err := v2Signatures(projectSignatures)
@@ -573,24 +578,29 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
f := logrus.Fields{
"functionName": "SignaturesGetCompanySignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
"companyName": aws.StringValue(params.CompanyName),
"signatureType": aws.StringValue(params.SignatureType),
"nextKey": aws.StringValue(params.NextKey),
"pageSize": aws.Int64Value(params.PageSize),
}
- // Lookup the internal company ID
- companyModel, err := companyService.GetCompanyByExternalID(ctx, params.CompanySFID)
+ companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem loading company by SFID - returning empty response")
- // Not sure this is the correct response as the LFX UI/Admin console wants 200 empty lists instead of non-200 status back
- return signatures.NewGetCompanySignaturesOK().WithXRequestID(reqID).WithPayload(&models.Signatures{
- Signatures: []*models.Signature{},
- ResultCount: 0,
- TotalCount: 0,
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewGetCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
+ }
+ return signatures.NewGetCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
})
}
+
if companyModel == nil {
log.WithFields(f).WithError(err).Warnf("problem loading company model by ID - returning empty response")
// Not sure this is the correct response as the LFX UI/Admin console wants 200 empty lists instead of non-200 status back
@@ -696,7 +706,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
"functionName": "SignaturesDownloadProjectSignatureEmployeeAsCSVHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
}
log.WithFields(f).Debug("processing request...")
@@ -731,8 +741,24 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
// utils.ErrorResponseBadRequest(reqID, cclaNotSupportedForCLAGroup))
}
+ companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
+ if err != nil {
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
+ }
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
+ })
+ }
+
log.WithFields(f).Debug("checking access control permissions for user...")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, params.CompanySFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
msg := fmt.Sprintf(" user %s is not authorized to view project employee signatures any scope of project",
authUser.UserName)
log.Warn(msg)
@@ -741,9 +767,9 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for corporate contributor signatures...")
- result, err := v2service.GetClaGroupCorporateContributorsCsv(ctx, params.ClaGroupID, params.CompanySFID)
+ result, err := v2service.GetClaGroupCorporateContributorsCsv(ctx, params.ClaGroupID, params.CompanyID)
if err != nil {
- msg := fmt.Sprintf("problem getting corporate contributors CSV for CLA Group: %s with company: %s", params.ClaGroupID, params.CompanySFID)
+ msg := fmt.Sprintf("problem getting corporate contributors CSV for CLA Group: %s with company: %s", params.ClaGroupID, companyModel.CompanyExternalID)
if _, ok := err.(*organizations.GetOrgNotFound); ok {
formatErr := errors.New("error retrieving company using companySFID")
return signatures.NewDownloadProjectSignatureEmployeeAsCSVNotFound().WithXRequestID(reqID).WithPayload(
@@ -830,17 +856,33 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
"functionName": "SignaturesListClaGroupCorporateContributorsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
}
- // Make sure the user has provided the companySFID
- if params.CompanySFID == nil {
- msg := "missing companySFID as input"
+ // Make sure the user has provided the companyID
+ if params.CompanyID == nil {
+ msg := "missing companyID as input"
log.WithFields(f).Warn(msg)
return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseBadRequest(reqID, msg))
}
+ companyModel, err := companyService.GetCompany(ctx, *params.CompanyID)
+ if err != nil {
+ msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", *params.CompanyID, err)
+ log.Warn(msg)
+ if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
+ Code: "404",
+ })
+ }
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
+ Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
+ Code: "400",
+ })
+ }
+
// Lookup the CLA Group by ID - make sure it's valid
claGroupModel, err := projectRepo.GetCLAGroupByID(ctx, params.ClaGroupID, project.DontLoadRepoDetails)
if err != nil {
@@ -868,18 +910,18 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
f["foundationSFID"] = claGroupModel.FoundationSFID
log.WithFields(f).Debug("checking access control permissions for user...")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, *params.CompanySFID, projectClaGroupsRepo) {
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s is not authorized to view project CCLA signatures any scope of project or project|organization scope with company ID: %s",
- authUser.UserName, aws.StringValue(params.CompanySFID))
+ authUser.UserName, companyModel.CompanyID)
log.Warn(msg)
return signatures.NewListClaGroupCorporateContributorsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for CCLA signatures...")
- result, err := v2service.GetClaGroupCorporateContributors(ctx, params.ClaGroupID, params.CompanySFID, params.SearchTerm)
+ result, err := v2service.GetClaGroupCorporateContributors(ctx, params.ClaGroupID, *params.CompanyID, params.SearchTerm)
if err != nil {
- msg := fmt.Sprintf("problem getting corporate contributors for CLA Group: %s with company: %s", params.ClaGroupID, *params.CompanySFID)
+ msg := fmt.Sprintf("problem getting corporate contributors for CLA Group: %s with company: %s", params.ClaGroupID, *params.CompanyID)
if _, ok := err.(*organizations.GetOrgNotFound); ok {
formatErr := errors.New("error retrieving company using companySFID")
return signatures.NewListClaGroupCorporateContributorsNotFound().WithXRequestID(reqID).WithPayload(
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 16bb8a8d2..d67b7a219 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -53,12 +53,12 @@ type service struct {
// Service contains method of v2 signature service
type Service interface {
- GetProjectCompanySignatures(ctx context.Context, companySFID string, projectSFID string) (*models.Signatures, error)
+ GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error)
GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error)
- GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companySFID string) ([]byte, error)
- GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companySFID *string, searchTerm *string) (*models.CorporateContributorList, error)
+ GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error)
+ GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companySFID string, searchTerm *string) (*models.CorporateContributorList, error)
GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error)
GetSignedIclaZipPdf(claGroupID string) (*models.URLObject, error)
GetSignedCclaZipPdf(claGroupID string) (*models.URLObject, error)
@@ -79,18 +79,14 @@ func NewService(awsSession *session.Session, signaturesBucketName string, v1Proj
}
}
-func (s *service) GetProjectCompanySignatures(ctx context.Context, companySFID string, projectSFID string) (*models.Signatures, error) {
- companyModel, err := s.v1CompanyService.GetCompanyByExternalID(ctx, companySFID)
- if err != nil {
- return nil, err
- }
+func (s *service) GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error) {
pm, err := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
if err != nil {
return nil, err
}
signed := true
approved := true
- sig, err := s.v1SignatureService.GetProjectCompanySignature(ctx, companyModel.CompanyID, pm.ClaGroupID, &signed, &approved, nil, aws.Int64(HugePageSize))
+ sig, err := s.v1SignatureService.GetProjectCompanySignature(ctx, companyID, pm.ClaGroupID, &signed, &approved, nil, aws.Int64(HugePageSize))
if err != nil {
return nil, err
}
@@ -101,7 +97,7 @@ func (s *service) GetProjectCompanySignatures(ctx context.Context, companySFID s
resp.ResultCount = 1
resp.Signatures = append(resp.Signatures, sig)
}
- return v2SignaturesReplaceCompanyID(resp, companyModel.CompanyID, companySFID)
+ return v2SignaturesReplaceCompanyID(resp, companyID, companySFID)
}
func eclaSigCsvLine(sig *v1Models.CorporateContributor) string {
@@ -116,14 +112,9 @@ func eclaSigCsvLine(sig *v1Models.CorporateContributor) string {
return fmt.Sprintf("\n%s,%s,%s,%s,\"%s\"", sig.GithubID, sig.LinuxFoundationID, sig.Name, sig.Email, dateTime)
}
-func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companySFID string) ([]byte, error) {
+func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error) {
var b bytes.Buffer
- comp, companyErr := s.v1CompanyService.GetCompanyByExternalID(ctx, companySFID)
- if companyErr != nil {
- return nil, companyErr
- }
-
- result, err := s.v1SignatureService.GetClaGroupCorporateContributors(ctx, claGroupID, &comp.CompanyID, nil)
+ result, err := s.v1SignatureService.GetClaGroupCorporateContributors(ctx, claGroupID, &companyID, nil)
if err != nil {
return nil, err
}
@@ -265,30 +256,18 @@ func (s service) IsZipPresentOnS3(zipFilePath string) (bool, error) {
return true, nil
}
-func (s service) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companySFID *string, searchTerm *string) (*models.CorporateContributorList, error) {
+func (s service) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID string, searchTerm *string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
"functionName": "GetClaGroupCorporateContributors",
"claGroupID": claGroupID,
- }
- if companySFID != nil {
- f["companySFID"] = *companySFID
+ "companyID": companyID,
}
if searchTerm != nil {
f["searchTerm"] = *searchTerm
}
- var companyID *string
- if companySFID != nil {
- log.WithFields(f).Debug("loading company by companySFID...")
- companyModel, err := s.v1CompanyService.GetCompanyByExternalID(ctx, *companySFID)
- if err != nil {
- return nil, err
- }
- companyID = &companyModel.CompanyID
- }
-
log.WithFields(f).Debug("querying CLA corporate contributors...")
- result, err := s.v1SignatureService.GetClaGroupCorporateContributors(ctx, claGroupID, companyID, searchTerm)
+ result, err := s.v1SignatureService.GetClaGroupCorporateContributors(ctx, claGroupID, &companyID, searchTerm)
if err != nil {
return nil, err
}
From 480a5856040bcab469bef95df1eca903e41d67da Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 27 Jan 2021 17:56:35 +0200
Subject: [PATCH 0015/1276] the # sign in the url was confusing the underlying
urllib (#2531)
Signed-off-by: makkalot
---
cla-backend/cla/tests/unit/test_utils.py | 21 +++++++++++++++++
cla-backend/cla/utils.py | 30 +++++++++++++++++++++---
2 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/cla-backend/cla/tests/unit/test_utils.py b/cla-backend/cla/tests/unit/test_utils.py
index bfda0d026..cd1eaf928 100644
--- a/cla-backend/cla/tests/unit/test_utils.py
+++ b/cla-backend/cla/tests/unit/test_utils.py
@@ -225,6 +225,27 @@ def test_append_project_version_to_url():
assert "version=1" in url
assert "something=else" in url
+ # try the weird case with # in url
+ url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=url, project_version="v2")
+ assert "version=2" in url
+ assert "version=1" not in url
+
+ url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=url, project_version="")
+ assert "version=1" in url
+ assert "version=2" not in url
+
+ url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=url, project_version=None)
+ assert "version=1" in url
+ assert "version=2" not in url
+
+ url = "https://dev.lfcla.com/#/?something=else"
+ url = append_project_version_to_url(address=url, project_version="")
+ assert "version=1" in url
+ assert "something=else" in url
+ assert "version=2" not in url
if __name__ == '__main__':
unittest.main()
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index e12f04edc..fe019c0b7 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -779,9 +779,9 @@ def get_full_sign_url(repository_service, installation_id, github_repository_id,
"""
base_url = '{}/v2/repository-provider/{}/sign/{}/{}/{}/#/'.format(cla.conf['API_BASE_URL'], repository_service,
- str(installation_id),
- str(github_repository_id),
- str(change_request_id))
+ str(installation_id),
+ str(github_repository_id),
+ str(change_request_id))
return append_project_version_to_url(address=base_url, project_version=project_version)
@@ -801,6 +801,30 @@ def append_project_version_to_url(address: str, project_version: str) -> str:
if "version" in f.args:
return address
f.args["version"] = version
+
+ # seem if the url has # in it (https://dev.lfcla.com/#/) the underlying urllib is being confused
+ # In[7]: url_parts = list(urlparse.urlparse(address))
+ # In[8]: url_parts
+ # Out[8]: ['https', 'dev.lfcla.com', '/', '', '', '/']
+ # In [9]: query = dict(urlparse.parse_qsl(url_parts[4]))
+ # In[12]: query["version"] = "1"
+ # In[13]: query
+ # Out[13]: {'version': '1'}
+ #
+ # In[14]: url_parts[4] = urlencode(query)
+ #
+ # In[15]: print(urlparse.urlunparse(url_parts))
+ # https://dev.lfcla.com/?version = 1#/
+
+ final_url = f.url
+ final_url = final_url.rstrip("/")
+ if final_url.endswith("#"):
+ final_url = final_url.rstrip("#")
+ parts = final_url.split("?")
+ if len(parts) > 2:
+ return f.url
+ return "#/?".join(parts)
+
return f.url
From 8a3203848b896435d794562948b07c035880e7f5 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Wed, 27 Jan 2021 18:48:22 +0300
Subject: [PATCH 0016/1276] [#2527,#2529] Bug/Invite Company Admin
- Resolved assigning company designee bug that was caused by wrong companyID
Signed-off-by: wanyaland
---
cla-backend-go/v2/cla_manager/handlers.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 50 +----------------------
2 files changed, 3 insertions(+), 49 deletions(-)
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 0a67e154c..7d7d7c60a 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -158,7 +158,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
// Note: anyone create assign a CLA manager designee...no permissions checks
log.WithFields(f).Debugf("processing create CLA Manager Desginee request")
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- claManagerDesignee, err := service.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, params.ProjectSFID, params.Body.UserEmail.String())
+ claManagerDesignee, err := service.CreateCLAManagerDesignee(ctx, params.CompanyID, params.ProjectSFID, params.Body.UserEmail.String())
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
msg := fmt.Sprintf("Conflict assigning cla manager role for Project SFID: %s ", params.ProjectSFID)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 9f9dff0bd..39467d46d 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -37,7 +37,6 @@ import (
v2OrgService "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
v2UserService "github.com/communitybridge/easycla/cla-backend-go/v2/user-service"
- v2UserModels "github.com/communitybridge/easycla/cla-backend-go/v2/user-service/models"
)
var (
@@ -979,7 +978,7 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
// check if claGroup is signed at foundation level
foundationSFID := projectCLAGroups[0].FoundationSFID
log.WithFields(f).Debugf("Create cla manager designee for foundation : %s ", foundationSFID)
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, organization.ID, foundationSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, companyID, foundationSFID, userEmail)
if err != nil {
msg := fmt.Sprintf("Problem creating cla Manager Designee for user : %s, error: %+v ", userEmail, err)
log.WithFields(f).Warn(msg)
@@ -989,7 +988,7 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
} else {
for _, pcg := range projectCLAGroups {
log.WithFields(f).Debugf("Create cla manager designee for Project SFID: %s", pcg.ProjectSFID)
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, organization.ID, pcg.ProjectSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, companyID, pcg.ProjectSFID, userEmail)
if err != nil {
msg := fmt.Sprintf("Problem creating cla Manager Designee for user : %s, error: %+v ", userEmail, err)
log.WithFields(f).Warn(msg)
@@ -998,10 +997,6 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
designeeScopes = append(designeeScopes, claManagerDesignee)
}
}
- conversionErr := s.convertGHUserToContact(ctx, contributor)
- if conversionErr != nil {
- return nil, conversionErr
- }
log.Debugf("Sending Email to CLA Manager Designee email: %s ", userEmail)
@@ -1440,47 +1435,6 @@ func buildErrorMessage(errPrefix string, claGroupID string, params cla_manager.C
errPrefix, params.CompanyID, claGroupID, *params.Body.FirstName, *params.Body.LastName, *params.Body.UserEmail, err)
}
-func (s *service) convertGHUserToContact(ctx context.Context, contributor *v1User.User) error {
- f := logrus.Fields{
- "functionName": "cla_manager.service.convertGHUserToContact",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- }
-
- userService := v2UserService.GetClient()
- log.Infof("Checking if GH User: %s, GH ID: %s has LFID for contact conversion ", contributor.UserGithubUsername, contributor.UserGithubID)
- var GHUserLF *v2UserModels.User
- var GHUserErr error
- if contributor.LFEmail != "" {
- GHUserLF, GHUserErr = userService.SearchUserByEmail(contributor.LFEmail)
- if GHUserErr != nil {
- msg := fmt.Sprintf("GH UserEmail: %s has no LF Login ", contributor.LFEmail)
- log.Warn(msg)
- }
-
- } else if contributor.LFUsername != "" {
- GHUserLF, GHUserErr = userService.GetUserByUsername(contributor.LFUsername)
- if GHUserErr != nil {
- msg := fmt.Sprintf("GH Username: %s has no LF Login ", contributor.LFUsername)
- log.Warn(msg)
- }
- }
-
- if GHUserLF != nil {
- // Convert user to contact
- if GHUserLF.Type == utils.Lead {
- // convert user to contact
- log.WithFields(f).Debug("converting lead to contact")
- err := userService.ConvertToContact(GHUserLF.ID)
- if err != nil {
- msg := fmt.Sprintf("converting lead to contact failed: %v", err)
- log.WithFields(f).Warn(msg)
- return err
- }
- }
- }
- return nil
-}
-
func companyV1toV2(v1CompanyModel *v1Models.Company) *models.Company {
return &models.Company{
CompanyACL: v1CompanyModel.CompanyACL,
From d66ddb4a4f9fc584ce01dfcfb122943e4324f6ed Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 27 Jan 2021 19:44:43 +0200
Subject: [PATCH 0017/1276] cover more cases for urls that include # and remove
the furl library (#2532)
since we needed more low level access to the url parsing
Signed-off-by: makkalot
---
cla-backend/cla/tests/unit/test_utils.py | 77 +++++++++++++++++-------
cla-backend/cla/utils.py | 48 ++++++---------
cla-backend/requirements.txt | 1 -
3 files changed, 74 insertions(+), 52 deletions(-)
diff --git a/cla-backend/cla/tests/unit/test_utils.py b/cla-backend/cla/tests/unit/test_utils.py
index cd1eaf928..5b246ebf0 100644
--- a/cla-backend/cla/tests/unit/test_utils.py
+++ b/cla-backend/cla/tests/unit/test_utils.py
@@ -192,60 +192,93 @@ def test_get_full_sign_url():
def test_append_project_version_to_url():
- url = "http://localhost:5000/v1/sign"
- url = append_project_version_to_url(address=url, project_version="v1")
+ original_url = "http://localhost:5000/v1/sign"
+ url = append_project_version_to_url(address=original_url, project_version="v1")
+ print(url)
assert "?version=1" in url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign"
- url = append_project_version_to_url(address=url, project_version="v2")
+ original_url = "http://localhost:5000/v1/sign"
+ url = append_project_version_to_url(address=original_url, project_version="v2")
+ print(url)
assert "?version=2" in url
assert "http://localhost:5000/v1/sign?version=2" == url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign"
- url = append_project_version_to_url(address=url, project_version=None)
+ original_url = "http://localhost:5000/v1/sign"
+ url = append_project_version_to_url(address=original_url, project_version=None)
+ print(url)
assert "?version=1" in url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign"
- url = append_project_version_to_url(address=url, project_version="invalid")
+ original_url = "http://localhost:5000/v1/sign"
+ url = append_project_version_to_url(address=original_url, project_version="invalid")
+ print(url)
assert "?version=1" in url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign?something=else"
- url = append_project_version_to_url(address=url, project_version="v2")
+ original_url = "http://localhost:5000/v1/sign?something=else"
+ url = append_project_version_to_url(address=original_url, project_version="v2")
+ print(url)
assert "version=2" in url
assert "something=else" in url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign?version=1"
- url = append_project_version_to_url(address=url, project_version="v2")
+ original_url = "http://localhost:5000/v1/sign?version=1"
+ url = append_project_version_to_url(address=original_url, project_version="v2")
+ print(url)
assert "version=2" not in url
assert "version=1" in url
+ assert original_url in url
- url = "http://localhost:5000/v1/sign?something=else&version=1"
- url = append_project_version_to_url(address=url, project_version="v2")
+ original_url = "http://localhost:5000/v1/sign?something=else&version=1"
+ url = append_project_version_to_url(address=original_url, project_version="v2")
+ print(url)
assert "version=2" not in url
assert "version=1" in url
assert "something=else" in url
+ assert original_url in url
# try the weird case with # in url
- url = "https://dev.lfcla.com/#/"
- url = append_project_version_to_url(address=url, project_version="v2")
+ original_url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=original_url, project_version="v2")
+ print(url)
assert "version=2" in url
assert "version=1" not in url
+ assert original_url in url
- url = "https://dev.lfcla.com/#/"
- url = append_project_version_to_url(address=url, project_version="")
+ original_url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=original_url, project_version="")
+ print(url)
assert "version=1" in url
assert "version=2" not in url
+ assert original_url in url
- url = "https://dev.lfcla.com/#/"
- url = append_project_version_to_url(address=url, project_version=None)
+ original_url = "https://dev.lfcla.com/#/"
+ url = append_project_version_to_url(address=original_url, project_version=None)
+ print(url)
assert "version=1" in url
assert "version=2" not in url
+ assert original_url in url
- url = "https://dev.lfcla.com/#/?something=else"
- url = append_project_version_to_url(address=url, project_version="")
+ original_url= "https://dev.lfcla.com/#/#/?something=else"
+ url = append_project_version_to_url(address=original_url, project_version="")
+ print(url)
assert "version=1" in url
assert "something=else" in url
assert "version=2" not in url
+ assert original_url in url
+
+ # check for crazier example ...
+ original_url = "https://dev.lfcla.com/1/#/2/#/3/#/?something=else&this=that"
+ url = append_project_version_to_url(address=original_url, project_version="")
+ print(url)
+ assert "version=1" in url
+ assert "something=else" in url
+ assert "this=that" in url
+ assert "version=2" not in url
+ assert original_url in url
+
if __name__ == '__main__':
unittest.main()
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index fe019c0b7..e9d097d90 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -9,6 +9,8 @@
import json
import os
import urllib.parse
+import urllib.parse as urlparse
+from urllib.parse import urlencode
from datetime import datetime
from typing import List, Optional
@@ -16,7 +18,6 @@
import requests
from hug.middleware import SessionMiddleware
from requests_oauthlib import OAuth2Session
-from furl import furl
import cla
from cla.models import DoesNotExist
@@ -797,35 +798,24 @@ def append_project_version_to_url(address: str, project_version: str) -> str:
if project_version and project_version == 'v2':
version = "2"
- f = furl(address)
- if "version" in f.args:
+ # seem if the url has # in it (https://dev.lfcla.com/#/version=1) the underlying urllib is being confused
+ # In[21]: list(urlparse.urlparse(address))
+ # Out[21]: ['https', 'dev.lfcla.com', '/', '', '', '/#/?version=1']
+
+ query = {}
+ if "?" in address:
+ query = dict(urlparse.parse_qsl(address.split("?")[1]))
+
+ # we don't alter for now
+ if "version" in query:
return address
- f.args["version"] = version
-
- # seem if the url has # in it (https://dev.lfcla.com/#/) the underlying urllib is being confused
- # In[7]: url_parts = list(urlparse.urlparse(address))
- # In[8]: url_parts
- # Out[8]: ['https', 'dev.lfcla.com', '/', '', '', '/']
- # In [9]: query = dict(urlparse.parse_qsl(url_parts[4]))
- # In[12]: query["version"] = "1"
- # In[13]: query
- # Out[13]: {'version': '1'}
- #
- # In[14]: url_parts[4] = urlencode(query)
- #
- # In[15]: print(urlparse.urlunparse(url_parts))
- # https://dev.lfcla.com/?version = 1#/
-
- final_url = f.url
- final_url = final_url.rstrip("/")
- if final_url.endswith("#"):
- final_url = final_url.rstrip("#")
- parts = final_url.split("?")
- if len(parts) > 2:
- return f.url
- return "#/?".join(parts)
-
- return f.url
+
+ query["version"] = version
+ query_params_str = urlencode(query)
+
+ if "?" in address:
+ return "?".join([address.split("?")[0], query_params_str])
+ return "?".join([address, query_params_str])
def get_comment_badge(repository_type, all_signed, sign_url, project_version, missing_user_id=False,
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index 342206003..64247bdbb 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -16,7 +16,6 @@ docutils==0.15.2
ecdsa==0.14.1
falcon==2.0.0
future==0.18.2
-furl==2.1.0
gossip==2.3.1
gunicorn==19.9.0
hug==2.6.0
From dbf2b374d4515a3c5eeed3568b4c79fe8ba1437e Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 28 Jan 2021 06:57:58 +0300
Subject: [PATCH 0018/1276] [#2515] Bug/Events and Contributors for Company and
Project (#2534)
- Resolved events endpoint caused by incorrect filter attribute changed to event_company_id
- Updated company project contributors endpoint with internal companyID as path parameter
Signed-off-by: wanyaland
---
cla-backend-go/events/repository.go | 4 ++--
cla-backend-go/swagger/cla.v2.yaml | 4 ++--
cla-backend-go/v2/company/handlers.go | 17 +++++++++++++----
cla-backend-go/v2/company/service.go | 19 +++++++++----------
4 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 01a825b73..89853230f 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -473,7 +473,7 @@ func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, found
keyCondition := expression.Key("company_sfid_foundation_sfid").Equal(expression.Value(key))
var filter expression.ConditionBuilder
if companyID != "" {
- filter = expression.Name("company_id").Equal(expression.Value(companyID))
+ filter = expression.Name("event_company_id").Equal(expression.Value(companyID))
}
return repo.queryEventsTable(CompanySFIDFoundationSFIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
@@ -484,7 +484,7 @@ func (repo *repository) GetCompanyClaGroupEvents(companySFID, companyID, claGrou
keyCondition := expression.Key("company_sfid_project_id").Equal(expression.Value(key))
var filter expression.ConditionBuilder
if companyID != "" {
- filter = expression.Name("company_id").Equal(expression.Value(companyID))
+ filter = expression.Name("event_company_id").Equal(expression.Value(companyID))
}
return repo.queryEventsTable(CompanySFIDProjectIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 01647e697..3207f8f7e 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3170,7 +3170,7 @@ paths:
tags:
- company
- /company/{companySFID}/project/{projectSFID}/contributors:
+ /company/{companyID}/project/{projectSFID}/contributors:
get:
summary: Get corporate contributors for project
description: Returns list of corporate contributors for project
@@ -3180,7 +3180,7 @@ paths:
- $ref: "#/parameters/x-acl"
- $ref: "#/parameters/x-username"
- $ref: "#/parameters/x-email"
- - $ref: "#/parameters/path-companySFID"
+ - $ref: "#/parameters/path-companyID"
- $ref: "#/parameters/path-projectSFID"
- $ref: "#/parameters/searchTerm"
responses:
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 30953b775..48fb2fddf 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -150,22 +150,31 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
"functionName": "company.handlers.CompanyGetCompanyProjectContributorsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
- "companySFID": params.CompanySFID,
+ "companyID": params.CompanyID,
+ }
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v1CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil || v1CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
// PM - check if authorized by project scope - allow if PM has project ID scope that matches
// Contact,Community Program Manager,CLA Manager,CLA Manager Designee,Company Admin - check if authorized by organization scope - allow if {Contact,Community Program Manager,CLA Manager,CLA Manager Designee,Company Admin} has organization ID scope that matches
// CLA Manager - check if authorized by project|organization scope - allow if CLA Manager (for example) has project ID + org DI scope that matches
log.WithFields(f).Debug("checking permissions")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, params.CompanySFID, projectClaGroupRepo) {
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, projectClaGroupRepo) {
return company.NewGetCompanyProjectContributorsForbidden().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseForbidden(
reqID,
fmt.Sprintf("user %s does not have access to get contributors with Project scope of %s or Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.ProjectSFID, params.CompanySFID)))
+ authUser.UserName, params.ProjectSFID, params.ProjectSFID, params.CompanyID)))
}
- result, err := service.GetCompanyProjectContributors(ctx, params.ProjectSFID, params.CompanySFID, utils.StringValue(params.SearchTerm))
+ result, err := service.GetCompanyProjectContributors(ctx, params.ProjectSFID, params.CompanyID, utils.StringValue(params.SearchTerm))
if err != nil {
if err == v1Company.ErrCompanyDoesNotExist {
return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index a3f2d9e8a..5a7f09f6f 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -323,16 +323,16 @@ func (s *service) GetCompanyProjectActiveCLAs(ctx context.Context, companyID str
return &out, nil
}
-func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID string, companySFID string, searchTerm string) (*models.CorporateContributorList, error) {
+func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID string, companyID string, searchTerm string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
"functionName": "GetCompanyProjectContributors",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
- "companySFID": companySFID,
+ "companyID": companyID,
"searchTerm": searchTerm,
}
list := make([]*models.CorporateContributor, 0)
- sigs, err := s.getAllCompanyProjectEmployeeSignatures(ctx, companySFID, projectSFID)
+ sigs, err := s.getAllCompanyProjectEmployeeSignatures(ctx, companyID, projectSFID)
if err != nil {
log.WithFields(f).Warnf("problem fetching all company project employee signatures, error: %+v", err)
return nil, err
@@ -1314,19 +1314,18 @@ func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepos
result <- &contributor
}
-func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, companySFID string, projectSFID string) ([]*v1Models.Signature, error) {
+func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, companyID string, projectSFID string) ([]*v1Models.Signature, error) {
f := logrus.Fields{
"functionName": "company.service.getAllCompanyProjectEmployeeSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "companySFID": companySFID,
+ "companyID": companyID,
"projectSFID": projectSFID,
}
log.WithFields(f).Debug("getAllCompanyProjectEmployeeSignatures")
- comp, claGroup, err := s.getCompanyAndClaGroup(ctx, companySFID, projectSFID)
+ _, claGroup, err := s.getCompanyAndClaGroup(ctx, companyID, projectSFID)
if err != nil {
return nil, err
}
- companyID := comp.CompanyID
params := v1SignatureParams.GetProjectCompanyEmployeeSignaturesParams{
HTTPRequest: nil,
CompanyID: companyID,
@@ -1340,11 +1339,11 @@ func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, co
}
// get company and project in parallel
-func (s *service) getCompanyAndClaGroup(ctx context.Context, companySFID, projectSFID string) (*v1Models.Company, *v1Models.ClaGroup, error) {
+func (s *service) getCompanyAndClaGroup(ctx context.Context, companyID, projectSFID string) (*v1Models.Company, *v1Models.ClaGroup, error) {
f := logrus.Fields{
"functionName": "company.service.getCompanyAndClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "companySFID": companySFID,
+ "companyID": companyID,
"projectSFID": projectSFID,
}
var comp *v1Models.Company
@@ -1355,7 +1354,7 @@ func (s *service) getCompanyAndClaGroup(ctx context.Context, companySFID, projec
cp.Add(2)
go func() {
defer cp.Done()
- comp, companyErr = s.companyRepo.GetCompanyByExternalID(ctx, companySFID)
+ comp, companyErr = s.companyRepo.GetCompany(ctx, companyID)
}()
go func() {
defer cp.Done()
From 0d4adcab2d7224b7bb0d6c1fdd9ced541f74817c Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Thu, 28 Jan 2021 18:14:07 +0200
Subject: [PATCH 0019/1276] createClaManagerDesignee companySFID -> companyID
(#2540)
Signed-off-by: makkalot
---
cla-backend-go/v2/cla_manager/handlers.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 22 +++++++++++-----------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 7d7d7c60a..696e308d0 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -330,7 +330,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
return cla_manager.NewCreateCLAManagerRequestForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- claManagerDesignee, err := service.CreateCLAManagerRequest(ctx, params.Body.ContactAdmin, v1CompanyModel.CompanyExternalID, params.ProjectSFID, params.Body.UserEmail.String(),
+ claManagerDesignee, err := service.CreateCLAManagerRequest(ctx, params.Body.ContactAdmin, v1CompanyModel.CompanyID, params.ProjectSFID, params.Body.UserEmail.String(),
*params.Body.FullName, authUser, LfxPortalURL)
if err != nil {
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 39467d46d..580bf3cb8 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -94,7 +94,7 @@ type Service interface {
DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse
InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, lFxPortalURL string) ([]*models.ClaManagerDesignee, error)
CreateCLAManagerDesignee(ctx context.Context, companyID string, projectID string, userEmail string) (*models.ClaManagerDesignee, error)
- CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companySFID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
+ CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, LfxPortalURL string) error
CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup) ([]*models.ClaManagerDesignee, string, error)
IsCLAManagerDesignee(ctx context.Context, companySFID, claGroupID, userLFID string) (*models.UserRoleStatus, error)
@@ -576,7 +576,7 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
if signedAtFoundationLevel {
if foundationSFID != "" {
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, foundationSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyID, foundationSFID, userEmail)
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
msg := fmt.Sprintf("Conflict assigning cla manager role for Foundation SFID: %s ", foundationSFID)
@@ -607,7 +607,7 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
go func(swg *sync.WaitGroup, pcg *projects_cla_groups.ProjectClaGroup, designeeChannel chan *result) {
defer swg.Done()
log.WithFields(f).Debugf("creating CLA Manager Designee for Project SFID: %s", pcg.ProjectSFID)
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyExternalID, pcg.ProjectSFID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, v1CompanyModel.CompanyID, pcg.ProjectSFID, userEmail)
var output result
if err != nil {
if err == ErrCLAManagerDesigneeConflict {
@@ -647,12 +647,12 @@ func (s *service) CreateCLAManagerDesigneeByGroup(ctx context.Context, params cl
}
// CreateCLAManagerRequest service method
-func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companySFID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error) {
+func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error) {
f := logrus.Fields{
"functionName": "cla_manager.service.CreateCLAManagerRequest",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"contactAdmin": contactAdmin,
- "companySFID": companySFID,
+ "companyID": companyID,
"projectID": projectID,
"userEmail": userEmail,
"fullName": fullName,
@@ -664,7 +664,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
log.WithFields(f).Debugf("loading company by external ID...")
// Search for salesForce Company aka external Company
- v1CompanyModel, companyErr := s.companyService.GetCompanyByExternalID(ctx, companySFID)
+ v1CompanyModel, companyErr := s.companyService.GetCompany(ctx, companyID)
if companyErr != nil {
msg := fmt.Sprintf("EasyCLA - 400 Bad Request - %s", companyErr)
log.Warn(msg)
@@ -703,17 +703,17 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
if contactAdmin {
log.WithFields(f).Debug("sending email to company Admin")
log.WithFields(f).Debug("querying user admin scopes...")
- scopes, listScopeErr := orgService.ListOrgUserAdminScopes(ctx, companySFID, nil)
+ scopes, listScopeErr := orgService.ListOrgUserAdminScopes(ctx, v1CompanyModel.CompanyExternalID, nil)
if listScopeErr != nil {
msg := fmt.Sprintf("EasyCLA - 400 Bad Request - Admin lookup error for organisation SFID: %s, error: %+v ",
- companySFID, listScopeErr)
+ v1CompanyModel.CompanyExternalID, listScopeErr)
log.WithFields(f).Warn(msg)
return nil, listScopeErr
}
if len(scopes.Userroles) == 0 {
msg := fmt.Sprintf("EasyCLA - 404 NotFound - No admins for organization SFID: %s",
- companySFID)
+ v1CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
return nil, ErrNoOrgAdmins
}
@@ -748,7 +748,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
msg := fmt.Sprintf("User: %s does not have an LF Login", userEmail)
log.WithFields(f).Warn(msg)
// Send email
- sendEmailErr := sendEmailToUserWithNoLFID(ctx, projectSF.Name, authUser.UserName, authUser.Email, fullName, userEmail, companySFID, &projectSF.ID, utils.CLADesigneeRole)
+ sendEmailErr := sendEmailToUserWithNoLFID(ctx, projectSF.Name, authUser.UserName, authUser.Email, fullName, userEmail, v1CompanyModel.CompanyExternalID, &projectSF.ID, utils.CLADesigneeRole)
if sendEmailErr != nil {
log.WithFields(f).Warnf("Error sending email: %+v", sendEmailErr)
return nil, sendEmailErr
@@ -761,7 +761,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
}
log.WithFields(f).Debug("sending CLA manager designee request...")
- claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, companySFID, projectID, userEmail)
+ claManagerDesignee, err := s.CreateCLAManagerDesignee(ctx, companyID, projectID, userEmail)
if err != nil {
// Check conflict for role scope
if _, ok := err.(*organizations.CreateOrgUsrRoleScopesConflict); ok {
From 9bc0f88e37167ccdfcba8b3b2be5975bd7d09fa0 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 28 Jan 2021 17:21:09 -0500
Subject: [PATCH 0020/1276] Added Get Company by ID/SFID + Added GitHub v4
GraphQL Lib (#2541)
- Added v2 Get Company by ID/SFID swagger/API endpoints
- Updated company not found error/404 logic
- Added 404 error to v2 endponts that were missing it
- Added initial release of GitHub v4 GraphQL API library and init logic
Signed-off-by: makkalot
Signed-off-by: David Deal
Co-authored-by: Denis Kyorov
---
.circleci/config.yml | 13 +
cla-backend-go/Makefile | 3 +-
.../cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 4 +-
cla-backend-go/company/repository.go | 37 +-
cla-backend-go/company/service.go | 4 +-
cla-backend-go/config/config.go | 22 +-
cla-backend-go/config/ssm.go | 22 +-
cla-backend-go/github/.graphqlconfig | 16 +
cla-backend-go/github/client.go | 12 +
cla-backend-go/github/github-query.graphql | 25 +
cla-backend-go/github/github-schema.graphql | 39977 ++++++++++++++++
cla-backend-go/github/protected_branch.go | 186 +
cla-backend-go/go.mod | 2 +
cla-backend-go/go.sum | 4 +
cla-backend-go/init/init.go | 2 +-
cla-backend-go/repositories/service.go | 2 +-
cla-backend-go/swagger/cla.v1.yaml | 50 +-
cla-backend-go/swagger/cla.v2.yaml | 73 +-
cla-backend-go/tests/github_v4_test.go | 45 +
cla-backend-go/users/handlers.go | 2 +-
cla-backend-go/utils/conversion.go | 5 +
cla-backend-go/utils/errors.go | 76 +-
cla-backend-go/v2/cla_manager/handlers.go | 3 +-
cla-backend-go/v2/company/handlers.go | 140 +-
cla-backend-go/v2/company/service.go | 55 +-
cla-backend-go/v2/dynamo_events/autoenable.go | 2 +-
.../v2/dynamo_events/github_organization.go | 44 +-
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/signatures/handlers.go | 12 +-
cla-backend-go/v2/signatures/service.go | 4 +-
31 files changed, 40719 insertions(+), 127 deletions(-)
create mode 100644 cla-backend-go/github/.graphqlconfig
create mode 100644 cla-backend-go/github/github-query.graphql
create mode 100644 cla-backend-go/github/github-schema.graphql
create mode 100644 cla-backend-go/tests/github_v4_test.go
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3e4d47925..b13471042 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -205,6 +205,7 @@ jobs:
- add_ssh_keys:
fingerprints:
- "e9:13:85:f1:b1:a1:25:bf:f5:44:34:66:82:1e:31:59"
+ - *setup_aws
- run: echo 'export NVM_DIR=${HOME}/.nvm' >> $BASH_ENV
- *install-node-12
- run: echo 'export GO111MODULE=on' >> $BASH_ENV
@@ -287,16 +288,28 @@ jobs:
<<: *buildGoBackendAnchor
environment:
STAGE: dev
+ AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_DEV
+ AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_DEV
+ AWS_PROFILE: lf-cla
+ AWS_REGION: us-east-1
buildGoBackendStaging:
<<: *buildGoBackendAnchor
environment:
STAGE: staging
+ AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_STAGING
+ AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_STAGING
+ AWS_PROFILE: lf-cla
+ AWS_REGION: us-east-1
buildGoBackendProd:
<<: *buildGoBackendAnchor
environment:
STAGE: prod
+ AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_PROD
+ AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_PROD
+ AWS_PROFILE: lf-cla
+ AWS_REGION: us-east-1
# Deploys
deployBackend: &deployBackendAnchor
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 10414aa88..25805ac3e 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -23,7 +23,6 @@ LINT_VERSION=v1.29.0
SWAGGER_TOOL_VERSION=v0.24.0
GO_PKGS=$(shell go list ./... | grep -v /vendor/ | grep -v /node_modules/)
GO_FILES=$(shell find . -type f -name '*.go' -not -path './vendor/*')
-TEST_ENV=AWS_REGION=us-east-1 DYNAMODB_AWS_REGION=us-east-1 AWS_PROFILE=bar AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar
.PHONY: generate setup tool-setup setup-dev setup-deploy clean-all clean swagger up fmt test run deps build build-mac build-aws-lambda user-subscribe-lambda qc lint
@@ -170,7 +169,7 @@ fmt:
test:
@echo "Running unit tests..."
- @ $(TEST_ENV) go test -v $(shell go list ./... | grep -v /vendor/ | grep -v /node_modules/) -coverprofile=cover.out
+ @go test -v $(shell go list ./... | grep -v /vendor/ | grep -v /node_modules/) -coverprofile=cover.out
mock:
@echo "Re-Generating mocks"
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index f9dba32de..2e9c30e74 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -91,7 +91,7 @@ func init() {
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
- github.Init(configFile.Github.AppID, configFile.Github.AppPrivateKey, configFile.Github.AccessToken)
+ github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
project_service.InitClient(configFile.APIGatewayURL)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 9242288c8..f62cba994 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -223,7 +223,7 @@ func server(localMode bool) http.Handler {
if err != nil {
logrus.Panic(err)
}
- github.Init(configFile.Github.AppID, configFile.Github.AppPrivateKey, configFile.Github.AccessToken)
+ github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
@@ -304,7 +304,7 @@ func server(localMode bool) http.Handler {
v2Health.Configure(v2API, healthService)
template.Configure(api, templateService, eventsService)
v2Template.Configure(v2API, templateService, eventsService)
- github.Configure(api, configFile.Github.ClientID, configFile.Github.ClientSecret, configFile.Github.AccessToken, sessionStore)
+ github.Configure(api, configFile.GitHub.ClientID, configFile.GitHub.ClientSecret, configFile.GitHub.AccessToken, sessionStore)
signatures.Configure(api, v1SignaturesService, sessionStore, eventsService)
v2Signatures.Configure(v2API, v1ProjectService, projectRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
approval_list.Configure(api, v1ApprovalListService, sessionStore, v1SignaturesService, eventsService)
diff --git a/cla-backend-go/company/repository.go b/cla-backend-go/company/repository.go
index 2708c608d..9dae3e5a7 100644
--- a/cla-backend-go/company/repository.go
+++ b/cla-backend-go/company/repository.go
@@ -5,7 +5,6 @@ package company
import (
"context"
- "errors"
"fmt"
"strings"
@@ -28,11 +27,6 @@ import (
"github.com/gofrs/uuid"
)
-// errors
-var (
- ErrCompanyDoesNotExist = errors.New("company does not exist")
-)
-
// IRepository interface methods
type IRepository interface { //nolint
CreateCompany(ctx context.Context, in *models.Company) (*models.Company, error)
@@ -176,8 +170,12 @@ func (repo repository) GetCompanyByExternalID(ctx context.Context, companySFID s
}
if len(companyRecords) == 0 {
log.WithFields(f).Debug("no records found")
- return nil, ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "no company records found for SFID",
+ CompanyID: companySFID,
+ }
}
+
log.WithFields(f).Debugf("loaded %d records", len(companyRecords))
// For debug when problems occur
f["companyName"] = companyRecords[0].CompanyName
@@ -195,7 +193,10 @@ func (repo repository) GetCompanyByExternalID(ctx context.Context, companySFID s
}
f["signingEntityNames"] = strings.Join(signingEntityNames, ";")
log.WithFields(f).Warning("unable to match company name with existing signing entity names")
- return nil, ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "company record not found - unable to match company name with existing signing entity names",
+ CompanyID: companySFID,
+ }
}
// GetCompaniesByExternalID returns a list of companies based on the company external ID. A company will have more than one if/when the SF record has multiple entity names - for which we create separate EasyCLA company records
@@ -232,9 +233,13 @@ func (repo repository) GetCompaniesByExternalID(ctx context.Context, companySFID
}
if len(results.Items) == 0 {
- log.WithFields(f).Debug("no records found")
- return nil, ErrCompanyDoesNotExist
+ log.WithFields(f).Debug("no company records found")
+ return nil, &utils.CompanyNotFound{
+ Message: "no company records found with matching external SFID",
+ CompanySFID: companySFID,
+ }
}
+
var dbCompanyModels []DBModel
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &dbCompanyModels)
if err != nil {
@@ -279,8 +284,13 @@ func (repo repository) GetCompanyBySigningEntityName(ctx context.Context, signin
}
if len(results.Items) == 0 {
- return nil, ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "no company with signing entity name found",
+ CompanySigningEntityName: signingEntityName,
+ Err: nil,
+ }
}
+
dbCompanyModel := DBModel{}
err = dynamodbattribute.UnmarshalMap(results.Items[0], &dbCompanyModel)
if err != nil {
@@ -366,7 +376,10 @@ func (repo repository) GetCompany(ctx context.Context, companyID string) (*model
}
if len(companyTableData.Item) == 0 {
- return nil, ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "no company matching company record",
+ CompanyID: companyID,
+ }
}
dbCompanyModel := DBModel{}
diff --git a/cla-backend-go/company/service.go b/cla-backend-go/company/service.go
index dabfaa1f9..7fc942d04 100644
--- a/cla-backend-go/company/service.go
+++ b/cla-backend-go/company/service.go
@@ -634,7 +634,7 @@ func (s service) GetCompanyByExternalID(ctx context.Context, companySFID string)
return comp, nil
}
- if err == ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
comp, err = s.CreateOrgFromExternalID(ctx, "", companySFID)
if err != nil {
return comp, err
@@ -675,7 +675,7 @@ func (s service) GetCompanyBySigningEntityName(ctx context.Context, signingEntit
return comp, nil
}
- if err == ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
log.WithFields(f).Debugf("Company with signing entity name %s does not exist", signingEntityName)
comp, err = s.CreateOrgFromExternalID(ctx, signingEntityName, companySFID)
if err != nil {
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index c562cdeed..537f7ab3f 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -38,8 +38,8 @@ type Config struct {
// AWS
AWS AWS `json:"aws"`
- // Github Application
- Github Github `json:"github"`
+ // GitHub Application
+ GitHub GitHub `json:"github"`
// Dynamo Session Store
SessionStoreTableName string `json:"sessionStoreTableName"`
@@ -110,13 +110,17 @@ type AWS struct {
Region string `json:"region"`
}
-// Github model
-type Github struct {
- ClientID string `json:"clientId"`
- ClientSecret string `json:"clientSecret"`
- AccessToken string `json:"accessToken"`
- AppID int `json:"app_id"`
- AppPrivateKey string `json:"app_private_key"`
+// GitHub model
+type GitHub struct {
+ ClientID string `json:"clientId"`
+ ClientSecret string `json:"clientSecret"`
+ AccessToken string `json:"accessToken"`
+ AppID int `json:"app_id"`
+ AppPrivateKey string `json:"app_private_key"`
+ TestOrganization string `json:"test_organization"`
+ TestOrganizationInstallationID string `json:"test_organization_installation_id"`
+ TestRepository string `json:"test_repository"`
+ TestRepositoryID string `json:"test_repository_id"`
}
// MetricsReport keeps the config needed to send the metrics data report
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 8138989e7..b6f0ba61c 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -65,6 +65,10 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-gh-access-token-%s", stage),
fmt.Sprintf("cla-gh-app-id-%s", stage),
fmt.Sprintf("cla-gh-app-private-key-%s", stage),
+ fmt.Sprintf("cla-gh-test-organization-%s", stage),
+ fmt.Sprintf("cla-gh-test-organization-installation-id-%s", stage),
+ fmt.Sprintf("cla-gh-test-repository-%s", stage),
+ fmt.Sprintf("cla-gh-test-repository-id-%s", stage),
fmt.Sprintf("cla-corporate-base-%s", stage),
fmt.Sprintf("cla-corporate-v2-base-%s", stage),
fmt.Sprintf("cla-doc-raptor-api-key-%s", stage),
@@ -118,20 +122,28 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
case fmt.Sprintf("cla-auth0-algorithm-%s", stage):
config.Auth0.Algorithm = resp.value
case fmt.Sprintf("cla-gh-oauth-client-id-go-backend-%s", stage):
- config.Github.ClientID = resp.value
+ config.GitHub.ClientID = resp.value
case fmt.Sprintf("cla-gh-oauth-secret-go-backend-%s", stage):
- config.Github.ClientSecret = resp.value
+ config.GitHub.ClientSecret = resp.value
case fmt.Sprintf("cla-gh-access-token-%s", stage):
- config.Github.AccessToken = resp.value
+ config.GitHub.AccessToken = resp.value
case fmt.Sprintf("cla-gh-app-id-%s", stage):
githubAppID, err := strconv.Atoi(resp.value)
if err != nil {
errMsg := fmt.Sprintf("invalid value of key: %s", fmt.Sprintf("cla-gh-app-id-%s", stage))
log.WithFields(f).WithError(err).Fatal(errMsg)
}
- config.Github.AppID = githubAppID
+ config.GitHub.AppID = githubAppID
case fmt.Sprintf("cla-gh-app-private-key-%s", stage):
- config.Github.AppPrivateKey = resp.value
+ config.GitHub.AppPrivateKey = resp.value
+ case fmt.Sprintf("cla-gh-test-organization-%s", stage):
+ config.GitHub.TestOrganization = resp.value
+ case fmt.Sprintf("cla-gh-test-organization-installation-id-%s", stage):
+ config.GitHub.TestOrganizationInstallationID = resp.value
+ case fmt.Sprintf("cla-gh-test-repository-%s", stage):
+ config.GitHub.TestRepository = resp.value
+ case fmt.Sprintf("cla-gh-test-repository-id-%s", stage):
+ config.GitHub.TestRepositoryID = resp.value
case fmt.Sprintf("cla-corporate-base-%s", stage):
corporateConsoleURLValue := resp.value
diff --git a/cla-backend-go/github/.graphqlconfig b/cla-backend-go/github/.graphqlconfig
new file mode 100644
index 000000000..fc565542a
--- /dev/null
+++ b/cla-backend-go/github/.graphqlconfig
@@ -0,0 +1,16 @@
+{
+ "name": "GitHub API V4 GraphQL Schema",
+ "schemaPath": "github-schema.graphql",
+ "extensions": {
+ "endpoints": {
+ "GitHub API V4 GraphQL Endpoint": {
+ "url": "https://api.github.com/graphql",
+ "headers": {
+ "Authorization": "Bearer ${env:GITHUB-ACCESS-TOKEN}",
+ "user-agent": "JS GraphQL"
+ },
+ "introspect": false
+ }
+ }
+ }
+}
diff --git a/cla-backend-go/github/client.go b/cla-backend-go/github/client.go
index e6efd8591..7f526b7a7 100644
--- a/cla-backend-go/github/client.go
+++ b/cla-backend-go/github/client.go
@@ -8,6 +8,9 @@ import (
"errors"
"fmt"
"net/http"
+ "time"
+
+ "github.com/shurcooL/githubv4"
"github.com/bradleyfalzon/ghinstallation"
"github.com/google/go-github/v33/github"
@@ -77,6 +80,15 @@ func NewGithubAppClient(installationID int64) (*github.Client, error) {
return github.NewClient(&http.Client{Transport: itr}), nil
}
+// NewGithubV4AppClient creates a new github v4 client from the supplied installationID
+func NewGithubV4AppClient(installationID int64) (*githubv4.Client, error) {
+ authTransport, err := ghinstallation.New(http.DefaultTransport, int64(getGithubAppID()), installationID, []byte(getGithubAppPrivateKey()))
+ if err != nil {
+ return nil, err
+ }
+ return githubv4.NewClient(&http.Client{Transport: authTransport, Timeout: 5 * time.Second}), nil
+}
+
// NewGithubOauthClient creates github client from global accessToken
func NewGithubOauthClient() *github.Client {
return NewGithubOauthClientWithAccessToken(getSecretAccessToken())
diff --git a/cla-backend-go/github/github-query.graphql b/cla-backend-go/github/github-query.graphql
new file mode 100644
index 000000000..4cf4ccb40
--- /dev/null
+++ b/cla-backend-go/github/github-query.graphql
@@ -0,0 +1,25 @@
+query BranchProtectionRule($organizationOwner: String!, $repositoryName: String!) {
+ repository(owner: $organizationOwner, name: $repositoryName) {
+ id
+ createdAt
+ branchProtectionRules(first: 100) {
+ totalCount
+ nodes {
+ pattern
+ id
+ allowsDeletions
+ requiredApprovingReviewCount
+ requiredStatusCheckContexts
+ }
+ edges {
+ node {
+ allowsDeletions
+ id
+ pattern
+ }
+ }
+ }
+ diskUsage
+ hasIssuesEnabled
+ }
+}
diff --git a/cla-backend-go/github/github-schema.graphql b/cla-backend-go/github/github-schema.graphql
new file mode 100644
index 000000000..3ae15a1a7
--- /dev/null
+++ b/cla-backend-go/github/github-schema.graphql
@@ -0,0 +1,39977 @@
+"""
+Defines what type of global IDs are accepted for a mutation argument of type ID.
+"""
+directive @possibleTypes(
+ """
+ Abstract type of accepted global ID
+ """
+ abstractType: String
+
+ """
+ Accepted types of global IDs.
+ """
+ concreteTypes: [String!]!
+) on INPUT_FIELD_DEFINITION
+
+"""
+Marks an element of a GraphQL schema as only available via a preview header
+"""
+directive @preview(
+ """
+ The identifier of the API preview that toggles this field.
+ """
+ toggledBy: String!
+) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+"""
+Autogenerated input type of AcceptEnterpriseAdministratorInvitation
+"""
+input AcceptEnterpriseAdministratorInvitationInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the invitation being accepted
+ """
+ invitationId: ID! @possibleTypes(concreteTypes: ["EnterpriseAdministratorInvitation"])
+}
+
+"""
+Autogenerated return type of AcceptEnterpriseAdministratorInvitation
+"""
+type AcceptEnterpriseAdministratorInvitationPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The invitation that was accepted.
+ """
+ invitation: EnterpriseAdministratorInvitation
+
+ """
+ A message confirming the result of accepting an administrator invitation.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of AcceptTopicSuggestion
+"""
+input AcceptTopicSuggestionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of the suggested topic.
+ """
+ name: String!
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of AcceptTopicSuggestion
+"""
+type AcceptTopicSuggestionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The accepted topic.
+ """
+ topic: Topic
+}
+
+"""
+Represents an object which can take actions on GitHub. Typically a User or Bot.
+"""
+interface Actor {
+ """
+ A URL pointing to the actor's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ The username of the actor.
+ """
+ login: String!
+
+ """
+ The HTTP path for this actor.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this actor.
+ """
+ url: URI!
+}
+
+"""
+Location information for an actor
+"""
+type ActorLocation {
+ """
+ City
+ """
+ city: String
+
+ """
+ Country name
+ """
+ country: String
+
+ """
+ Country code
+ """
+ countryCode: String
+
+ """
+ Region name
+ """
+ region: String
+
+ """
+ Region or state code
+ """
+ regionCode: String
+}
+
+"""
+Autogenerated input type of AddAssigneesToAssignable
+"""
+input AddAssigneesToAssignableInput {
+ """
+ The id of the assignable object to add assignees to.
+ """
+ assignableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Assignable")
+
+ """
+ The id of users to add as assignees.
+ """
+ assigneeIds: [ID!]! @possibleTypes(concreteTypes: ["User"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated return type of AddAssigneesToAssignable
+"""
+type AddAssigneesToAssignablePayload {
+ """
+ The item that was assigned.
+ """
+ assignable: Assignable
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of AddComment
+"""
+input AddCommentInput {
+ """
+ The contents of the comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the subject to modify.
+ """
+ subjectId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "IssueOrPullRequest")
+}
+
+"""
+Autogenerated return type of AddComment
+"""
+type AddCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The edge from the subject's comment connection.
+ """
+ commentEdge: IssueCommentEdge
+
+ """
+ The subject
+ """
+ subject: Node
+
+ """
+ The edge from the subject's timeline connection.
+ """
+ timelineEdge: IssueTimelineItemEdge
+}
+
+"""
+Autogenerated input type of AddEnterpriseSupportEntitlement
+"""
+input AddEnterpriseSupportEntitlementInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Enterprise which the admin belongs to.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of a member who will receive the support entitlement.
+ """
+ login: String!
+}
+
+"""
+Autogenerated return type of AddEnterpriseSupportEntitlement
+"""
+type AddEnterpriseSupportEntitlementPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A message confirming the result of adding the support entitlement.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of AddLabelsToLabelable
+"""
+input AddLabelsToLabelableInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ids of the labels to add.
+ """
+ labelIds: [ID!]! @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ The id of the labelable object to add labels to.
+ """
+ labelableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Labelable")
+}
+
+"""
+Autogenerated return type of AddLabelsToLabelable
+"""
+type AddLabelsToLabelablePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The item that was labeled.
+ """
+ labelable: Labelable
+}
+
+"""
+Autogenerated input type of AddProjectCard
+"""
+input AddProjectCardInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The content of the card. Must be a member of the ProjectCardItem union
+ """
+ contentId: ID @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "ProjectCardItem")
+
+ """
+ The note on the card.
+ """
+ note: String
+
+ """
+ The Node ID of the ProjectColumn.
+ """
+ projectColumnId: ID! @possibleTypes(concreteTypes: ["ProjectColumn"])
+}
+
+"""
+Autogenerated return type of AddProjectCard
+"""
+type AddProjectCardPayload {
+ """
+ The edge from the ProjectColumn's card connection.
+ """
+ cardEdge: ProjectCardEdge
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ProjectColumn
+ """
+ projectColumn: ProjectColumn
+}
+
+"""
+Autogenerated input type of AddProjectColumn
+"""
+input AddProjectColumnInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of the column.
+ """
+ name: String!
+
+ """
+ The Node ID of the project.
+ """
+ projectId: ID! @possibleTypes(concreteTypes: ["Project"])
+}
+
+"""
+Autogenerated return type of AddProjectColumn
+"""
+type AddProjectColumnPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The edge from the project's column connection.
+ """
+ columnEdge: ProjectColumnEdge
+
+ """
+ The project
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of AddPullRequestReviewComment
+"""
+input AddPullRequestReviewCommentInput {
+ """
+ The text of the comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The SHA of the commit to comment on.
+ """
+ commitOID: GitObjectID
+
+ """
+ The comment id to reply to.
+ """
+ inReplyTo: ID @possibleTypes(concreteTypes: ["PullRequestReviewComment"])
+
+ """
+ The relative path of the file to comment on.
+ """
+ path: String
+
+ """
+ The line index in the diff to comment on.
+ """
+ position: Int
+
+ """
+ The node ID of the pull request reviewing
+ """
+ pullRequestId: ID @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The Node ID of the review to modify.
+ """
+ pullRequestReviewId: ID @possibleTypes(concreteTypes: ["PullRequestReview"])
+}
+
+"""
+Autogenerated return type of AddPullRequestReviewComment
+"""
+type AddPullRequestReviewCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The newly created comment.
+ """
+ comment: PullRequestReviewComment
+
+ """
+ The edge from the review's comment connection.
+ """
+ commentEdge: PullRequestReviewCommentEdge
+}
+
+"""
+Autogenerated input type of AddPullRequestReview
+"""
+input AddPullRequestReviewInput {
+ """
+ The contents of the review body comment.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The review line comments.
+ """
+ comments: [DraftPullRequestReviewComment]
+
+ """
+ The commit OID the review pertains to.
+ """
+ commitOID: GitObjectID
+
+ """
+ The event to perform on the pull request review.
+ """
+ event: PullRequestReviewEvent
+
+ """
+ The Node ID of the pull request to modify.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The review line comment threads.
+ """
+ threads: [DraftPullRequestReviewThread]
+}
+
+"""
+Autogenerated return type of AddPullRequestReview
+"""
+type AddPullRequestReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The newly created pull request review.
+ """
+ pullRequestReview: PullRequestReview
+
+ """
+ The edge from the pull request's review connection.
+ """
+ reviewEdge: PullRequestReviewEdge
+}
+
+"""
+Autogenerated input type of AddPullRequestReviewThread
+"""
+input AddPullRequestReviewThreadInput {
+ """
+ Body of the thread's first comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The line of the blob to which the thread refers. The end of the line range for multi-line comments.
+ """
+ line: Int!
+
+ """
+ Path to the file being commented on.
+ """
+ path: String!
+
+ """
+ The node ID of the pull request reviewing
+ """
+ pullRequestId: ID @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The Node ID of the review to modify.
+ """
+ pullRequestReviewId: ID @possibleTypes(concreteTypes: ["PullRequestReview"])
+
+ """
+ The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.
+ """
+ side: DiffSide = RIGHT
+
+ """
+ The first line of the range to which the comment refers.
+ """
+ startLine: Int
+
+ """
+ The side of the diff on which the start line resides.
+ """
+ startSide: DiffSide = RIGHT
+}
+
+"""
+Autogenerated return type of AddPullRequestReviewThread
+"""
+type AddPullRequestReviewThreadPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The newly created thread.
+ """
+ thread: PullRequestReviewThread
+}
+
+"""
+Autogenerated input type of AddReaction
+"""
+input AddReactionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of the emoji to react with.
+ """
+ content: ReactionContent!
+
+ """
+ The Node ID of the subject to modify.
+ """
+ subjectId: ID! @possibleTypes(concreteTypes: ["CommitComment", "Issue", "IssueComment", "PullRequest", "PullRequestReview", "PullRequestReviewComment", "TeamDiscussion", "TeamDiscussionComment"], abstractType: "Reactable")
+}
+
+"""
+Autogenerated return type of AddReaction
+"""
+type AddReactionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The reaction object.
+ """
+ reaction: Reaction
+
+ """
+ The reactable subject.
+ """
+ subject: Reactable
+}
+
+"""
+Autogenerated input type of AddStar
+"""
+input AddStarInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Starrable ID to star.
+ """
+ starrableId: ID! @possibleTypes(concreteTypes: ["Gist", "Repository", "Topic"], abstractType: "Starrable")
+}
+
+"""
+Autogenerated return type of AddStar
+"""
+type AddStarPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The starrable.
+ """
+ starrable: Starrable
+}
+
+"""
+Autogenerated input type of AddVerifiableDomain
+"""
+input AddVerifiableDomainInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The URL of the domain
+ """
+ domain: URI!
+
+ """
+ The ID of the owner to add the domain to
+ """
+ ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "VerifiableDomainOwner")
+}
+
+"""
+Autogenerated return type of AddVerifiableDomain
+"""
+type AddVerifiableDomainPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The verifiable domain that was added.
+ """
+ domain: VerifiableDomain
+}
+
+"""
+Represents a 'added_to_project' event on a given issue or pull request.
+"""
+type AddedToProjectEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Project referenced by event.
+ """
+ project: Project @preview(toggledBy: "starfox-preview")
+
+ """
+ Project card referenced by this project event.
+ """
+ projectCard: ProjectCard @preview(toggledBy: "starfox-preview")
+
+ """
+ Column name referenced by this project event.
+ """
+ projectColumnName: String! @preview(toggledBy: "starfox-preview")
+}
+
+"""
+A GitHub App.
+"""
+type App implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The description of the app.
+ """
+ description: String
+ id: ID!
+
+ """
+ The hex color code, without the leading '#', for the logo background.
+ """
+ logoBackgroundColor: String!
+
+ """
+ A URL pointing to the app's logo.
+ """
+ logoUrl(
+ """
+ The size of the resulting image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ The name of the app.
+ """
+ name: String!
+
+ """
+ A slug based on the name of the app for use in URLs.
+ """
+ slug: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The URL to the app's homepage.
+ """
+ url: URI!
+}
+
+"""
+Autogenerated input type of ArchiveRepository
+"""
+input ArchiveRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the repository to mark as archived.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of ArchiveRepository
+"""
+type ArchiveRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The repository that was marked as archived.
+ """
+ repository: Repository
+}
+
+"""
+An object that can have users assigned to it.
+"""
+interface Assignable {
+ """
+ A list of Users assigned to this object.
+ """
+ assignees(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+}
+
+"""
+Represents an 'assigned' event on any assignable object.
+"""
+type AssignedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the assignable associated with the event.
+ """
+ assignable: Assignable!
+
+ """
+ Identifies the user or mannequin that was assigned.
+ """
+ assignee: Assignee
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the user who was assigned.
+ """
+ user: User @deprecated(reason: "Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC.")
+}
+
+"""
+Types that can be assigned to issues.
+"""
+union Assignee = Bot | Mannequin | Organization | User
+
+"""
+An entry in the audit log.
+"""
+interface AuditEntry {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Types that can initiate an audit log event.
+"""
+union AuditEntryActor = Bot | Organization | User
+
+"""
+Ordering options for Audit Log connections.
+"""
+input AuditLogOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection
+
+ """
+ The field to order Audit Logs by.
+ """
+ field: AuditLogOrderField
+}
+
+"""
+Properties by which Audit Log connections can be ordered.
+"""
+enum AuditLogOrderField {
+ """
+ Order audit log entries by timestamp
+ """
+ CREATED_AT
+}
+
+"""
+Represents a 'auto_merge_disabled' event on a given pull request.
+"""
+type AutoMergeDisabledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The user who disabled auto-merge for this Pull Request
+ """
+ disabler: User
+ id: ID!
+
+ """
+ PullRequest referenced by event
+ """
+ pullRequest: PullRequest
+
+ """
+ The reason auto-merge was disabled
+ """
+ reason: String
+
+ """
+ The reason_code relating to why auto-merge was disabled
+ """
+ reasonCode: String
+}
+
+"""
+Represents a 'auto_merge_enabled' event on a given pull request.
+"""
+type AutoMergeEnabledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The user who enabled auto-merge for this Pull Request
+ """
+ enabler: User
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'auto_rebase_enabled' event on a given pull request.
+"""
+type AutoRebaseEnabledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The user who enabled auto-merge (rebase) for this Pull Request
+ """
+ enabler: User
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'auto_squash_enabled' event on a given pull request.
+"""
+type AutoSquashEnabledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The user who enabled auto-merge (squash) for this Pull Request
+ """
+ enabler: User
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'automatic_base_change_failed' event on a given pull request.
+"""
+type AutomaticBaseChangeFailedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The new base for this PR
+ """
+ newBase: String!
+
+ """
+ The old base for this PR
+ """
+ oldBase: String!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Represents a 'automatic_base_change_succeeded' event on a given pull request.
+"""
+type AutomaticBaseChangeSucceededEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The new base for this PR
+ """
+ newBase: String!
+
+ """
+ The old base for this PR
+ """
+ oldBase: String!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Represents a 'base_ref_changed' event on a given issue or pull request.
+"""
+type BaseRefChangedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the name of the base ref for the pull request after it was changed.
+ """
+ currentRefName: String!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Identifies the name of the base ref for the pull request before it was changed.
+ """
+ previousRefName: String!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Represents a 'base_ref_deleted' event on a given pull request.
+"""
+type BaseRefDeletedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the name of the Ref associated with the `base_ref_deleted` event.
+ """
+ baseRefName: String
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'base_ref_force_pushed' event on a given pull request.
+"""
+type BaseRefForcePushedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the after commit SHA for the 'base_ref_force_pushed' event.
+ """
+ afterCommit: Commit
+
+ """
+ Identifies the before commit SHA for the 'base_ref_force_pushed' event.
+ """
+ beforeCommit: Commit
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the fully qualified ref name for the 'base_ref_force_pushed' event.
+ """
+ ref: Ref
+}
+
+"""
+Represents a Git blame.
+"""
+type Blame {
+ """
+ The list of ranges from a Git blame.
+ """
+ ranges: [BlameRange!]!
+}
+
+"""
+Represents a range of information from a Git blame.
+"""
+type BlameRange {
+ """
+ Identifies the recency of the change, from 1 (new) to 10 (old). This is
+ calculated as a 2-quantile and determines the length of distance between the
+ median age of all the changes in the file and the recency of the current
+ range's change.
+ """
+ age: Int!
+
+ """
+ Identifies the line author
+ """
+ commit: Commit!
+
+ """
+ The ending line for the range
+ """
+ endingLine: Int!
+
+ """
+ The starting line for the range
+ """
+ startingLine: Int!
+}
+
+"""
+Represents a Git blob.
+"""
+type Blob implements GitObject & Node {
+ """
+ An abbreviated version of the Git object ID
+ """
+ abbreviatedOid: String!
+
+ """
+ Byte size of Blob object
+ """
+ byteSize: Int!
+
+ """
+ The HTTP path for this Git object
+ """
+ commitResourcePath: URI!
+
+ """
+ The HTTP URL for this Git object
+ """
+ commitUrl: URI!
+ id: ID!
+
+ """
+ Indicates whether the Blob is binary or text. Returns null if unable to determine the encoding.
+ """
+ isBinary: Boolean
+
+ """
+ Indicates whether the contents is truncated
+ """
+ isTruncated: Boolean!
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID!
+
+ """
+ The Repository the Git object belongs to
+ """
+ repository: Repository!
+
+ """
+ UTF8 text data or null if the Blob is binary
+ """
+ text: String
+}
+
+"""
+A special type of user which takes actions on behalf of GitHub Apps.
+"""
+type Bot implements Actor & Node & UniformResourceLocatable {
+ """
+ A URL pointing to the GitHub App's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The username of the actor.
+ """
+ login: String!
+
+ """
+ The HTTP path for this bot
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this bot
+ """
+ url: URI!
+}
+
+"""
+A branch protection rule.
+"""
+type BranchProtectionRule implements Node {
+ """
+ Can this branch be deleted.
+ """
+ allowsDeletions: Boolean!
+
+ """
+ Are force pushes allowed on this branch.
+ """
+ allowsForcePushes: Boolean!
+
+ """
+ A list of conflicts matching branches protection rule and other branch protection rules
+ """
+ branchProtectionRuleConflicts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): BranchProtectionRuleConflictConnection!
+
+ """
+ The actor who created this branch protection rule.
+ """
+ creator: Actor
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ Will new commits pushed to matching branches dismiss pull request review approvals.
+ """
+ dismissesStaleReviews: Boolean!
+ id: ID!
+
+ """
+ Can admins overwrite branch protection.
+ """
+ isAdminEnforced: Boolean!
+
+ """
+ Repository refs that are protected by this rule
+ """
+ matchingRefs(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filters refs with query on name
+ """
+ query: String
+ ): RefConnection!
+
+ """
+ Identifies the protection rule pattern.
+ """
+ pattern: String!
+
+ """
+ A list push allowances for this branch protection rule.
+ """
+ pushAllowances(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PushAllowanceConnection!
+
+ """
+ The repository associated with this branch protection rule.
+ """
+ repository: Repository
+
+ """
+ Number of approving reviews required to update matching branches.
+ """
+ requiredApprovingReviewCount: Int
+
+ """
+ List of required status check contexts that must pass for commits to be accepted to matching branches.
+ """
+ requiredStatusCheckContexts: [String]
+
+ """
+ Are approving reviews required to update matching branches.
+ """
+ requiresApprovingReviews: Boolean!
+
+ """
+ Are reviews from code owners required to update matching branches.
+ """
+ requiresCodeOwnerReviews: Boolean!
+
+ """
+ Are commits required to be signed.
+ """
+ requiresCommitSignatures: Boolean!
+
+ """
+ Are merge commits prohibited from being pushed to this branch.
+ """
+ requiresLinearHistory: Boolean!
+
+ """
+ Are status checks required to update matching branches.
+ """
+ requiresStatusChecks: Boolean!
+
+ """
+ Are branches required to be up to date before merging.
+ """
+ requiresStrictStatusChecks: Boolean!
+
+ """
+ Is pushing to matching branches restricted.
+ """
+ restrictsPushes: Boolean!
+
+ """
+ Is dismissal of pull request reviews restricted.
+ """
+ restrictsReviewDismissals: Boolean!
+
+ """
+ A list review dismissal allowances for this branch protection rule.
+ """
+ reviewDismissalAllowances(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ReviewDismissalAllowanceConnection!
+}
+
+"""
+A conflict between two branch protection rules.
+"""
+type BranchProtectionRuleConflict {
+ """
+ Identifies the branch protection rule.
+ """
+ branchProtectionRule: BranchProtectionRule
+
+ """
+ Identifies the conflicting branch protection rule.
+ """
+ conflictingBranchProtectionRule: BranchProtectionRule
+
+ """
+ Identifies the branch ref that has conflicting rules
+ """
+ ref: Ref
+}
+
+"""
+The connection type for BranchProtectionRuleConflict.
+"""
+type BranchProtectionRuleConflictConnection {
+ """
+ A list of edges.
+ """
+ edges: [BranchProtectionRuleConflictEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [BranchProtectionRuleConflict]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type BranchProtectionRuleConflictEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: BranchProtectionRuleConflict
+}
+
+"""
+The connection type for BranchProtectionRule.
+"""
+type BranchProtectionRuleConnection {
+ """
+ A list of edges.
+ """
+ edges: [BranchProtectionRuleEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [BranchProtectionRule]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type BranchProtectionRuleEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: BranchProtectionRule
+}
+
+"""
+Autogenerated input type of CancelEnterpriseAdminInvitation
+"""
+input CancelEnterpriseAdminInvitationInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the pending enterprise administrator invitation.
+ """
+ invitationId: ID! @possibleTypes(concreteTypes: ["EnterpriseAdministratorInvitation"])
+}
+
+"""
+Autogenerated return type of CancelEnterpriseAdminInvitation
+"""
+type CancelEnterpriseAdminInvitationPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The invitation that was canceled.
+ """
+ invitation: EnterpriseAdministratorInvitation
+
+ """
+ A message confirming the result of canceling an administrator invitation.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of ChangeUserStatus
+"""
+input ChangeUserStatusInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:.
+ """
+ emoji: String
+
+ """
+ If set, the user status will not be shown after this date.
+ """
+ expiresAt: DateTime
+
+ """
+ Whether this status should indicate you are not fully available on GitHub, e.g., you are away.
+ """
+ limitedAvailability: Boolean = false
+
+ """
+ A short description of your current status.
+ """
+ message: String
+
+ """
+ The ID of the organization whose members will be allowed to see the status. If
+ omitted, the status will be publicly visible.
+ """
+ organizationId: ID @possibleTypes(concreteTypes: ["Organization"])
+}
+
+"""
+Autogenerated return type of ChangeUserStatus
+"""
+type ChangeUserStatusPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Your updated status.
+ """
+ status: UserStatus
+}
+
+"""
+A single check annotation.
+"""
+type CheckAnnotation {
+ """
+ The annotation's severity level.
+ """
+ annotationLevel: CheckAnnotationLevel
+
+ """
+ The path to the file that this annotation was made on.
+ """
+ blobUrl: URI!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The position of this annotation.
+ """
+ location: CheckAnnotationSpan!
+
+ """
+ The annotation's message.
+ """
+ message: String!
+
+ """
+ The path that this annotation was made on.
+ """
+ path: String!
+
+ """
+ Additional information about the annotation.
+ """
+ rawDetails: String
+
+ """
+ The annotation's title
+ """
+ title: String
+}
+
+"""
+The connection type for CheckAnnotation.
+"""
+type CheckAnnotationConnection {
+ """
+ A list of edges.
+ """
+ edges: [CheckAnnotationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CheckAnnotation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Information from a check run analysis to specific lines of code.
+"""
+input CheckAnnotationData {
+ """
+ Represents an annotation's information level
+ """
+ annotationLevel: CheckAnnotationLevel!
+
+ """
+ The location of the annotation
+ """
+ location: CheckAnnotationRange!
+
+ """
+ A short description of the feedback for these lines of code.
+ """
+ message: String!
+
+ """
+ The path of the file to add an annotation to.
+ """
+ path: String!
+
+ """
+ Details about this annotation.
+ """
+ rawDetails: String
+
+ """
+ The title that represents the annotation.
+ """
+ title: String
+}
+
+"""
+An edge in a connection.
+"""
+type CheckAnnotationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CheckAnnotation
+}
+
+"""
+Represents an annotation's information level.
+"""
+enum CheckAnnotationLevel {
+ """
+ An annotation indicating an inescapable error.
+ """
+ FAILURE
+
+ """
+ An annotation indicating some information.
+ """
+ NOTICE
+
+ """
+ An annotation indicating an ignorable error.
+ """
+ WARNING
+}
+
+"""
+A character position in a check annotation.
+"""
+type CheckAnnotationPosition {
+ """
+ Column number (1 indexed).
+ """
+ column: Int
+
+ """
+ Line number (1 indexed).
+ """
+ line: Int!
+}
+
+"""
+Information from a check run analysis to specific lines of code.
+"""
+input CheckAnnotationRange {
+ """
+ The ending column of the range.
+ """
+ endColumn: Int
+
+ """
+ The ending line of the range.
+ """
+ endLine: Int!
+
+ """
+ The starting column of the range.
+ """
+ startColumn: Int
+
+ """
+ The starting line of the range.
+ """
+ startLine: Int!
+}
+
+"""
+An inclusive pair of positions for a check annotation.
+"""
+type CheckAnnotationSpan {
+ """
+ End position (inclusive).
+ """
+ end: CheckAnnotationPosition!
+
+ """
+ Start position (inclusive).
+ """
+ start: CheckAnnotationPosition!
+}
+
+"""
+The possible states for a check suite or run conclusion.
+"""
+enum CheckConclusionState {
+ """
+ The check suite or run requires action.
+ """
+ ACTION_REQUIRED
+
+ """
+ The check suite or run has been cancelled.
+ """
+ CANCELLED
+
+ """
+ The check suite or run has failed.
+ """
+ FAILURE
+
+ """
+ The check suite or run was neutral.
+ """
+ NEUTRAL
+
+ """
+ The check suite or run was skipped.
+ """
+ SKIPPED
+
+ """
+ The check suite or run was marked stale by GitHub. Only GitHub can use this conclusion.
+ """
+ STALE
+
+ """
+ The check suite or run has failed at startup.
+ """
+ STARTUP_FAILURE
+
+ """
+ The check suite or run has succeeded.
+ """
+ SUCCESS
+
+ """
+ The check suite or run has timed out.
+ """
+ TIMED_OUT
+}
+
+"""
+A check run.
+"""
+type CheckRun implements Node & UniformResourceLocatable {
+ """
+ The check run's annotations
+ """
+ annotations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CheckAnnotationConnection
+
+ """
+ The check suite that this run is a part of.
+ """
+ checkSuite: CheckSuite!
+
+ """
+ Identifies the date and time when the check run was completed.
+ """
+ completedAt: DateTime
+
+ """
+ The conclusion of the check run.
+ """
+ conclusion: CheckConclusionState
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The URL from which to find full details of the check run on the integrator's site.
+ """
+ detailsUrl: URI
+
+ """
+ A reference for the check run on the integrator's system.
+ """
+ externalId: String
+ id: ID!
+
+ """
+ The name of the check for this check run.
+ """
+ name: String!
+
+ """
+ The permalink to the check run summary.
+ """
+ permalink: URI!
+
+ """
+ The repository associated with this check run.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this check run.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the check run was started.
+ """
+ startedAt: DateTime
+
+ """
+ The current status of the check run.
+ """
+ status: CheckStatusState!
+
+ """
+ A string representing the check run's summary
+ """
+ summary: String
+
+ """
+ A string representing the check run's text
+ """
+ text: String
+
+ """
+ A string representing the check run
+ """
+ title: String
+
+ """
+ The HTTP URL for this check run.
+ """
+ url: URI!
+}
+
+"""
+Possible further actions the integrator can perform.
+"""
+input CheckRunAction {
+ """
+ A short explanation of what this action would do.
+ """
+ description: String!
+
+ """
+ A reference for the action on the integrator's system.
+ """
+ identifier: String!
+
+ """
+ The text to be displayed on a button in the web UI.
+ """
+ label: String!
+}
+
+"""
+The connection type for CheckRun.
+"""
+type CheckRunConnection {
+ """
+ A list of edges.
+ """
+ edges: [CheckRunEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CheckRun]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CheckRunEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CheckRun
+}
+
+"""
+The filters that are available when fetching check runs.
+"""
+input CheckRunFilter {
+ """
+ Filters the check runs created by this application ID.
+ """
+ appId: Int
+
+ """
+ Filters the check runs by this name.
+ """
+ checkName: String
+
+ """
+ Filters the check runs by this type.
+ """
+ checkType: CheckRunType
+
+ """
+ Filters the check runs by this status.
+ """
+ status: CheckStatusState
+}
+
+"""
+Descriptive details about the check run.
+"""
+input CheckRunOutput {
+ """
+ The annotations that are made as part of the check run.
+ """
+ annotations: [CheckAnnotationData!]
+
+ """
+ Images attached to the check run output displayed in the GitHub pull request UI.
+ """
+ images: [CheckRunOutputImage!]
+
+ """
+ The summary of the check run (supports Commonmark).
+ """
+ summary: String!
+
+ """
+ The details of the check run (supports Commonmark).
+ """
+ text: String
+
+ """
+ A title to provide for this check run.
+ """
+ title: String!
+}
+
+"""
+Images attached to the check run output displayed in the GitHub pull request UI.
+"""
+input CheckRunOutputImage {
+ """
+ The alternative text for the image.
+ """
+ alt: String!
+
+ """
+ A short image description.
+ """
+ caption: String
+
+ """
+ The full URL of the image.
+ """
+ imageUrl: URI!
+}
+
+"""
+The possible types of check runs.
+"""
+enum CheckRunType {
+ """
+ Every check run available.
+ """
+ ALL
+
+ """
+ The latest check run.
+ """
+ LATEST
+}
+
+"""
+The possible states for a check suite or run status.
+"""
+enum CheckStatusState {
+ """
+ The check suite or run has been completed.
+ """
+ COMPLETED
+
+ """
+ The check suite or run is in progress.
+ """
+ IN_PROGRESS
+
+ """
+ The check suite or run has been queued.
+ """
+ QUEUED
+
+ """
+ The check suite or run has been requested.
+ """
+ REQUESTED
+
+ """
+ The check suite or run is in waiting state.
+ """
+ WAITING
+}
+
+"""
+A check suite.
+"""
+type CheckSuite implements Node {
+ """
+ The GitHub App which created this check suite.
+ """
+ app: App
+
+ """
+ The name of the branch for this check suite.
+ """
+ branch: Ref
+
+ """
+ The check runs associated with a check suite.
+ """
+ checkRuns(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filters the check runs by this type.
+ """
+ filterBy: CheckRunFilter
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CheckRunConnection
+
+ """
+ The commit for this check suite
+ """
+ commit: Commit!
+
+ """
+ The conclusion of this check suite.
+ """
+ conclusion: CheckConclusionState
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ A list of open pull requests matching the check suite.
+ """
+ matchingPullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection
+
+ """
+ The push that triggered this check suite.
+ """
+ push: Push
+
+ """
+ The repository associated with this check suite.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this check suite
+ """
+ resourcePath: URI!
+
+ """
+ The status of this check suite.
+ """
+ status: CheckStatusState!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this check suite
+ """
+ url: URI!
+}
+
+"""
+The auto-trigger preferences that are available for check suites.
+"""
+input CheckSuiteAutoTriggerPreference {
+ """
+ The node ID of the application that owns the check suite.
+ """
+ appId: ID!
+
+ """
+ Set to `true` to enable automatic creation of CheckSuite events upon pushes to the repository.
+ """
+ setting: Boolean!
+}
+
+"""
+The connection type for CheckSuite.
+"""
+type CheckSuiteConnection {
+ """
+ A list of edges.
+ """
+ edges: [CheckSuiteEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CheckSuite]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CheckSuiteEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CheckSuite
+}
+
+"""
+The filters that are available when fetching check suites.
+"""
+input CheckSuiteFilter {
+ """
+ Filters the check suites created by this application ID.
+ """
+ appId: Int
+
+ """
+ Filters the check suites by this name.
+ """
+ checkName: String
+}
+
+"""
+Autogenerated input type of ClearLabelsFromLabelable
+"""
+input ClearLabelsFromLabelableInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the labelable object to clear the labels from.
+ """
+ labelableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Labelable")
+}
+
+"""
+Autogenerated return type of ClearLabelsFromLabelable
+"""
+type ClearLabelsFromLabelablePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The item that was unlabeled.
+ """
+ labelable: Labelable
+}
+
+"""
+Autogenerated input type of CloneProject
+"""
+input CloneProjectInput {
+ """
+ The description of the project.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Whether or not to clone the source project's workflows.
+ """
+ includeWorkflows: Boolean!
+
+ """
+ The name of the project.
+ """
+ name: String!
+
+ """
+ The visibility of the project, defaults to false (private).
+ """
+ public: Boolean
+
+ """
+ The source project to clone.
+ """
+ sourceId: ID! @possibleTypes(concreteTypes: ["Project"])
+
+ """
+ The owner ID to create the project under.
+ """
+ targetOwnerId: ID! @possibleTypes(concreteTypes: ["Organization", "Repository", "User"], abstractType: "ProjectOwner")
+}
+
+"""
+Autogenerated return type of CloneProject
+"""
+type CloneProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the JobStatus for populating cloned fields.
+ """
+ jobStatusId: String
+
+ """
+ The new cloned project.
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of CloneTemplateRepository
+"""
+input CloneTemplateRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A short description of the new repository.
+ """
+ description: String
+
+ """
+ Whether to copy all branches from the template to the new repository. Defaults
+ to copying only the default branch of the template.
+ """
+ includeAllBranches: Boolean = false
+
+ """
+ The name of the new repository.
+ """
+ name: String!
+
+ """
+ The ID of the owner for the new repository.
+ """
+ ownerId: ID! @possibleTypes(concreteTypes: ["Organization", "User"], abstractType: "RepositoryOwner")
+
+ """
+ The Node ID of the template repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ Indicates the repository's visibility level.
+ """
+ visibility: RepositoryVisibility!
+}
+
+"""
+Autogenerated return type of CloneTemplateRepository
+"""
+type CloneTemplateRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new repository.
+ """
+ repository: Repository
+}
+
+"""
+An object that can be closed
+"""
+interface Closable {
+ """
+ `true` if the object is closed (definition of closed may depend on type)
+ """
+ closed: Boolean!
+
+ """
+ Identifies the date and time when the object was closed.
+ """
+ closedAt: DateTime
+}
+
+"""
+Autogenerated input type of CloseIssue
+"""
+input CloseIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the issue to be closed.
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+}
+
+"""
+Autogenerated return type of CloseIssue
+"""
+type CloseIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue that was closed.
+ """
+ issue: Issue
+}
+
+"""
+Autogenerated input type of ClosePullRequest
+"""
+input ClosePullRequestInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the pull request to be closed.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of ClosePullRequest
+"""
+type ClosePullRequestPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request that was closed.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'closed' event on any `Closable`.
+"""
+type ClosedEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Object that was closed.
+ """
+ closable: Closable!
+
+ """
+ Object which triggered the creation of this event.
+ """
+ closer: Closer
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The HTTP path for this closed event.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this closed event.
+ """
+ url: URI!
+}
+
+"""
+The object which triggered a `ClosedEvent`.
+"""
+union Closer = Commit | PullRequest
+
+"""
+The Code of Conduct for a repository
+"""
+type CodeOfConduct implements Node {
+ """
+ The body of the Code of Conduct
+ """
+ body: String
+ id: ID!
+
+ """
+ The key for the Code of Conduct
+ """
+ key: String!
+
+ """
+ The formal name of the Code of Conduct
+ """
+ name: String!
+
+ """
+ The HTTP path for this Code of Conduct
+ """
+ resourcePath: URI
+
+ """
+ The HTTP URL for this Code of Conduct
+ """
+ url: URI
+}
+
+"""
+Collaborators affiliation level with a subject.
+"""
+enum CollaboratorAffiliation {
+ """
+ All collaborators the authenticated user can see.
+ """
+ ALL
+
+ """
+ All collaborators with permissions to an organization-owned subject, regardless of organization membership status.
+ """
+ DIRECT
+
+ """
+ All outside collaborators of an organization-owned subject.
+ """
+ OUTSIDE
+}
+
+"""
+Represents a comment.
+"""
+interface Comment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ The body as Markdown.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+A comment author association with repository.
+"""
+enum CommentAuthorAssociation {
+ """
+ Author has been invited to collaborate on the repository.
+ """
+ COLLABORATOR
+
+ """
+ Author has previously committed to the repository.
+ """
+ CONTRIBUTOR
+
+ """
+ Author has not previously committed to GitHub.
+ """
+ FIRST_TIMER
+
+ """
+ Author has not previously committed to the repository.
+ """
+ FIRST_TIME_CONTRIBUTOR
+
+ """
+ Author is a placeholder for an unclaimed user.
+ """
+ MANNEQUIN
+
+ """
+ Author is a member of the organization that owns the repository.
+ """
+ MEMBER
+
+ """
+ Author has no association with the repository.
+ """
+ NONE
+
+ """
+ Author is the owner of the repository.
+ """
+ OWNER
+}
+
+"""
+The possible errors that will prevent a user from updating a comment.
+"""
+enum CommentCannotUpdateReason {
+ """
+ Unable to create comment because repository is archived.
+ """
+ ARCHIVED
+
+ """
+ You cannot update this comment
+ """
+ DENIED
+
+ """
+ You must be the author or have write access to this repository to update this comment.
+ """
+ INSUFFICIENT_ACCESS
+
+ """
+ Unable to create comment because issue is locked.
+ """
+ LOCKED
+
+ """
+ You must be logged in to update this comment.
+ """
+ LOGIN_REQUIRED
+
+ """
+ Repository is under maintenance.
+ """
+ MAINTENANCE
+
+ """
+ At least one email address must be verified to update this comment.
+ """
+ VERIFIED_EMAIL_REQUIRED
+}
+
+"""
+Represents a 'comment_deleted' event on a given issue or pull request.
+"""
+type CommentDeletedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The user who authored the deleted comment.
+ """
+ deletedCommentAuthor: Actor
+ id: ID!
+}
+
+"""
+Represents a Git commit.
+"""
+type Commit implements GitObject & Node & Subscribable & UniformResourceLocatable {
+ """
+ An abbreviated version of the Git object ID
+ """
+ abbreviatedOid: String!
+
+ """
+ The number of additions in this commit.
+ """
+ additions: Int!
+
+ """
+ The pull requests associated with a commit
+ """
+ associatedPullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests.
+ """
+ orderBy: PullRequestOrder = {field: CREATED_AT, direction: ASC}
+ ): PullRequestConnection
+
+ """
+ Authorship details of the commit.
+ """
+ author: GitActor
+
+ """
+ Check if the committer and the author match.
+ """
+ authoredByCommitter: Boolean!
+
+ """
+ The datetime when this commit was authored.
+ """
+ authoredDate: DateTime!
+
+ """
+ The list of authors for this commit based on the git author and the Co-authored-by
+ message trailer. The git author will always be first.
+ """
+ authors(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): GitActorConnection!
+
+ """
+ Fetches `git blame` information.
+ """
+ blame(
+ """
+ The file whose Git blame information you want.
+ """
+ path: String!
+ ): Blame!
+
+ """
+ The number of changed files in this commit.
+ """
+ changedFiles: Int!
+
+ """
+ The check suites associated with a commit.
+ """
+ checkSuites(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filters the check suites by this type.
+ """
+ filterBy: CheckSuiteFilter
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CheckSuiteConnection
+
+ """
+ Comments made on the commit.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitCommentConnection!
+
+ """
+ The HTTP path for this Git object
+ """
+ commitResourcePath: URI!
+
+ """
+ The HTTP URL for this Git object
+ """
+ commitUrl: URI!
+
+ """
+ The datetime when this commit was committed.
+ """
+ committedDate: DateTime!
+
+ """
+ Check if committed via GitHub web UI.
+ """
+ committedViaWeb: Boolean!
+
+ """
+ Committer details of the commit.
+ """
+ committer: GitActor
+
+ """
+ The number of deletions in this commit.
+ """
+ deletions: Int!
+
+ """
+ The deployments associated with a commit.
+ """
+ deployments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Environments to list deployments for
+ """
+ environments: [String!]
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for deployments returned from the connection.
+ """
+ orderBy: DeploymentOrder = {field: CREATED_AT, direction: ASC}
+ ): DeploymentConnection
+
+ """
+ The tree entry representing the file located at the given path.
+ """
+ file(
+ """
+ The path for the file
+ """
+ path: String!
+ ): TreeEntry
+
+ """
+ The linear commit history starting from (and including) this commit, in the same order as `git log`.
+ """
+ history(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ If non-null, filters history to only show commits with matching authorship.
+ """
+ author: CommitAuthor
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ If non-null, filters history to only show commits touching files under this path.
+ """
+ path: String
+
+ """
+ Allows specifying a beginning time or date for fetching commits.
+ """
+ since: GitTimestamp
+
+ """
+ Allows specifying an ending time or date for fetching commits.
+ """
+ until: GitTimestamp
+ ): CommitHistoryConnection!
+ id: ID!
+
+ """
+ The Git commit message
+ """
+ message: String!
+
+ """
+ The Git commit message body
+ """
+ messageBody: String!
+
+ """
+ The commit message body rendered to HTML.
+ """
+ messageBodyHTML: HTML!
+
+ """
+ The Git commit message headline
+ """
+ messageHeadline: String!
+
+ """
+ The commit message headline rendered to HTML.
+ """
+ messageHeadlineHTML: HTML!
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID!
+
+ """
+ The organization this commit was made on behalf of.
+ """
+ onBehalfOf: Organization
+
+ """
+ The parents of a commit.
+ """
+ parents(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitConnection!
+
+ """
+ The datetime when this commit was pushed.
+ """
+ pushedDate: DateTime
+
+ """
+ The Repository this commit belongs to
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this commit
+ """
+ resourcePath: URI!
+
+ """
+ Commit signing information, if present.
+ """
+ signature: GitSignature
+
+ """
+ Status information for this commit
+ """
+ status: Status
+
+ """
+ Check and Status rollup information for this commit.
+ """
+ statusCheckRollup: StatusCheckRollup
+
+ """
+ Returns a list of all submodules in this repository as of this Commit parsed from the .gitmodules file.
+ """
+ submodules(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): SubmoduleConnection!
+
+ """
+ Returns a URL to download a tarball archive for a repository.
+ Note: For private repositories, these links are temporary and expire after five minutes.
+ """
+ tarballUrl: URI!
+
+ """
+ Commit's root Tree
+ """
+ tree: Tree!
+
+ """
+ The HTTP path for the tree of this commit
+ """
+ treeResourcePath: URI!
+
+ """
+ The HTTP URL for the tree of this commit
+ """
+ treeUrl: URI!
+
+ """
+ The HTTP URL for this commit
+ """
+ url: URI!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+
+ """
+ Returns a URL to download a zipball archive for a repository.
+ Note: For private repositories, these links are temporary and expire after five minutes.
+ """
+ zipballUrl: URI!
+}
+
+"""
+Specifies an author for filtering Git commits.
+"""
+input CommitAuthor {
+ """
+ Email addresses to filter by. Commits authored by any of the specified email addresses will be returned.
+ """
+ emails: [String!]
+
+ """
+ ID of a User to filter by. If non-null, only commits authored by this user
+ will be returned. This field takes precedence over emails.
+ """
+ id: ID
+}
+
+"""
+Represents a comment on a given Commit.
+"""
+type CommitComment implements Comment & Deletable & Minimizable & Node & Reactable & RepositoryNode & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ Identifies the comment body.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the commit associated with the comment, if the commit exists.
+ """
+ commit: Commit
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Returns whether or not a comment has been minimized.
+ """
+ isMinimized: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Returns why the comment was minimized.
+ """
+ minimizedReason: String
+
+ """
+ Identifies the file path associated with the comment.
+ """
+ path: String
+
+ """
+ Identifies the line position associated with the comment.
+ """
+ position: Int
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path permalink for this commit comment.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL permalink for this commit comment.
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Check if the current viewer can minimize this object.
+ """
+ viewerCanMinimize: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+The connection type for CommitComment.
+"""
+type CommitCommentConnection {
+ """
+ A list of edges.
+ """
+ edges: [CommitCommentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CommitComment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CommitCommentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CommitComment
+}
+
+"""
+A thread of comments on a commit.
+"""
+type CommitCommentThread implements Node & RepositoryNode {
+ """
+ The comments that exist in this thread.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitCommentConnection!
+
+ """
+ The commit the comments were made on.
+ """
+ commit: Commit
+ id: ID!
+
+ """
+ The file the comments were made on.
+ """
+ path: String
+
+ """
+ The position in the diff for the commit that the comment was made on.
+ """
+ position: Int
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+}
+
+"""
+The connection type for Commit.
+"""
+type CommitConnection {
+ """
+ A list of edges.
+ """
+ edges: [CommitEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Commit]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Ordering options for commit contribution connections.
+"""
+input CommitContributionOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field by which to order commit contributions.
+ """
+ field: CommitContributionOrderField!
+}
+
+"""
+Properties by which commit contribution connections can be ordered.
+"""
+enum CommitContributionOrderField {
+ """
+ Order commit contributions by how many commits they represent.
+ """
+ COMMIT_COUNT
+
+ """
+ Order commit contributions by when they were made.
+ """
+ OCCURRED_AT
+}
+
+"""
+This aggregates commits made by a user within one repository.
+"""
+type CommitContributionsByRepository {
+ """
+ The commit contributions, each representing a day.
+ """
+ contributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for commit contributions returned from the connection.
+ """
+ orderBy: CommitContributionOrder = {field: OCCURRED_AT, direction: DESC}
+ ): CreatedCommitContributionConnection!
+
+ """
+ The repository in which the commits were made.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for the user's commits to the repository in this time range.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for the user's commits to the repository in this time range.
+ """
+ url: URI!
+}
+
+"""
+An edge in a connection.
+"""
+type CommitEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Commit
+}
+
+"""
+The connection type for Commit.
+"""
+type CommitHistoryConnection {
+ """
+ A list of edges.
+ """
+ edges: [CommitEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Commit]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a 'connected' event on a given issue or pull request.
+"""
+type ConnectedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Reference originated in a different repository.
+ """
+ isCrossRepository: Boolean!
+
+ """
+ Issue or pull request that made the reference.
+ """
+ source: ReferencedSubject!
+
+ """
+ Issue or pull request which was connected.
+ """
+ subject: ReferencedSubject!
+}
+
+"""
+A content attachment
+"""
+type ContentAttachment {
+ """
+ The body text of the content attachment. This parameter supports markdown.
+ """
+ body: String!
+
+ """
+ The content reference that the content attachment is attached to.
+ """
+ contentReference: ContentReference!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int!
+ id: ID!
+
+ """
+ The title of the content attachment.
+ """
+ title: String!
+}
+
+"""
+A content reference
+"""
+type ContentReference {
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int!
+ id: ID!
+
+ """
+ The reference of the content reference.
+ """
+ reference: String!
+}
+
+"""
+Represents a contribution a user made on GitHub, such as opening an issue.
+"""
+interface Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+A calendar of contributions made on GitHub by a user.
+"""
+type ContributionCalendar {
+ """
+ A list of hex color codes used in this calendar. The darker the color, the more contributions it represents.
+ """
+ colors: [String!]!
+
+ """
+ Determine if the color set was chosen because it's currently Halloween.
+ """
+ isHalloween: Boolean!
+
+ """
+ A list of the months of contributions in this calendar.
+ """
+ months: [ContributionCalendarMonth!]!
+
+ """
+ The count of total contributions in the calendar.
+ """
+ totalContributions: Int!
+
+ """
+ A list of the weeks of contributions in this calendar.
+ """
+ weeks: [ContributionCalendarWeek!]!
+}
+
+"""
+Represents a single day of contributions on GitHub by a user.
+"""
+type ContributionCalendarDay {
+ """
+ The hex color code that represents how many contributions were made on this day compared to others in the calendar.
+ """
+ color: String!
+
+ """
+ How many contributions were made by the user on this day.
+ """
+ contributionCount: Int!
+
+ """
+ Indication of contributions, relative to other days. Can be used to indicate
+ which color to represent this day on a calendar.
+ """
+ contributionLevel: ContributionLevel!
+
+ """
+ The day this square represents.
+ """
+ date: Date!
+
+ """
+ A number representing which day of the week this square represents, e.g., 1 is Monday.
+ """
+ weekday: Int!
+}
+
+"""
+A month of contributions in a user's contribution graph.
+"""
+type ContributionCalendarMonth {
+ """
+ The date of the first day of this month.
+ """
+ firstDay: Date!
+
+ """
+ The name of the month.
+ """
+ name: String!
+
+ """
+ How many weeks started in this month.
+ """
+ totalWeeks: Int!
+
+ """
+ The year the month occurred in.
+ """
+ year: Int!
+}
+
+"""
+A week of contributions in a user's contribution graph.
+"""
+type ContributionCalendarWeek {
+ """
+ The days of contributions in this week.
+ """
+ contributionDays: [ContributionCalendarDay!]!
+
+ """
+ The date of the earliest square in this week.
+ """
+ firstDay: Date!
+}
+
+"""
+Varying levels of contributions from none to many.
+"""
+enum ContributionLevel {
+ """
+ Lowest 25% of days of contributions.
+ """
+ FIRST_QUARTILE
+
+ """
+ Highest 25% of days of contributions. More contributions than the third quartile.
+ """
+ FOURTH_QUARTILE
+
+ """
+ No contributions occurred.
+ """
+ NONE
+
+ """
+ Second lowest 25% of days of contributions. More contributions than the first quartile.
+ """
+ SECOND_QUARTILE
+
+ """
+ Second highest 25% of days of contributions. More contributions than second quartile, less than the fourth quartile.
+ """
+ THIRD_QUARTILE
+}
+
+"""
+Ordering options for contribution connections.
+"""
+input ContributionOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+}
+
+"""
+A contributions collection aggregates contributions such as opened issues and commits created by a user.
+"""
+type ContributionsCollection {
+ """
+ Commit contributions made by the user, grouped by repository.
+ """
+ commitContributionsByRepository(
+ """
+ How many repositories should be included.
+ """
+ maxRepositories: Int = 25
+ ): [CommitContributionsByRepository!]!
+
+ """
+ A calendar of this user's contributions on GitHub.
+ """
+ contributionCalendar: ContributionCalendar!
+
+ """
+ The years the user has been making contributions with the most recent year first.
+ """
+ contributionYears: [Int!]!
+
+ """
+ Determine if this collection's time span ends in the current month.
+ """
+ doesEndInCurrentMonth: Boolean!
+
+ """
+ The date of the first restricted contribution the user made in this time
+ period. Can only be non-null when the user has enabled private contribution counts.
+ """
+ earliestRestrictedContributionDate: Date
+
+ """
+ The ending date and time of this collection.
+ """
+ endedAt: DateTime!
+
+ """
+ The first issue the user opened on GitHub. This will be null if that issue was
+ opened outside the collection's time range and ignoreTimeRange is false. If
+ the issue is not visible but the user has opted to show private contributions,
+ a RestrictedContribution will be returned.
+ """
+ firstIssueContribution: CreatedIssueOrRestrictedContribution
+
+ """
+ The first pull request the user opened on GitHub. This will be null if that
+ pull request was opened outside the collection's time range and
+ ignoreTimeRange is not true. If the pull request is not visible but the user
+ has opted to show private contributions, a RestrictedContribution will be returned.
+ """
+ firstPullRequestContribution: CreatedPullRequestOrRestrictedContribution
+
+ """
+ The first repository the user created on GitHub. This will be null if that
+ first repository was created outside the collection's time range and
+ ignoreTimeRange is false. If the repository is not visible, then a
+ RestrictedContribution is returned.
+ """
+ firstRepositoryContribution: CreatedRepositoryOrRestrictedContribution
+
+ """
+ Does the user have any more activity in the timeline that occurred prior to the collection's time range?
+ """
+ hasActivityInThePast: Boolean!
+
+ """
+ Determine if there are any contributions in this collection.
+ """
+ hasAnyContributions: Boolean!
+
+ """
+ Determine if the user made any contributions in this time frame whose details
+ are not visible because they were made in a private repository. Can only be
+ true if the user enabled private contribution counts.
+ """
+ hasAnyRestrictedContributions: Boolean!
+
+ """
+ Whether or not the collector's time span is all within the same day.
+ """
+ isSingleDay: Boolean!
+
+ """
+ A list of issues the user opened.
+ """
+ issueContributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Should the user's first issue ever be excluded from the result.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented issue be excluded from the result.
+ """
+ excludePopular: Boolean = false
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedIssueContributionConnection!
+
+ """
+ Issue contributions made by the user, grouped by repository.
+ """
+ issueContributionsByRepository(
+ """
+ Should the user's first issue ever be excluded from the result.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented issue be excluded from the result.
+ """
+ excludePopular: Boolean = false
+
+ """
+ How many repositories should be included.
+ """
+ maxRepositories: Int = 25
+ ): [IssueContributionsByRepository!]!
+
+ """
+ When the user signed up for GitHub. This will be null if that sign up date
+ falls outside the collection's time range and ignoreTimeRange is false.
+ """
+ joinedGitHubContribution: JoinedGitHubContribution
+
+ """
+ The date of the most recent restricted contribution the user made in this time
+ period. Can only be non-null when the user has enabled private contribution counts.
+ """
+ latestRestrictedContributionDate: Date
+
+ """
+ When this collection's time range does not include any activity from the user, use this
+ to get a different collection from an earlier time range that does have activity.
+ """
+ mostRecentCollectionWithActivity: ContributionsCollection
+
+ """
+ Returns a different contributions collection from an earlier time range than this one
+ that does not have any contributions.
+ """
+ mostRecentCollectionWithoutActivity: ContributionsCollection
+
+ """
+ The issue the user opened on GitHub that received the most comments in the specified
+ time frame.
+ """
+ popularIssueContribution: CreatedIssueContribution
+
+ """
+ The pull request the user opened on GitHub that received the most comments in the
+ specified time frame.
+ """
+ popularPullRequestContribution: CreatedPullRequestContribution
+
+ """
+ Pull request contributions made by the user.
+ """
+ pullRequestContributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Should the user's first pull request ever be excluded from the result.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented pull request be excluded from the result.
+ """
+ excludePopular: Boolean = false
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedPullRequestContributionConnection!
+
+ """
+ Pull request contributions made by the user, grouped by repository.
+ """
+ pullRequestContributionsByRepository(
+ """
+ Should the user's first pull request ever be excluded from the result.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented pull request be excluded from the result.
+ """
+ excludePopular: Boolean = false
+
+ """
+ How many repositories should be included.
+ """
+ maxRepositories: Int = 25
+ ): [PullRequestContributionsByRepository!]!
+
+ """
+ Pull request review contributions made by the user.
+ """
+ pullRequestReviewContributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedPullRequestReviewContributionConnection!
+
+ """
+ Pull request review contributions made by the user, grouped by repository.
+ """
+ pullRequestReviewContributionsByRepository(
+ """
+ How many repositories should be included.
+ """
+ maxRepositories: Int = 25
+ ): [PullRequestReviewContributionsByRepository!]!
+
+ """
+ A list of repositories owned by the user that the user created in this time range.
+ """
+ repositoryContributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Should the user's first repository ever be excluded from the result.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedRepositoryContributionConnection!
+
+ """
+ A count of contributions made by the user that the viewer cannot access. Only
+ non-zero when the user has chosen to share their private contribution counts.
+ """
+ restrictedContributionsCount: Int!
+
+ """
+ The beginning date and time of this collection.
+ """
+ startedAt: DateTime!
+
+ """
+ How many commits were made by the user in this time span.
+ """
+ totalCommitContributions: Int!
+
+ """
+ How many issues the user opened.
+ """
+ totalIssueContributions(
+ """
+ Should the user's first issue ever be excluded from this count.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented issue be excluded from this count.
+ """
+ excludePopular: Boolean = false
+ ): Int!
+
+ """
+ How many pull requests the user opened.
+ """
+ totalPullRequestContributions(
+ """
+ Should the user's first pull request ever be excluded from this count.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented pull request be excluded from this count.
+ """
+ excludePopular: Boolean = false
+ ): Int!
+
+ """
+ How many pull request reviews the user left.
+ """
+ totalPullRequestReviewContributions: Int!
+
+ """
+ How many different repositories the user committed to.
+ """
+ totalRepositoriesWithContributedCommits: Int!
+
+ """
+ How many different repositories the user opened issues in.
+ """
+ totalRepositoriesWithContributedIssues(
+ """
+ Should the user's first issue ever be excluded from this count.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented issue be excluded from this count.
+ """
+ excludePopular: Boolean = false
+ ): Int!
+
+ """
+ How many different repositories the user left pull request reviews in.
+ """
+ totalRepositoriesWithContributedPullRequestReviews: Int!
+
+ """
+ How many different repositories the user opened pull requests in.
+ """
+ totalRepositoriesWithContributedPullRequests(
+ """
+ Should the user's first pull request ever be excluded from this count.
+ """
+ excludeFirst: Boolean = false
+
+ """
+ Should the user's most commented pull request be excluded from this count.
+ """
+ excludePopular: Boolean = false
+ ): Int!
+
+ """
+ How many repositories the user created.
+ """
+ totalRepositoryContributions(
+ """
+ Should the user's first repository ever be excluded from this count.
+ """
+ excludeFirst: Boolean = false
+ ): Int!
+
+ """
+ The user who made the contributions in this collection.
+ """
+ user: User!
+}
+
+"""
+Autogenerated input type of ConvertProjectCardNoteToIssue
+"""
+input ConvertProjectCardNoteToIssueInput {
+ """
+ The body of the newly created issue.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ProjectCard ID to convert.
+ """
+ projectCardId: ID! @possibleTypes(concreteTypes: ["ProjectCard"])
+
+ """
+ The ID of the repository to create the issue in.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The title of the newly created issue. Defaults to the card's note text.
+ """
+ title: String
+}
+
+"""
+Autogenerated return type of ConvertProjectCardNoteToIssue
+"""
+type ConvertProjectCardNoteToIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated ProjectCard.
+ """
+ projectCard: ProjectCard
+}
+
+"""
+Represents a 'convert_to_draft' event on a given pull request.
+"""
+type ConvertToDraftEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The HTTP path for this convert to draft event.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this convert to draft event.
+ """
+ url: URI!
+}
+
+"""
+Represents a 'converted_note_to_issue' event on a given issue or pull request.
+"""
+type ConvertedNoteToIssueEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Project referenced by event.
+ """
+ project: Project @preview(toggledBy: "starfox-preview")
+
+ """
+ Project card referenced by this project event.
+ """
+ projectCard: ProjectCard @preview(toggledBy: "starfox-preview")
+
+ """
+ Column name referenced by this project event.
+ """
+ projectColumnName: String! @preview(toggledBy: "starfox-preview")
+}
+
+"""
+Autogenerated input type of CreateBranchProtectionRule
+"""
+input CreateBranchProtectionRuleInput {
+ """
+ Can this branch be deleted.
+ """
+ allowsDeletions: Boolean
+
+ """
+ Are force pushes allowed on this branch.
+ """
+ allowsForcePushes: Boolean
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Will new commits pushed to matching branches dismiss pull request review approvals.
+ """
+ dismissesStaleReviews: Boolean
+
+ """
+ Can admins overwrite branch protection.
+ """
+ isAdminEnforced: Boolean
+
+ """
+ The glob-like pattern used to determine matching branches.
+ """
+ pattern: String!
+
+ """
+ A list of User, Team or App IDs allowed to push to matching branches.
+ """
+ pushActorIds: [ID!]
+
+ """
+ The global relay id of the repository in which a new branch protection rule should be created in.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ Number of approving reviews required to update matching branches.
+ """
+ requiredApprovingReviewCount: Int
+
+ """
+ List of required status check contexts that must pass for commits to be accepted to matching branches.
+ """
+ requiredStatusCheckContexts: [String!]
+
+ """
+ Are approving reviews required to update matching branches.
+ """
+ requiresApprovingReviews: Boolean
+
+ """
+ Are reviews from code owners required to update matching branches.
+ """
+ requiresCodeOwnerReviews: Boolean
+
+ """
+ Are commits required to be signed.
+ """
+ requiresCommitSignatures: Boolean
+
+ """
+ Are merge commits prohibited from being pushed to this branch.
+ """
+ requiresLinearHistory: Boolean
+
+ """
+ Are status checks required to update matching branches.
+ """
+ requiresStatusChecks: Boolean
+
+ """
+ Are branches required to be up to date before merging.
+ """
+ requiresStrictStatusChecks: Boolean
+
+ """
+ Is pushing to matching branches restricted.
+ """
+ restrictsPushes: Boolean
+
+ """
+ Is dismissal of pull request reviews restricted.
+ """
+ restrictsReviewDismissals: Boolean
+
+ """
+ A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.
+ """
+ reviewDismissalActorIds: [ID!]
+}
+
+"""
+Autogenerated return type of CreateBranchProtectionRule
+"""
+type CreateBranchProtectionRulePayload {
+ """
+ The newly created BranchProtectionRule.
+ """
+ branchProtectionRule: BranchProtectionRule
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of CreateCheckRun
+"""
+input CreateCheckRunInput {
+ """
+ Possible further actions the integrator can perform, which a user may trigger.
+ """
+ actions: [CheckRunAction!]
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The time that the check run finished.
+ """
+ completedAt: DateTime
+
+ """
+ The final conclusion of the check.
+ """
+ conclusion: CheckConclusionState
+
+ """
+ The URL of the integrator's site that has the full details of the check.
+ """
+ detailsUrl: URI
+
+ """
+ A reference for the run on the integrator's system.
+ """
+ externalId: String
+
+ """
+ The SHA of the head commit.
+ """
+ headSha: GitObjectID!
+
+ """
+ The name of the check.
+ """
+ name: String!
+
+ """
+ Descriptive details about the run.
+ """
+ output: CheckRunOutput
+
+ """
+ The node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The time that the check run began.
+ """
+ startedAt: DateTime
+
+ """
+ The current status.
+ """
+ status: RequestableCheckStatusState
+}
+
+"""
+Autogenerated return type of CreateCheckRun
+"""
+type CreateCheckRunPayload {
+ """
+ The newly created check run.
+ """
+ checkRun: CheckRun
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of CreateCheckSuite
+"""
+input CreateCheckSuiteInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The SHA of the head commit.
+ """
+ headSha: GitObjectID!
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of CreateCheckSuite
+"""
+type CreateCheckSuitePayload {
+ """
+ The newly created check suite.
+ """
+ checkSuite: CheckSuite
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of CreateContentAttachment
+"""
+input CreateContentAttachmentInput {
+ """
+ The body of the content attachment, which may contain markdown.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The node ID of the content_reference.
+ """
+ contentReferenceId: ID! @possibleTypes(concreteTypes: ["ContentReference"])
+
+ """
+ The title of the content attachment.
+ """
+ title: String!
+}
+
+"""
+Autogenerated return type of CreateContentAttachment
+"""
+type CreateContentAttachmentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The newly created content attachment.
+ """
+ contentAttachment: ContentAttachment
+}
+
+"""
+Autogenerated input type of CreateDeployment
+"""
+input CreateDeploymentInput @preview(toggledBy: "flash-preview") {
+ """
+ Attempt to automatically merge the default branch into the requested ref, defaults to true.
+ """
+ autoMerge: Boolean = true
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Short description of the deployment.
+ """
+ description: String = ""
+
+ """
+ Name for the target deployment environment.
+ """
+ environment: String = "production"
+
+ """
+ JSON payload with extra information about the deployment.
+ """
+ payload: String = "{}"
+
+ """
+ The node ID of the ref to be deployed.
+ """
+ refId: ID! @possibleTypes(concreteTypes: ["Ref"])
+
+ """
+ The node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The status contexts to verify against commit status checks. To bypass required
+ contexts, pass an empty array. Defaults to all unique contexts.
+ """
+ requiredContexts: [String!]
+
+ """
+ Specifies a task to execute.
+ """
+ task: String = "deploy"
+}
+
+"""
+Autogenerated return type of CreateDeployment
+"""
+type CreateDeploymentPayload @preview(toggledBy: "flash-preview") {
+ """
+ True if the default branch has been auto-merged into the deployment ref.
+ """
+ autoMerged: Boolean
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new deployment.
+ """
+ deployment: Deployment
+}
+
+"""
+Autogenerated input type of CreateDeploymentStatus
+"""
+input CreateDeploymentStatusInput @preview(toggledBy: "flash-preview") {
+ """
+ Adds a new inactive status to all non-transient, non-production environment
+ deployments with the same repository and environment name as the created
+ status's deployment.
+ """
+ autoInactive: Boolean = true
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The node ID of the deployment.
+ """
+ deploymentId: ID! @possibleTypes(concreteTypes: ["Deployment"])
+
+ """
+ A short description of the status. Maximum length of 140 characters.
+ """
+ description: String = ""
+
+ """
+ If provided, updates the environment of the deploy. Otherwise, does not modify the environment.
+ """
+ environment: String
+
+ """
+ Sets the URL for accessing your environment.
+ """
+ environmentUrl: String = ""
+
+ """
+ The log URL to associate with this status. This URL should contain
+ output to keep the user updated while the task is running or serve as
+ historical information for what happened in the deployment.
+ """
+ logUrl: String = ""
+
+ """
+ The state of the deployment.
+ """
+ state: DeploymentStatusState!
+}
+
+"""
+Autogenerated return type of CreateDeploymentStatus
+"""
+type CreateDeploymentStatusPayload @preview(toggledBy: "flash-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new deployment status.
+ """
+ deploymentStatus: DeploymentStatus
+}
+
+"""
+Autogenerated input type of CreateEnterpriseOrganization
+"""
+input CreateEnterpriseOrganizationInput {
+ """
+ The logins for the administrators of the new organization.
+ """
+ adminLogins: [String!]!
+
+ """
+ The email used for sending billing receipts.
+ """
+ billingEmail: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise owning the new organization.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of the new organization.
+ """
+ login: String!
+
+ """
+ The profile name of the new organization.
+ """
+ profileName: String!
+}
+
+"""
+Autogenerated return type of CreateEnterpriseOrganization
+"""
+type CreateEnterpriseOrganizationPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise that owns the created organization.
+ """
+ enterprise: Enterprise
+
+ """
+ The organization that was created.
+ """
+ organization: Organization
+}
+
+"""
+Autogenerated input type of CreateIpAllowListEntry
+"""
+input CreateIpAllowListEntryInput {
+ """
+ An IP address or range of addresses in CIDR notation.
+ """
+ allowListValue: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Whether the IP allow list entry is active when an IP allow list is enabled.
+ """
+ isActive: Boolean!
+
+ """
+ An optional name for the IP allow list entry.
+ """
+ name: String
+
+ """
+ The ID of the owner for which to create the new IP allow list entry.
+ """
+ ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner")
+}
+
+"""
+Autogenerated return type of CreateIpAllowListEntry
+"""
+type CreateIpAllowListEntryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The IP allow list entry that was created.
+ """
+ ipAllowListEntry: IpAllowListEntry
+}
+
+"""
+Autogenerated input type of CreateIssue
+"""
+input CreateIssueInput {
+ """
+ The Node ID for the user assignee for this issue.
+ """
+ assigneeIds: [ID!] @possibleTypes(concreteTypes: ["User"])
+
+ """
+ The body for the issue description.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of an issue template in the repository, assigns labels and assignees from the template to the issue
+ """
+ issueTemplate: String
+
+ """
+ An array of Node IDs of labels for this issue.
+ """
+ labelIds: [ID!] @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ The Node ID of the milestone for this issue.
+ """
+ milestoneId: ID @possibleTypes(concreteTypes: ["Milestone"])
+
+ """
+ An array of Node IDs for projects associated with this issue.
+ """
+ projectIds: [ID!] @possibleTypes(concreteTypes: ["Project"])
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The title for the issue.
+ """
+ title: String!
+}
+
+"""
+Autogenerated return type of CreateIssue
+"""
+type CreateIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new issue.
+ """
+ issue: Issue
+}
+
+"""
+Autogenerated input type of CreateLabel
+"""
+input CreateLabelInput @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A 6 character hex code, without the leading #, identifying the color of the label.
+ """
+ color: String!
+
+ """
+ A brief description of the label, such as its purpose.
+ """
+ description: String
+
+ """
+ The name of the label.
+ """
+ name: String!
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of CreateLabel
+"""
+type CreateLabelPayload @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new label.
+ """
+ label: Label
+}
+
+"""
+Autogenerated input type of CreateProject
+"""
+input CreateProjectInput {
+ """
+ The description of project.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of project.
+ """
+ name: String!
+
+ """
+ The owner ID to create the project under.
+ """
+ ownerId: ID! @possibleTypes(concreteTypes: ["Organization", "Repository", "User"], abstractType: "ProjectOwner")
+
+ """
+ A list of repository IDs to create as linked repositories for the project
+ """
+ repositoryIds: [ID!] @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The name of the GitHub-provided template.
+ """
+ template: ProjectTemplate
+}
+
+"""
+Autogenerated return type of CreateProject
+"""
+type CreateProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new project.
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of CreatePullRequest
+"""
+input CreatePullRequestInput {
+ """
+ The name of the branch you want your changes pulled into. This should be an existing branch
+ on the current repository. You cannot update the base branch on a pull request to point
+ to another repository.
+ """
+ baseRefName: String!
+
+ """
+ The contents of the pull request.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Indicates whether this pull request should be a draft.
+ """
+ draft: Boolean = false
+
+ """
+ The name of the branch where your changes are implemented. For cross-repository pull requests
+ in the same network, namespace `head_ref_name` with a user like this: `username:branch`.
+ """
+ headRefName: String!
+
+ """
+ Indicates whether maintainers can modify the pull request.
+ """
+ maintainerCanModify: Boolean = true
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The title of the pull request.
+ """
+ title: String!
+}
+
+"""
+Autogenerated return type of CreatePullRequest
+"""
+type CreatePullRequestPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new pull request.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Autogenerated input type of CreateRef
+"""
+input CreateRefInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The fully qualified name of the new Ref (ie: `refs/heads/my_new_branch`).
+ """
+ name: String!
+
+ """
+ The GitObjectID that the new Ref shall target. Must point to a commit.
+ """
+ oid: GitObjectID!
+
+ """
+ The Node ID of the Repository to create the Ref in.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of CreateRef
+"""
+type CreateRefPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The newly created ref.
+ """
+ ref: Ref
+}
+
+"""
+Autogenerated input type of CreateRepository
+"""
+input CreateRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A short description of the new repository.
+ """
+ description: String
+
+ """
+ Indicates if the repository should have the issues feature enabled.
+ """
+ hasIssuesEnabled: Boolean = true
+
+ """
+ Indicates if the repository should have the wiki feature enabled.
+ """
+ hasWikiEnabled: Boolean = false
+
+ """
+ The URL for a web page about this repository.
+ """
+ homepageUrl: URI
+
+ """
+ The name of the new repository.
+ """
+ name: String!
+
+ """
+ The ID of the owner for the new repository.
+ """
+ ownerId: ID @possibleTypes(concreteTypes: ["Organization", "User"], abstractType: "RepositoryOwner")
+
+ """
+ When an organization is specified as the owner, this ID identifies the team
+ that should be granted access to the new repository.
+ """
+ teamId: ID @possibleTypes(concreteTypes: ["Team"])
+
+ """
+ Whether this repository should be marked as a template such that anyone who
+ can access it can create new repositories with the same files and directory structure.
+ """
+ template: Boolean = false
+
+ """
+ Indicates the repository's visibility level.
+ """
+ visibility: RepositoryVisibility!
+}
+
+"""
+Autogenerated return type of CreateRepository
+"""
+type CreateRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new repository.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of CreateTeamDiscussionComment
+"""
+input CreateTeamDiscussionCommentInput {
+ """
+ The content of the comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the discussion to which the comment belongs.
+ """
+ discussionId: ID! @possibleTypes(concreteTypes: ["TeamDiscussion"])
+}
+
+"""
+Autogenerated return type of CreateTeamDiscussionComment
+"""
+type CreateTeamDiscussionCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new comment.
+ """
+ teamDiscussionComment: TeamDiscussionComment
+}
+
+"""
+Autogenerated input type of CreateTeamDiscussion
+"""
+input CreateTeamDiscussionInput {
+ """
+ The content of the discussion.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ If true, restricts the visibility of this discussion to team members and
+ organization admins. If false or not specified, allows any organization member
+ to view this discussion.
+ """
+ private: Boolean
+
+ """
+ The ID of the team to which the discussion belongs.
+ """
+ teamId: ID! @possibleTypes(concreteTypes: ["Team"])
+
+ """
+ The title of the discussion.
+ """
+ title: String!
+}
+
+"""
+Autogenerated return type of CreateTeamDiscussion
+"""
+type CreateTeamDiscussionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new discussion.
+ """
+ teamDiscussion: TeamDiscussion
+}
+
+"""
+Represents the contribution a user made by committing to a repository.
+"""
+type CreatedCommitContribution implements Contribution {
+ """
+ How many commits were made on this day to this repository by the user.
+ """
+ commitCount: Int!
+
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The repository the user made a commit in.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+The connection type for CreatedCommitContribution.
+"""
+type CreatedCommitContributionConnection {
+ """
+ A list of edges.
+ """
+ edges: [CreatedCommitContributionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CreatedCommitContribution]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of commits across days and repositories in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CreatedCommitContributionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CreatedCommitContribution
+}
+
+"""
+Represents the contribution a user made on GitHub by opening an issue.
+"""
+type CreatedIssueContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ The issue that was opened.
+ """
+ issue: Issue!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+The connection type for CreatedIssueContribution.
+"""
+type CreatedIssueContributionConnection {
+ """
+ A list of edges.
+ """
+ edges: [CreatedIssueContributionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CreatedIssueContribution]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CreatedIssueContributionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CreatedIssueContribution
+}
+
+"""
+Represents either a issue the viewer can access or a restricted contribution.
+"""
+union CreatedIssueOrRestrictedContribution = CreatedIssueContribution | RestrictedContribution
+
+"""
+Represents the contribution a user made on GitHub by opening a pull request.
+"""
+type CreatedPullRequestContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The pull request that was opened.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+The connection type for CreatedPullRequestContribution.
+"""
+type CreatedPullRequestContributionConnection {
+ """
+ A list of edges.
+ """
+ edges: [CreatedPullRequestContributionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CreatedPullRequestContribution]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CreatedPullRequestContributionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CreatedPullRequestContribution
+}
+
+"""
+Represents either a pull request the viewer can access or a restricted contribution.
+"""
+union CreatedPullRequestOrRestrictedContribution = CreatedPullRequestContribution | RestrictedContribution
+
+"""
+Represents the contribution a user made by leaving a review on a pull request.
+"""
+type CreatedPullRequestReviewContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The pull request the user reviewed.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The review the user left on the pull request.
+ """
+ pullRequestReview: PullRequestReview!
+
+ """
+ The repository containing the pull request that the user reviewed.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+The connection type for CreatedPullRequestReviewContribution.
+"""
+type CreatedPullRequestReviewContributionConnection {
+ """
+ A list of edges.
+ """
+ edges: [CreatedPullRequestReviewContributionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CreatedPullRequestReviewContribution]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CreatedPullRequestReviewContributionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CreatedPullRequestReviewContribution
+}
+
+"""
+Represents the contribution a user made on GitHub by creating a repository.
+"""
+type CreatedRepositoryContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The repository that was created.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+The connection type for CreatedRepositoryContribution.
+"""
+type CreatedRepositoryContributionConnection {
+ """
+ A list of edges.
+ """
+ edges: [CreatedRepositoryContributionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CreatedRepositoryContribution]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type CreatedRepositoryContributionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CreatedRepositoryContribution
+}
+
+"""
+Represents either a repository the viewer can access or a restricted contribution.
+"""
+union CreatedRepositoryOrRestrictedContribution = CreatedRepositoryContribution | RestrictedContribution
+
+"""
+Represents a mention made by one issue or pull request to another.
+"""
+type CrossReferencedEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Reference originated in a different repository.
+ """
+ isCrossRepository: Boolean!
+
+ """
+ Identifies when the reference was made.
+ """
+ referencedAt: DateTime!
+
+ """
+ The HTTP path for this pull request.
+ """
+ resourcePath: URI!
+
+ """
+ Issue or pull request that made the reference.
+ """
+ source: ReferencedSubject!
+
+ """
+ Issue or pull request to which the reference was made.
+ """
+ target: ReferencedSubject!
+
+ """
+ The HTTP URL for this pull request.
+ """
+ url: URI!
+
+ """
+ Checks if the target will be closed when the source is merged.
+ """
+ willCloseTarget: Boolean!
+}
+
+"""
+An ISO-8601 encoded date string.
+"""
+scalar Date
+
+"""
+An ISO-8601 encoded UTC date string.
+"""
+scalar DateTime
+
+"""
+Autogenerated input type of DeclineTopicSuggestion
+"""
+input DeclineTopicSuggestionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of the suggested topic.
+ """
+ name: String!
+
+ """
+ The reason why the suggested topic is declined.
+ """
+ reason: TopicSuggestionDeclineReason!
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of DeclineTopicSuggestion
+"""
+type DeclineTopicSuggestionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The declined topic.
+ """
+ topic: Topic
+}
+
+"""
+The possible default permissions for repositories.
+"""
+enum DefaultRepositoryPermissionField {
+ """
+ Can read, write, and administrate repos by default
+ """
+ ADMIN
+
+ """
+ No access
+ """
+ NONE
+
+ """
+ Can read repos by default
+ """
+ READ
+
+ """
+ Can read and write repos by default
+ """
+ WRITE
+}
+
+"""
+Entities that can be deleted.
+"""
+interface Deletable {
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+}
+
+"""
+Autogenerated input type of DeleteBranchProtectionRule
+"""
+input DeleteBranchProtectionRuleInput {
+ """
+ The global relay id of the branch protection rule to be deleted.
+ """
+ branchProtectionRuleId: ID! @possibleTypes(concreteTypes: ["BranchProtectionRule"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated return type of DeleteBranchProtectionRule
+"""
+type DeleteBranchProtectionRulePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteDeployment
+"""
+input DeleteDeploymentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the deployment to be deleted.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["Deployment"])
+}
+
+"""
+Autogenerated return type of DeleteDeployment
+"""
+type DeleteDeploymentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteIpAllowListEntry
+"""
+input DeleteIpAllowListEntryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the IP allow list entry to delete.
+ """
+ ipAllowListEntryId: ID! @possibleTypes(concreteTypes: ["IpAllowListEntry"])
+}
+
+"""
+Autogenerated return type of DeleteIpAllowListEntry
+"""
+type DeleteIpAllowListEntryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The IP allow list entry that was deleted.
+ """
+ ipAllowListEntry: IpAllowListEntry
+}
+
+"""
+Autogenerated input type of DeleteIssueComment
+"""
+input DeleteIssueCommentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the comment to delete.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["IssueComment"])
+}
+
+"""
+Autogenerated return type of DeleteIssueComment
+"""
+type DeleteIssueCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteIssue
+"""
+input DeleteIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the issue to delete.
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+}
+
+"""
+Autogenerated return type of DeleteIssue
+"""
+type DeleteIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The repository the issue belonged to
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of DeleteLabel
+"""
+input DeleteLabelInput @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the label to be deleted.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["Label"])
+}
+
+"""
+Autogenerated return type of DeleteLabel
+"""
+type DeleteLabelPayload @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeletePackageVersion
+"""
+input DeletePackageVersionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the package version to be deleted.
+ """
+ packageVersionId: ID! @possibleTypes(concreteTypes: ["PackageVersion"])
+}
+
+"""
+Autogenerated return type of DeletePackageVersion
+"""
+type DeletePackageVersionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Whether or not the operation succeeded.
+ """
+ success: Boolean
+}
+
+"""
+Autogenerated input type of DeleteProjectCard
+"""
+input DeleteProjectCardInput {
+ """
+ The id of the card to delete.
+ """
+ cardId: ID! @possibleTypes(concreteTypes: ["ProjectCard"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated return type of DeleteProjectCard
+"""
+type DeleteProjectCardPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The column the deleted card was in.
+ """
+ column: ProjectColumn
+
+ """
+ The deleted card ID.
+ """
+ deletedCardId: ID
+}
+
+"""
+Autogenerated input type of DeleteProjectColumn
+"""
+input DeleteProjectColumnInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the column to delete.
+ """
+ columnId: ID! @possibleTypes(concreteTypes: ["ProjectColumn"])
+}
+
+"""
+Autogenerated return type of DeleteProjectColumn
+"""
+type DeleteProjectColumnPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The deleted column ID.
+ """
+ deletedColumnId: ID
+
+ """
+ The project the deleted column was in.
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of DeleteProject
+"""
+input DeleteProjectInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Project ID to update.
+ """
+ projectId: ID! @possibleTypes(concreteTypes: ["Project"])
+}
+
+"""
+Autogenerated return type of DeleteProject
+"""
+type DeleteProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The repository or organization the project was removed from.
+ """
+ owner: ProjectOwner
+}
+
+"""
+Autogenerated input type of DeletePullRequestReviewComment
+"""
+input DeletePullRequestReviewCommentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the comment to delete.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["PullRequestReviewComment"])
+}
+
+"""
+Autogenerated return type of DeletePullRequestReviewComment
+"""
+type DeletePullRequestReviewCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request review the deleted comment belonged to.
+ """
+ pullRequestReview: PullRequestReview
+}
+
+"""
+Autogenerated input type of DeletePullRequestReview
+"""
+input DeletePullRequestReviewInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the pull request review to delete.
+ """
+ pullRequestReviewId: ID! @possibleTypes(concreteTypes: ["PullRequestReview"])
+}
+
+"""
+Autogenerated return type of DeletePullRequestReview
+"""
+type DeletePullRequestReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The deleted pull request review.
+ """
+ pullRequestReview: PullRequestReview
+}
+
+"""
+Autogenerated input type of DeleteRef
+"""
+input DeleteRefInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the Ref to be deleted.
+ """
+ refId: ID! @possibleTypes(concreteTypes: ["Ref"])
+}
+
+"""
+Autogenerated return type of DeleteRef
+"""
+type DeleteRefPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteTeamDiscussionComment
+"""
+input DeleteTeamDiscussionCommentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the comment to delete.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["TeamDiscussionComment"])
+}
+
+"""
+Autogenerated return type of DeleteTeamDiscussionComment
+"""
+type DeleteTeamDiscussionCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteTeamDiscussion
+"""
+input DeleteTeamDiscussionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The discussion ID to delete.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["TeamDiscussion"])
+}
+
+"""
+Autogenerated return type of DeleteTeamDiscussion
+"""
+type DeleteTeamDiscussionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of DeleteVerifiableDomain
+"""
+input DeleteVerifiableDomainInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the verifiable domain to delete.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["VerifiableDomain"])
+}
+
+"""
+Autogenerated return type of DeleteVerifiableDomain
+"""
+type DeleteVerifiableDomainPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The owning account from which the domain was deleted.
+ """
+ owner: VerifiableDomainOwner
+}
+
+"""
+Represents a 'demilestoned' event on a given issue or pull request.
+"""
+type DemilestonedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the milestone title associated with the 'demilestoned' event.
+ """
+ milestoneTitle: String!
+
+ """
+ Object referenced by event.
+ """
+ subject: MilestoneItem!
+}
+
+"""
+A dependency manifest entry
+"""
+type DependencyGraphDependency @preview(toggledBy: "hawkgirl-preview") {
+ """
+ Does the dependency itself have dependencies?
+ """
+ hasDependencies: Boolean!
+
+ """
+ The dependency package manager
+ """
+ packageManager: String
+
+ """
+ The required package name
+ """
+ packageName: String!
+
+ """
+ The repository containing the package
+ """
+ repository: Repository
+
+ """
+ The dependency version requirements
+ """
+ requirements: String!
+}
+
+"""
+The connection type for DependencyGraphDependency.
+"""
+type DependencyGraphDependencyConnection @preview(toggledBy: "hawkgirl-preview") {
+ """
+ A list of edges.
+ """
+ edges: [DependencyGraphDependencyEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [DependencyGraphDependency]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type DependencyGraphDependencyEdge @preview(toggledBy: "hawkgirl-preview") {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: DependencyGraphDependency
+}
+
+"""
+Dependency manifest for a repository
+"""
+type DependencyGraphManifest implements Node @preview(toggledBy: "hawkgirl-preview") {
+ """
+ Path to view the manifest file blob
+ """
+ blobPath: String!
+
+ """
+ A list of manifest dependencies
+ """
+ dependencies(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): DependencyGraphDependencyConnection
+
+ """
+ The number of dependencies listed in the manifest
+ """
+ dependenciesCount: Int
+
+ """
+ Is the manifest too big to parse?
+ """
+ exceedsMaxSize: Boolean!
+
+ """
+ Fully qualified manifest filename
+ """
+ filename: String!
+ id: ID!
+
+ """
+ Were we able to parse the manifest?
+ """
+ parseable: Boolean!
+
+ """
+ The repository containing the manifest
+ """
+ repository: Repository!
+}
+
+"""
+The connection type for DependencyGraphManifest.
+"""
+type DependencyGraphManifestConnection @preview(toggledBy: "hawkgirl-preview") {
+ """
+ A list of edges.
+ """
+ edges: [DependencyGraphManifestEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [DependencyGraphManifest]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type DependencyGraphManifestEdge @preview(toggledBy: "hawkgirl-preview") {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: DependencyGraphManifest
+}
+
+"""
+A repository deploy key.
+"""
+type DeployKey implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The deploy key.
+ """
+ key: String!
+
+ """
+ Whether or not the deploy key is read only.
+ """
+ readOnly: Boolean!
+
+ """
+ The deploy key title.
+ """
+ title: String!
+
+ """
+ Whether or not the deploy key has been verified.
+ """
+ verified: Boolean!
+}
+
+"""
+The connection type for DeployKey.
+"""
+type DeployKeyConnection {
+ """
+ A list of edges.
+ """
+ edges: [DeployKeyEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [DeployKey]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type DeployKeyEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: DeployKey
+}
+
+"""
+Represents a 'deployed' event on a given pull request.
+"""
+type DeployedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The deployment associated with the 'deployed' event.
+ """
+ deployment: Deployment!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The ref associated with the 'deployed' event.
+ """
+ ref: Ref
+}
+
+"""
+Represents triggered deployment instance.
+"""
+type Deployment implements Node {
+ """
+ Identifies the commit sha of the deployment.
+ """
+ commit: Commit
+
+ """
+ Identifies the oid of the deployment commit, even if the commit has been deleted.
+ """
+ commitOid: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the actor who triggered the deployment.
+ """
+ creator: Actor!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The deployment description.
+ """
+ description: String
+
+ """
+ The latest environment to which this deployment was made.
+ """
+ environment: String
+ id: ID!
+
+ """
+ The latest environment to which this deployment was made.
+ """
+ latestEnvironment: String
+
+ """
+ The latest status of this deployment.
+ """
+ latestStatus: DeploymentStatus
+
+ """
+ The original environment to which this deployment was made.
+ """
+ originalEnvironment: String
+
+ """
+ Extra information that a deployment system might need.
+ """
+ payload: String
+
+ """
+ Identifies the Ref of the deployment, if the deployment was created by ref.
+ """
+ ref: Ref
+
+ """
+ Identifies the repository associated with the deployment.
+ """
+ repository: Repository!
+
+ """
+ The current state of the deployment.
+ """
+ state: DeploymentState
+
+ """
+ A list of statuses associated with the deployment.
+ """
+ statuses(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): DeploymentStatusConnection
+
+ """
+ The deployment task.
+ """
+ task: String
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+The connection type for Deployment.
+"""
+type DeploymentConnection {
+ """
+ A list of edges.
+ """
+ edges: [DeploymentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Deployment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type DeploymentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Deployment
+}
+
+"""
+Represents a 'deployment_environment_changed' event on a given pull request.
+"""
+type DeploymentEnvironmentChangedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The deployment status that updated the deployment environment.
+ """
+ deploymentStatus: DeploymentStatus!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Ordering options for deployment connections
+"""
+input DeploymentOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order deployments by.
+ """
+ field: DeploymentOrderField!
+}
+
+"""
+Properties by which deployment connections can be ordered.
+"""
+enum DeploymentOrderField {
+ """
+ Order collection by creation time
+ """
+ CREATED_AT
+}
+
+"""
+The possible states in which a deployment can be.
+"""
+enum DeploymentState {
+ """
+ The pending deployment was not updated after 30 minutes.
+ """
+ ABANDONED
+
+ """
+ The deployment is currently active.
+ """
+ ACTIVE
+
+ """
+ An inactive transient deployment.
+ """
+ DESTROYED
+
+ """
+ The deployment experienced an error.
+ """
+ ERROR
+
+ """
+ The deployment has failed.
+ """
+ FAILURE
+
+ """
+ The deployment is inactive.
+ """
+ INACTIVE
+
+ """
+ The deployment is in progress.
+ """
+ IN_PROGRESS
+
+ """
+ The deployment is pending.
+ """
+ PENDING
+
+ """
+ The deployment has queued
+ """
+ QUEUED
+
+ """
+ The deployment is waiting.
+ """
+ WAITING
+}
+
+"""
+Describes the status of a given deployment attempt.
+"""
+type DeploymentStatus implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the actor who triggered the deployment.
+ """
+ creator: Actor!
+
+ """
+ Identifies the deployment associated with status.
+ """
+ deployment: Deployment!
+
+ """
+ Identifies the description of the deployment.
+ """
+ description: String
+
+ """
+ Identifies the environment of the deployment at the time of this deployment status
+ """
+ environment: String @preview(toggledBy: "flash-preview")
+
+ """
+ Identifies the environment URL of the deployment.
+ """
+ environmentUrl: URI
+ id: ID!
+
+ """
+ Identifies the log URL of the deployment.
+ """
+ logUrl: URI
+
+ """
+ Identifies the current state of the deployment.
+ """
+ state: DeploymentStatusState!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+The connection type for DeploymentStatus.
+"""
+type DeploymentStatusConnection {
+ """
+ A list of edges.
+ """
+ edges: [DeploymentStatusEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [DeploymentStatus]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type DeploymentStatusEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: DeploymentStatus
+}
+
+"""
+The possible states for a deployment status.
+"""
+enum DeploymentStatusState {
+ """
+ The deployment experienced an error.
+ """
+ ERROR
+
+ """
+ The deployment has failed.
+ """
+ FAILURE
+
+ """
+ The deployment is inactive.
+ """
+ INACTIVE
+
+ """
+ The deployment is in progress.
+ """
+ IN_PROGRESS
+
+ """
+ The deployment is pending.
+ """
+ PENDING
+
+ """
+ The deployment is queued
+ """
+ QUEUED
+
+ """
+ The deployment was successful.
+ """
+ SUCCESS
+
+ """
+ The deployment is waiting.
+ """
+ WAITING
+}
+
+"""
+The possible sides of a diff.
+"""
+enum DiffSide {
+ """
+ The left side of the diff.
+ """
+ LEFT
+
+ """
+ The right side of the diff.
+ """
+ RIGHT
+}
+
+"""
+Represents a 'disconnected' event on a given issue or pull request.
+"""
+type DisconnectedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Reference originated in a different repository.
+ """
+ isCrossRepository: Boolean!
+
+ """
+ Issue or pull request from which the issue was disconnected.
+ """
+ source: ReferencedSubject!
+
+ """
+ Issue or pull request which was disconnected.
+ """
+ subject: ReferencedSubject!
+}
+
+"""
+Autogenerated input type of DismissPullRequestReview
+"""
+input DismissPullRequestReviewInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The contents of the pull request review dismissal message.
+ """
+ message: String!
+
+ """
+ The Node ID of the pull request review to modify.
+ """
+ pullRequestReviewId: ID! @possibleTypes(concreteTypes: ["PullRequestReview"])
+}
+
+"""
+Autogenerated return type of DismissPullRequestReview
+"""
+type DismissPullRequestReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The dismissed pull request review.
+ """
+ pullRequestReview: PullRequestReview
+}
+
+"""
+Specifies a review comment to be left with a Pull Request Review.
+"""
+input DraftPullRequestReviewComment {
+ """
+ Body of the comment to leave.
+ """
+ body: String!
+
+ """
+ Path to the file being commented on.
+ """
+ path: String!
+
+ """
+ Position in the file to leave a comment on.
+ """
+ position: Int!
+}
+
+"""
+Specifies a review comment thread to be left with a Pull Request Review.
+"""
+input DraftPullRequestReviewThread {
+ """
+ Body of the comment to leave.
+ """
+ body: String!
+
+ """
+ The line of the blob to which the thread refers. The end of the line range for multi-line comments.
+ """
+ line: Int!
+
+ """
+ Path to the file being commented on.
+ """
+ path: String!
+
+ """
+ The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.
+ """
+ side: DiffSide = RIGHT
+
+ """
+ The first line of the range to which the comment refers.
+ """
+ startLine: Int
+
+ """
+ The side of the diff on which the start line resides.
+ """
+ startSide: DiffSide = RIGHT
+}
+
+"""
+An account to manage multiple organizations with consolidated policy and billing.
+"""
+type Enterprise implements Node {
+ """
+ A URL pointing to the enterprise's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ Enterprise billing information visible to enterprise billing managers.
+ """
+ billingInfo: EnterpriseBillingInfo
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The description of the enterprise.
+ """
+ description: String
+
+ """
+ The description of the enterprise as HTML.
+ """
+ descriptionHTML: HTML!
+ id: ID!
+
+ """
+ The location of the enterprise.
+ """
+ location: String
+
+ """
+ A list of users who are members of this enterprise.
+ """
+ members(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Only return members within the selected GitHub Enterprise deployment
+ """
+ deployment: EnterpriseUserDeployment
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for members returned from the connection.
+ """
+ orderBy: EnterpriseMemberOrder = {field: LOGIN, direction: ASC}
+
+ """
+ Only return members within the organizations with these logins
+ """
+ organizationLogins: [String!]
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ The role of the user in the enterprise organization or server.
+ """
+ role: EnterpriseUserAccountMembershipRole
+ ): EnterpriseMemberConnection!
+
+ """
+ The name of the enterprise.
+ """
+ name: String!
+
+ """
+ A list of organizations that belong to this enterprise.
+ """
+ organizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations returned from the connection.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+ ): OrganizationConnection!
+
+ """
+ Enterprise information only visible to enterprise owners.
+ """
+ ownerInfo: EnterpriseOwnerInfo
+
+ """
+ The HTTP path for this enterprise.
+ """
+ resourcePath: URI!
+
+ """
+ The URL-friendly identifier for the enterprise.
+ """
+ slug: String!
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ url: URI!
+
+ """
+ A list of user accounts on this enterprise.
+ """
+ userAccounts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): EnterpriseUserAccountConnection!
+
+ """
+ Is the current viewer an admin of this enterprise?
+ """
+ viewerIsAdmin: Boolean!
+
+ """
+ The URL of the enterprise website.
+ """
+ websiteUrl: URI
+}
+
+"""
+The connection type for User.
+"""
+type EnterpriseAdministratorConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseAdministratorEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A User who is an administrator of an enterprise.
+"""
+type EnterpriseAdministratorEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: User
+
+ """
+ The role of the administrator.
+ """
+ role: EnterpriseAdministratorRole!
+}
+
+"""
+An invitation for a user to become an owner or billing manager of an enterprise.
+"""
+type EnterpriseAdministratorInvitation implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The email of the person who was invited to the enterprise.
+ """
+ email: String
+
+ """
+ The enterprise the invitation is for.
+ """
+ enterprise: Enterprise!
+ id: ID!
+
+ """
+ The user who was invited to the enterprise.
+ """
+ invitee: User
+
+ """
+ The user who created the invitation.
+ """
+ inviter: User
+
+ """
+ The invitee's pending role in the enterprise (owner or billing_manager).
+ """
+ role: EnterpriseAdministratorRole!
+}
+
+"""
+The connection type for EnterpriseAdministratorInvitation.
+"""
+type EnterpriseAdministratorInvitationConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseAdministratorInvitationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseAdministratorInvitation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseAdministratorInvitationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseAdministratorInvitation
+}
+
+"""
+Ordering options for enterprise administrator invitation connections
+"""
+input EnterpriseAdministratorInvitationOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order enterprise administrator invitations by.
+ """
+ field: EnterpriseAdministratorInvitationOrderField!
+}
+
+"""
+Properties by which enterprise administrator invitation connections can be ordered.
+"""
+enum EnterpriseAdministratorInvitationOrderField {
+ """
+ Order enterprise administrator member invitations by creation time
+ """
+ CREATED_AT
+}
+
+"""
+The possible administrator roles in an enterprise account.
+"""
+enum EnterpriseAdministratorRole {
+ """
+ Represents a billing manager of the enterprise account.
+ """
+ BILLING_MANAGER
+
+ """
+ Represents an owner of the enterprise account.
+ """
+ OWNER
+}
+
+"""
+Metadata for an audit entry containing enterprise account information.
+"""
+interface EnterpriseAuditEntryData {
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+}
+
+"""
+Enterprise billing information visible to enterprise billing managers and owners.
+"""
+type EnterpriseBillingInfo {
+ """
+ The number of licenseable users/emails across the enterprise.
+ """
+ allLicensableUsersCount: Int!
+
+ """
+ The number of data packs used by all organizations owned by the enterprise.
+ """
+ assetPacks: Int!
+
+ """
+ The number of available seats across all owned organizations based on the unique number of billable users.
+ """
+ availableSeats: Int! @deprecated(reason: "`availableSeats` will be replaced with `totalAvailableLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalAvailableLicenses instead. Removal on 2020-01-01 UTC.")
+
+ """
+ The bandwidth quota in GB for all organizations owned by the enterprise.
+ """
+ bandwidthQuota: Float!
+
+ """
+ The bandwidth usage in GB for all organizations owned by the enterprise.
+ """
+ bandwidthUsage: Float!
+
+ """
+ The bandwidth usage as a percentage of the bandwidth quota.
+ """
+ bandwidthUsagePercentage: Int!
+
+ """
+ The total seats across all organizations owned by the enterprise.
+ """
+ seats: Int! @deprecated(reason: "`seats` will be replaced with `totalLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalLicenses instead. Removal on 2020-01-01 UTC.")
+
+ """
+ The storage quota in GB for all organizations owned by the enterprise.
+ """
+ storageQuota: Float!
+
+ """
+ The storage usage in GB for all organizations owned by the enterprise.
+ """
+ storageUsage: Float!
+
+ """
+ The storage usage as a percentage of the storage quota.
+ """
+ storageUsagePercentage: Int!
+
+ """
+ The number of available licenses across all owned organizations based on the unique number of billable users.
+ """
+ totalAvailableLicenses: Int!
+
+ """
+ The total number of licenses allocated.
+ """
+ totalLicenses: Int!
+}
+
+"""
+The possible values for the enterprise default repository permission setting.
+"""
+enum EnterpriseDefaultRepositoryPermissionSettingValue {
+ """
+ Organization members will be able to clone, pull, push, and add new collaborators to all organization repositories.
+ """
+ ADMIN
+
+ """
+ Organization members will only be able to clone and pull public repositories.
+ """
+ NONE
+
+ """
+ Organizations in the enterprise choose default repository permissions for their members.
+ """
+ NO_POLICY
+
+ """
+ Organization members will be able to clone and pull all organization repositories.
+ """
+ READ
+
+ """
+ Organization members will be able to clone, pull, and push all organization repositories.
+ """
+ WRITE
+}
+
+"""
+The possible values for an enabled/disabled enterprise setting.
+"""
+enum EnterpriseEnabledDisabledSettingValue {
+ """
+ The setting is disabled for organizations in the enterprise.
+ """
+ DISABLED
+
+ """
+ The setting is enabled for organizations in the enterprise.
+ """
+ ENABLED
+
+ """
+ There is no policy set for organizations in the enterprise.
+ """
+ NO_POLICY
+}
+
+"""
+The possible values for an enabled/no policy enterprise setting.
+"""
+enum EnterpriseEnabledSettingValue {
+ """
+ The setting is enabled for organizations in the enterprise.
+ """
+ ENABLED
+
+ """
+ There is no policy set for organizations in the enterprise.
+ """
+ NO_POLICY
+}
+
+"""
+An identity provider configured to provision identities for an enterprise.
+"""
+type EnterpriseIdentityProvider implements Node {
+ """
+ The digest algorithm used to sign SAML requests for the identity provider.
+ """
+ digestMethod: SamlDigestAlgorithm
+
+ """
+ The enterprise this identity provider belongs to.
+ """
+ enterprise: Enterprise
+
+ """
+ ExternalIdentities provisioned by this identity provider.
+ """
+ externalIdentities(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ExternalIdentityConnection!
+ id: ID!
+
+ """
+ The x509 certificate used by the identity provider to sign assertions and responses.
+ """
+ idpCertificate: X509Certificate
+
+ """
+ The Issuer Entity ID for the SAML identity provider.
+ """
+ issuer: String
+
+ """
+ Recovery codes that can be used by admins to access the enterprise if the identity provider is unavailable.
+ """
+ recoveryCodes: [String!]
+
+ """
+ The signature algorithm used to sign SAML requests for the identity provider.
+ """
+ signatureMethod: SamlSignatureAlgorithm
+
+ """
+ The URL endpoint for the identity provider's SAML SSO.
+ """
+ ssoUrl: URI
+}
+
+"""
+An object that is a member of an enterprise.
+"""
+union EnterpriseMember = EnterpriseUserAccount | User
+
+"""
+The connection type for EnterpriseMember.
+"""
+type EnterpriseMemberConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseMemberEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseMember]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A User who is a member of an enterprise through one or more organizations.
+"""
+type EnterpriseMemberEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ Whether the user does not have a license for the enterprise.
+ """
+ isUnlicensed: Boolean! @deprecated(reason: "All members consume a license Removal on 2021-01-01 UTC.")
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseMember
+}
+
+"""
+Ordering options for enterprise member connections.
+"""
+input EnterpriseMemberOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order enterprise members by.
+ """
+ field: EnterpriseMemberOrderField!
+}
+
+"""
+Properties by which enterprise member connections can be ordered.
+"""
+enum EnterpriseMemberOrderField {
+ """
+ Order enterprise members by creation time
+ """
+ CREATED_AT
+
+ """
+ Order enterprise members by login
+ """
+ LOGIN
+}
+
+"""
+The possible values for the enterprise members can create repositories setting.
+"""
+enum EnterpriseMembersCanCreateRepositoriesSettingValue {
+ """
+ Members will be able to create public and private repositories.
+ """
+ ALL
+
+ """
+ Members will not be able to create public or private repositories.
+ """
+ DISABLED
+
+ """
+ Organization administrators choose whether to allow members to create repositories.
+ """
+ NO_POLICY
+
+ """
+ Members will be able to create only private repositories.
+ """
+ PRIVATE
+
+ """
+ Members will be able to create only public repositories.
+ """
+ PUBLIC
+}
+
+"""
+The possible values for the members can make purchases setting.
+"""
+enum EnterpriseMembersCanMakePurchasesSettingValue {
+ """
+ The setting is disabled for organizations in the enterprise.
+ """
+ DISABLED
+
+ """
+ The setting is enabled for organizations in the enterprise.
+ """
+ ENABLED
+}
+
+"""
+The connection type for Organization.
+"""
+type EnterpriseOrganizationMembershipConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseOrganizationMembershipEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Organization]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An enterprise organization that a user is a member of.
+"""
+type EnterpriseOrganizationMembershipEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Organization
+
+ """
+ The role of the user in the enterprise membership.
+ """
+ role: EnterpriseUserAccountMembershipRole!
+}
+
+"""
+The connection type for User.
+"""
+type EnterpriseOutsideCollaboratorConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseOutsideCollaboratorEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A User who is an outside collaborator of an enterprise through one or more organizations.
+"""
+type EnterpriseOutsideCollaboratorEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ Whether the outside collaborator does not have a license for the enterprise.
+ """
+ isUnlicensed: Boolean! @deprecated(reason: "All outside collaborators consume a license Removal on 2021-01-01 UTC.")
+
+ """
+ The item at the end of the edge.
+ """
+ node: User
+
+ """
+ The enterprise organization repositories this user is a member of.
+ """
+ repositories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories.
+ """
+ orderBy: RepositoryOrder = {field: NAME, direction: ASC}
+ ): EnterpriseRepositoryInfoConnection!
+}
+
+"""
+Enterprise information only visible to enterprise owners.
+"""
+type EnterpriseOwnerInfo {
+ """
+ A list of all of the administrators for this enterprise.
+ """
+ admins(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for administrators returned from the connection.
+ """
+ orderBy: EnterpriseMemberOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ The role to filter by.
+ """
+ role: EnterpriseAdministratorRole
+ ): EnterpriseAdministratorConnection!
+
+ """
+ A list of users in the enterprise who currently have two-factor authentication disabled.
+ """
+ affiliatedUsersWithTwoFactorDisabled(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ Whether or not affiliated users with two-factor authentication disabled exist in the enterprise.
+ """
+ affiliatedUsersWithTwoFactorDisabledExist: Boolean!
+
+ """
+ The setting value for whether private repository forking is enabled for repositories in organizations in this enterprise.
+ """
+ allowPrivateRepositoryForkingSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided private repository forking setting value.
+ """
+ allowPrivateRepositoryForkingSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for base repository permissions for organizations in this enterprise.
+ """
+ defaultRepositoryPermissionSetting: EnterpriseDefaultRepositoryPermissionSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided default repository permission.
+ """
+ defaultRepositoryPermissionSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The permission to find organizations for.
+ """
+ value: DefaultRepositoryPermissionField!
+ ): OrganizationConnection!
+
+ """
+ A list of domains owned by the enterprise.
+ """
+ domains(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Filter whether or not the domain is verified.
+ """
+ isVerified: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for verifiable domains returned.
+ """
+ orderBy: VerifiableDomainOrder = {field: DOMAIN, direction: ASC}
+ ): VerifiableDomainConnection!
+
+ """
+ Enterprise Server installations owned by the enterprise.
+ """
+ enterpriseServerInstallations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Whether or not to only return installations discovered via GitHub Connect.
+ """
+ connectedOnly: Boolean = false
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for Enterprise Server installations returned.
+ """
+ orderBy: EnterpriseServerInstallationOrder = {field: HOST_NAME, direction: ASC}
+ ): EnterpriseServerInstallationConnection!
+
+ """
+ The setting value for whether the enterprise has an IP allow list enabled.
+ """
+ ipAllowListEnabledSetting: IpAllowListEnabledSettingValue!
+
+ """
+ The IP addresses that are allowed to access resources owned by the enterprise.
+ """
+ ipAllowListEntries(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for IP allow list entries returned.
+ """
+ orderBy: IpAllowListEntryOrder = {field: ALLOW_LIST_VALUE, direction: ASC}
+ ): IpAllowListEntryConnection!
+
+ """
+ Whether or not the default repository permission is currently being updated.
+ """
+ isUpdatingDefaultRepositoryPermission: Boolean!
+
+ """
+ Whether the two-factor authentication requirement is currently being enforced.
+ """
+ isUpdatingTwoFactorRequirement: Boolean!
+
+ """
+ The setting value for whether organization members with admin permissions on a
+ repository can change repository visibility.
+ """
+ membersCanChangeRepositoryVisibilitySetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided can change repository visibility setting value.
+ """
+ membersCanChangeRepositoryVisibilitySettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether members of organizations in the enterprise can create internal repositories.
+ """
+ membersCanCreateInternalRepositoriesSetting: Boolean
+
+ """
+ The setting value for whether members of organizations in the enterprise can create private repositories.
+ """
+ membersCanCreatePrivateRepositoriesSetting: Boolean
+
+ """
+ The setting value for whether members of organizations in the enterprise can create public repositories.
+ """
+ membersCanCreatePublicRepositoriesSetting: Boolean
+
+ """
+ The setting value for whether members of organizations in the enterprise can create repositories.
+ """
+ membersCanCreateRepositoriesSetting: EnterpriseMembersCanCreateRepositoriesSettingValue
+
+ """
+ A list of enterprise organizations configured with the provided repository creation setting value.
+ """
+ membersCanCreateRepositoriesSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting to find organizations for.
+ """
+ value: OrganizationMembersCanCreateRepositoriesSettingValue!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether members with admin permissions for repositories can delete issues.
+ """
+ membersCanDeleteIssuesSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided members can delete issues setting value.
+ """
+ membersCanDeleteIssuesSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether members with admin permissions for repositories can delete or transfer repositories.
+ """
+ membersCanDeleteRepositoriesSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided members can delete repositories setting value.
+ """
+ membersCanDeleteRepositoriesSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether members of organizations in the enterprise can invite outside collaborators.
+ """
+ membersCanInviteCollaboratorsSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided members can invite collaborators setting value.
+ """
+ membersCanInviteCollaboratorsSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ Indicates whether members of this enterprise's organizations can purchase additional services for those organizations.
+ """
+ membersCanMakePurchasesSetting: EnterpriseMembersCanMakePurchasesSettingValue!
+
+ """
+ The setting value for whether members with admin permissions for repositories can update protected branches.
+ """
+ membersCanUpdateProtectedBranchesSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided members can update protected branches setting value.
+ """
+ membersCanUpdateProtectedBranchesSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether members can view dependency insights.
+ """
+ membersCanViewDependencyInsightsSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided members can view dependency insights setting value.
+ """
+ membersCanViewDependencyInsightsSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether organization projects are enabled for organizations in this enterprise.
+ """
+ organizationProjectsSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided organization projects setting value.
+ """
+ organizationProjectsSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ A list of outside collaborators across the repositories in the enterprise.
+ """
+ outsideCollaborators(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ The login of one specific outside collaborator.
+ """
+ login: String
+
+ """
+ Ordering options for outside collaborators returned from the connection.
+ """
+ orderBy: EnterpriseMemberOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ Only return outside collaborators on repositories with this visibility.
+ """
+ visibility: RepositoryVisibility
+ ): EnterpriseOutsideCollaboratorConnection!
+
+ """
+ A list of pending administrator invitations for the enterprise.
+ """
+ pendingAdminInvitations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pending enterprise administrator invitations returned from the connection.
+ """
+ orderBy: EnterpriseAdministratorInvitationOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ The role to filter by.
+ """
+ role: EnterpriseAdministratorRole
+ ): EnterpriseAdministratorInvitationConnection!
+
+ """
+ A list of pending collaborator invitations across the repositories in the enterprise.
+ """
+ pendingCollaboratorInvitations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pending repository collaborator invitations returned from the connection.
+ """
+ orderBy: RepositoryInvitationOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+ ): RepositoryInvitationConnection!
+
+ """
+ A list of pending collaborators across the repositories in the enterprise.
+ """
+ pendingCollaborators(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pending repository collaborator invitations returned from the connection.
+ """
+ orderBy: RepositoryInvitationOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+ ): EnterprisePendingCollaboratorConnection! @deprecated(reason: "Repository invitations can now be associated with an email, not only an invitee. Use the `pendingCollaboratorInvitations` field instead. Removal on 2020-10-01 UTC.")
+
+ """
+ A list of pending member invitations for organizations in the enterprise.
+ """
+ pendingMemberInvitations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ The search string to look for.
+ """
+ query: String
+ ): EnterprisePendingMemberInvitationConnection!
+
+ """
+ The setting value for whether repository projects are enabled in this enterprise.
+ """
+ repositoryProjectsSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided repository projects setting value.
+ """
+ repositoryProjectsSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The SAML Identity Provider for the enterprise.
+ """
+ samlIdentityProvider: EnterpriseIdentityProvider
+
+ """
+ A list of enterprise organizations configured with the SAML single sign-on setting value.
+ """
+ samlIdentityProviderSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: IdentityProviderConfigurationState!
+ ): OrganizationConnection!
+
+ """
+ A list of members with a support entitlement.
+ """
+ supportEntitlements(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for support entitlement users returned from the connection.
+ """
+ orderBy: EnterpriseMemberOrder = {field: LOGIN, direction: ASC}
+ ): EnterpriseMemberConnection!
+
+ """
+ The setting value for whether team discussions are enabled for organizations in this enterprise.
+ """
+ teamDiscussionsSetting: EnterpriseEnabledDisabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the provided team discussions setting value.
+ """
+ teamDiscussionsSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+
+ """
+ The setting value for whether the enterprise requires two-factor authentication for its organizations and users.
+ """
+ twoFactorRequiredSetting: EnterpriseEnabledSettingValue!
+
+ """
+ A list of enterprise organizations configured with the two-factor authentication setting value.
+ """
+ twoFactorRequiredSettingOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations with this setting.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The setting value to find organizations for.
+ """
+ value: Boolean!
+ ): OrganizationConnection!
+}
+
+"""
+The connection type for User.
+"""
+type EnterprisePendingCollaboratorConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterprisePendingCollaboratorEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A user with an invitation to be a collaborator on a repository owned by an organization in an enterprise.
+"""
+type EnterprisePendingCollaboratorEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ Whether the invited collaborator does not have a license for the enterprise.
+ """
+ isUnlicensed: Boolean! @deprecated(reason: "All pending collaborators consume a license Removal on 2021-01-01 UTC.")
+
+ """
+ The item at the end of the edge.
+ """
+ node: User
+
+ """
+ The enterprise organization repositories this user is a member of.
+ """
+ repositories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories.
+ """
+ orderBy: RepositoryOrder = {field: NAME, direction: ASC}
+ ): EnterpriseRepositoryInfoConnection!
+}
+
+"""
+The connection type for OrganizationInvitation.
+"""
+type EnterprisePendingMemberInvitationConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterprisePendingMemberInvitationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [OrganizationInvitation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ Identifies the total count of unique users in the connection.
+ """
+ totalUniqueUserCount: Int!
+}
+
+"""
+An invitation to be a member in an enterprise organization.
+"""
+type EnterprisePendingMemberInvitationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ Whether the invitation has a license for the enterprise.
+ """
+ isUnlicensed: Boolean! @deprecated(reason: "All pending members consume a license Removal on 2020-07-01 UTC.")
+
+ """
+ The item at the end of the edge.
+ """
+ node: OrganizationInvitation
+}
+
+"""
+A subset of repository information queryable from an enterprise.
+"""
+type EnterpriseRepositoryInfo implements Node {
+ id: ID!
+
+ """
+ Identifies if the repository is private.
+ """
+ isPrivate: Boolean!
+
+ """
+ The repository's name.
+ """
+ name: String!
+
+ """
+ The repository's name with owner.
+ """
+ nameWithOwner: String!
+}
+
+"""
+The connection type for EnterpriseRepositoryInfo.
+"""
+type EnterpriseRepositoryInfoConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseRepositoryInfoEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseRepositoryInfo]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseRepositoryInfoEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseRepositoryInfo
+}
+
+"""
+An Enterprise Server installation.
+"""
+type EnterpriseServerInstallation implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The customer name to which the Enterprise Server installation belongs.
+ """
+ customerName: String!
+
+ """
+ The host name of the Enterprise Server installation.
+ """
+ hostName: String!
+ id: ID!
+
+ """
+ Whether or not the installation is connected to an Enterprise Server installation via GitHub Connect.
+ """
+ isConnected: Boolean!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ User accounts on this Enterprise Server installation.
+ """
+ userAccounts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for Enterprise Server user accounts returned from the connection.
+ """
+ orderBy: EnterpriseServerUserAccountOrder = {field: LOGIN, direction: ASC}
+ ): EnterpriseServerUserAccountConnection!
+
+ """
+ User accounts uploads for the Enterprise Server installation.
+ """
+ userAccountsUploads(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for Enterprise Server user accounts uploads returned from the connection.
+ """
+ orderBy: EnterpriseServerUserAccountsUploadOrder = {field: CREATED_AT, direction: DESC}
+ ): EnterpriseServerUserAccountsUploadConnection!
+}
+
+"""
+The connection type for EnterpriseServerInstallation.
+"""
+type EnterpriseServerInstallationConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseServerInstallationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseServerInstallation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseServerInstallationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseServerInstallation
+}
+
+"""
+Ordering options for Enterprise Server installation connections.
+"""
+input EnterpriseServerInstallationOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order Enterprise Server installations by.
+ """
+ field: EnterpriseServerInstallationOrderField!
+}
+
+"""
+Properties by which Enterprise Server installation connections can be ordered.
+"""
+enum EnterpriseServerInstallationOrderField {
+ """
+ Order Enterprise Server installations by creation time
+ """
+ CREATED_AT
+
+ """
+ Order Enterprise Server installations by customer name
+ """
+ CUSTOMER_NAME
+
+ """
+ Order Enterprise Server installations by host name
+ """
+ HOST_NAME
+}
+
+"""
+A user account on an Enterprise Server installation.
+"""
+type EnterpriseServerUserAccount implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ User emails belonging to this user account.
+ """
+ emails(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for Enterprise Server user account emails returned from the connection.
+ """
+ orderBy: EnterpriseServerUserAccountEmailOrder = {field: EMAIL, direction: ASC}
+ ): EnterpriseServerUserAccountEmailConnection!
+
+ """
+ The Enterprise Server installation on which this user account exists.
+ """
+ enterpriseServerInstallation: EnterpriseServerInstallation!
+ id: ID!
+
+ """
+ Whether the user account is a site administrator on the Enterprise Server installation.
+ """
+ isSiteAdmin: Boolean!
+
+ """
+ The login of the user account on the Enterprise Server installation.
+ """
+ login: String!
+
+ """
+ The profile name of the user account on the Enterprise Server installation.
+ """
+ profileName: String
+
+ """
+ The date and time when the user account was created on the Enterprise Server installation.
+ """
+ remoteCreatedAt: DateTime!
+
+ """
+ The ID of the user account on the Enterprise Server installation.
+ """
+ remoteUserId: Int!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+The connection type for EnterpriseServerUserAccount.
+"""
+type EnterpriseServerUserAccountConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseServerUserAccountEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseServerUserAccount]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseServerUserAccountEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseServerUserAccount
+}
+
+"""
+An email belonging to a user account on an Enterprise Server installation.
+"""
+type EnterpriseServerUserAccountEmail implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The email address.
+ """
+ email: String!
+ id: ID!
+
+ """
+ Indicates whether this is the primary email of the associated user account.
+ """
+ isPrimary: Boolean!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The user account to which the email belongs.
+ """
+ userAccount: EnterpriseServerUserAccount!
+}
+
+"""
+The connection type for EnterpriseServerUserAccountEmail.
+"""
+type EnterpriseServerUserAccountEmailConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseServerUserAccountEmailEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseServerUserAccountEmail]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseServerUserAccountEmailEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseServerUserAccountEmail
+}
+
+"""
+Ordering options for Enterprise Server user account email connections.
+"""
+input EnterpriseServerUserAccountEmailOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order emails by.
+ """
+ field: EnterpriseServerUserAccountEmailOrderField!
+}
+
+"""
+Properties by which Enterprise Server user account email connections can be ordered.
+"""
+enum EnterpriseServerUserAccountEmailOrderField {
+ """
+ Order emails by email
+ """
+ EMAIL
+}
+
+"""
+Ordering options for Enterprise Server user account connections.
+"""
+input EnterpriseServerUserAccountOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order user accounts by.
+ """
+ field: EnterpriseServerUserAccountOrderField!
+}
+
+"""
+Properties by which Enterprise Server user account connections can be ordered.
+"""
+enum EnterpriseServerUserAccountOrderField {
+ """
+ Order user accounts by login
+ """
+ LOGIN
+
+ """
+ Order user accounts by creation time on the Enterprise Server installation
+ """
+ REMOTE_CREATED_AT
+}
+
+"""
+A user accounts upload from an Enterprise Server installation.
+"""
+type EnterpriseServerUserAccountsUpload implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The enterprise to which this upload belongs.
+ """
+ enterprise: Enterprise!
+
+ """
+ The Enterprise Server installation for which this upload was generated.
+ """
+ enterpriseServerInstallation: EnterpriseServerInstallation!
+ id: ID!
+
+ """
+ The name of the file uploaded.
+ """
+ name: String!
+
+ """
+ The synchronization state of the upload
+ """
+ syncState: EnterpriseServerUserAccountsUploadSyncState!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+The connection type for EnterpriseServerUserAccountsUpload.
+"""
+type EnterpriseServerUserAccountsUploadConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseServerUserAccountsUploadEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseServerUserAccountsUpload]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseServerUserAccountsUploadEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseServerUserAccountsUpload
+}
+
+"""
+Ordering options for Enterprise Server user accounts upload connections.
+"""
+input EnterpriseServerUserAccountsUploadOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order user accounts uploads by.
+ """
+ field: EnterpriseServerUserAccountsUploadOrderField!
+}
+
+"""
+Properties by which Enterprise Server user accounts upload connections can be ordered.
+"""
+enum EnterpriseServerUserAccountsUploadOrderField {
+ """
+ Order user accounts uploads by creation time
+ """
+ CREATED_AT
+}
+
+"""
+Synchronization state of the Enterprise Server user accounts upload
+"""
+enum EnterpriseServerUserAccountsUploadSyncState {
+ """
+ The synchronization of the upload failed.
+ """
+ FAILURE
+
+ """
+ The synchronization of the upload is pending.
+ """
+ PENDING
+
+ """
+ The synchronization of the upload succeeded.
+ """
+ SUCCESS
+}
+
+"""
+An account for a user who is an admin of an enterprise or a member of an enterprise through one or more organizations.
+"""
+type EnterpriseUserAccount implements Actor & Node {
+ """
+ A URL pointing to the enterprise user account's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The enterprise in which this user account exists.
+ """
+ enterprise: Enterprise!
+ id: ID!
+
+ """
+ An identifier for the enterprise user account, a login or email address
+ """
+ login: String!
+
+ """
+ The name of the enterprise user account
+ """
+ name: String
+
+ """
+ A list of enterprise organizations this user is a member of.
+ """
+ organizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for organizations returned from the connection.
+ """
+ orderBy: OrganizationOrder = {field: LOGIN, direction: ASC}
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ The role of the user in the enterprise organization.
+ """
+ role: EnterpriseUserAccountMembershipRole
+ ): EnterpriseOrganizationMembershipConnection!
+
+ """
+ The HTTP path for this user.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this user.
+ """
+ url: URI!
+
+ """
+ The user within the enterprise.
+ """
+ user: User
+}
+
+"""
+The connection type for EnterpriseUserAccount.
+"""
+type EnterpriseUserAccountConnection {
+ """
+ A list of edges.
+ """
+ edges: [EnterpriseUserAccountEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EnterpriseUserAccount]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type EnterpriseUserAccountEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EnterpriseUserAccount
+}
+
+"""
+The possible roles for enterprise membership.
+"""
+enum EnterpriseUserAccountMembershipRole {
+ """
+ The user is a member of the enterprise membership.
+ """
+ MEMBER
+
+ """
+ The user is an owner of the enterprise membership.
+ """
+ OWNER
+}
+
+"""
+The possible GitHub Enterprise deployments where this user can exist.
+"""
+enum EnterpriseUserDeployment {
+ """
+ The user is part of a GitHub Enterprise Cloud deployment.
+ """
+ CLOUD
+
+ """
+ The user is part of a GitHub Enterprise Server deployment.
+ """
+ SERVER
+}
+
+"""
+An external identity provisioned by SAML SSO or SCIM.
+"""
+type ExternalIdentity implements Node {
+ """
+ The GUID for this identity
+ """
+ guid: String!
+ id: ID!
+
+ """
+ Organization invitation for this SCIM-provisioned external identity
+ """
+ organizationInvitation: OrganizationInvitation
+
+ """
+ SAML Identity attributes
+ """
+ samlIdentity: ExternalIdentitySamlAttributes
+
+ """
+ SCIM Identity attributes
+ """
+ scimIdentity: ExternalIdentityScimAttributes
+
+ """
+ User linked to this external identity. Will be NULL if this identity has not been claimed by an organization member.
+ """
+ user: User
+}
+
+"""
+The connection type for ExternalIdentity.
+"""
+type ExternalIdentityConnection {
+ """
+ A list of edges.
+ """
+ edges: [ExternalIdentityEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ExternalIdentity]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ExternalIdentityEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ExternalIdentity
+}
+
+"""
+SAML attributes for the External Identity
+"""
+type ExternalIdentitySamlAttributes {
+ """
+ The emails associated with the SAML identity
+ """
+ emails: [UserEmailMetadata!]
+
+ """
+ Family name of the SAML identity
+ """
+ familyName: String
+
+ """
+ Given name of the SAML identity
+ """
+ givenName: String
+
+ """
+ The groups linked to this identity in IDP
+ """
+ groups: [String!]
+
+ """
+ The NameID of the SAML identity
+ """
+ nameId: String
+
+ """
+ The userName of the SAML identity
+ """
+ username: String
+}
+
+"""
+SCIM attributes for the External Identity
+"""
+type ExternalIdentityScimAttributes {
+ """
+ The emails associated with the SCIM identity
+ """
+ emails: [UserEmailMetadata!]
+
+ """
+ Family name of the SCIM identity
+ """
+ familyName: String
+
+ """
+ Given name of the SCIM identity
+ """
+ givenName: String
+
+ """
+ The groups linked to this identity in IDP
+ """
+ groups: [String!]
+
+ """
+ The userName of the SCIM identity
+ """
+ username: String
+}
+
+"""
+The possible viewed states of a file .
+"""
+enum FileViewedState {
+ """
+ The file has new changes since last viewed.
+ """
+ DISMISSED
+
+ """
+ The file has not been marked as viewed.
+ """
+ UNVIEWED
+
+ """
+ The file has been marked as viewed.
+ """
+ VIEWED
+}
+
+"""
+Autogenerated input type of FollowUser
+"""
+input FollowUserInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the user to follow.
+ """
+ userId: ID! @possibleTypes(concreteTypes: ["User"])
+}
+
+"""
+Autogenerated return type of FollowUser
+"""
+type FollowUserPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The user that was followed.
+ """
+ user: User
+}
+
+"""
+The connection type for User.
+"""
+type FollowerConnection {
+ """
+ A list of edges.
+ """
+ edges: [UserEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+The connection type for User.
+"""
+type FollowingConnection {
+ """
+ A list of edges.
+ """
+ edges: [UserEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A funding platform link for a repository.
+"""
+type FundingLink {
+ """
+ The funding platform this link is for.
+ """
+ platform: FundingPlatform!
+
+ """
+ The configured URL for this funding link.
+ """
+ url: URI!
+}
+
+"""
+The possible funding platforms for repository funding links.
+"""
+enum FundingPlatform {
+ """
+ Community Bridge funding platform.
+ """
+ COMMUNITY_BRIDGE
+
+ """
+ Custom funding platform.
+ """
+ CUSTOM
+
+ """
+ GitHub funding platform.
+ """
+ GITHUB
+
+ """
+ IssueHunt funding platform.
+ """
+ ISSUEHUNT
+
+ """
+ Ko-fi funding platform.
+ """
+ KO_FI
+
+ """
+ Liberapay funding platform.
+ """
+ LIBERAPAY
+
+ """
+ Open Collective funding platform.
+ """
+ OPEN_COLLECTIVE
+
+ """
+ Otechie funding platform.
+ """
+ OTECHIE
+
+ """
+ Patreon funding platform.
+ """
+ PATREON
+
+ """
+ Tidelift funding platform.
+ """
+ TIDELIFT
+}
+
+"""
+A generic hovercard context with a message and icon
+"""
+type GenericHovercardContext implements HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+}
+
+"""
+A Gist.
+"""
+type Gist implements Node & Starrable & UniformResourceLocatable {
+ """
+ A list of comments associated with the gist
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): GistCommentConnection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The gist description.
+ """
+ description: String
+
+ """
+ The files in this gist.
+ """
+ files(
+ """
+ The maximum number of files to return.
+ """
+ limit: Int = 10
+
+ """
+ The oid of the files to return
+ """
+ oid: GitObjectID
+ ): [GistFile]
+
+ """
+ A list of forks associated with the gist
+ """
+ forks(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for gists returned from the connection
+ """
+ orderBy: GistOrder
+ ): GistConnection!
+ id: ID!
+
+ """
+ Identifies if the gist is a fork.
+ """
+ isFork: Boolean!
+
+ """
+ Whether the gist is public or not.
+ """
+ isPublic: Boolean!
+
+ """
+ The gist name.
+ """
+ name: String!
+
+ """
+ The gist owner.
+ """
+ owner: RepositoryOwner
+
+ """
+ Identifies when the gist was last pushed to.
+ """
+ pushedAt: DateTime
+
+ """
+ The HTML path to this resource.
+ """
+ resourcePath: URI!
+
+ """
+ Returns a count of how many stargazers there are on this object
+ """
+ stargazerCount: Int!
+
+ """
+ A list of users who have starred this starrable.
+ """
+ stargazers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: StarOrder
+ ): StargazerConnection!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this Gist.
+ """
+ url: URI!
+
+ """
+ Returns a boolean indicating whether the viewing user has starred this starrable.
+ """
+ viewerHasStarred: Boolean!
+}
+
+"""
+Represents a comment on an Gist.
+"""
+type GistComment implements Comment & Deletable & Minimizable & Node & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the gist.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ Identifies the comment body.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+
+ """
+ The associated gist.
+ """
+ gist: Gist!
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Returns whether or not a comment has been minimized.
+ """
+ isMinimized: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Returns why the comment was minimized.
+ """
+ minimizedReason: String
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Check if the current viewer can minimize this object.
+ """
+ viewerCanMinimize: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+The connection type for GistComment.
+"""
+type GistCommentConnection {
+ """
+ A list of edges.
+ """
+ edges: [GistCommentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [GistComment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type GistCommentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: GistComment
+}
+
+"""
+The connection type for Gist.
+"""
+type GistConnection {
+ """
+ A list of edges.
+ """
+ edges: [GistEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Gist]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type GistEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Gist
+}
+
+"""
+A file in a gist.
+"""
+type GistFile {
+ """
+ The file name encoded to remove characters that are invalid in URL paths.
+ """
+ encodedName: String
+
+ """
+ The gist file encoding.
+ """
+ encoding: String
+
+ """
+ The file extension from the file name.
+ """
+ extension: String
+
+ """
+ Indicates if this file is an image.
+ """
+ isImage: Boolean!
+
+ """
+ Whether the file's contents were truncated.
+ """
+ isTruncated: Boolean!
+
+ """
+ The programming language this file is written in.
+ """
+ language: Language
+
+ """
+ The gist file name.
+ """
+ name: String
+
+ """
+ The gist file size in bytes.
+ """
+ size: Int
+
+ """
+ UTF8 text data or null if the file is binary
+ """
+ text(
+ """
+ Optionally truncate the returned file to this length.
+ """
+ truncate: Int
+ ): String
+}
+
+"""
+Ordering options for gist connections
+"""
+input GistOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order repositories by.
+ """
+ field: GistOrderField!
+}
+
+"""
+Properties by which gist connections can be ordered.
+"""
+enum GistOrderField {
+ """
+ Order gists by creation time
+ """
+ CREATED_AT
+
+ """
+ Order gists by push time
+ """
+ PUSHED_AT
+
+ """
+ Order gists by update time
+ """
+ UPDATED_AT
+}
+
+"""
+The privacy of a Gist
+"""
+enum GistPrivacy {
+ """
+ Gists that are public and secret
+ """
+ ALL
+
+ """
+ Public
+ """
+ PUBLIC
+
+ """
+ Secret
+ """
+ SECRET
+}
+
+"""
+Represents an actor in a Git commit (ie. an author or committer).
+"""
+type GitActor {
+ """
+ A URL pointing to the author's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ The timestamp of the Git action (authoring or committing).
+ """
+ date: GitTimestamp
+
+ """
+ The email in the Git commit.
+ """
+ email: String
+
+ """
+ The name in the Git commit.
+ """
+ name: String
+
+ """
+ The GitHub user corresponding to the email field. Null if no such user exists.
+ """
+ user: User
+}
+
+"""
+The connection type for GitActor.
+"""
+type GitActorConnection {
+ """
+ A list of edges.
+ """
+ edges: [GitActorEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [GitActor]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type GitActorEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: GitActor
+}
+
+"""
+Represents information about the GitHub instance.
+"""
+type GitHubMetadata {
+ """
+ Returns a String that's a SHA of `github-services`
+ """
+ gitHubServicesSha: GitObjectID!
+
+ """
+ IP addresses that users connect to for git operations
+ """
+ gitIpAddresses: [String!]
+
+ """
+ IP addresses that service hooks are sent from
+ """
+ hookIpAddresses: [String!]
+
+ """
+ IP addresses that the importer connects from
+ """
+ importerIpAddresses: [String!]
+
+ """
+ Whether or not users are verified
+ """
+ isPasswordAuthenticationVerifiable: Boolean!
+
+ """
+ IP addresses for GitHub Pages' A records
+ """
+ pagesIpAddresses: [String!]
+}
+
+"""
+Represents a Git object.
+"""
+interface GitObject {
+ """
+ An abbreviated version of the Git object ID
+ """
+ abbreviatedOid: String!
+
+ """
+ The HTTP path for this Git object
+ """
+ commitResourcePath: URI!
+
+ """
+ The HTTP URL for this Git object
+ """
+ commitUrl: URI!
+ id: ID!
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID!
+
+ """
+ The Repository the Git object belongs to
+ """
+ repository: Repository!
+}
+
+"""
+A Git object ID.
+"""
+scalar GitObjectID
+
+"""
+A fully qualified reference name (e.g. `refs/heads/master`).
+"""
+scalar GitRefname @preview(toggledBy: "update-refs-preview")
+
+"""
+Git SSH string
+"""
+scalar GitSSHRemote
+
+"""
+Information about a signature (GPG or S/MIME) on a Commit or Tag.
+"""
+interface GitSignature {
+ """
+ Email used to sign this object.
+ """
+ email: String!
+
+ """
+ True if the signature is valid and verified by GitHub.
+ """
+ isValid: Boolean!
+
+ """
+ Payload for GPG signing object. Raw ODB object without the signature header.
+ """
+ payload: String!
+
+ """
+ ASCII-armored signature header from object.
+ """
+ signature: String!
+
+ """
+ GitHub user corresponding to the email signing this commit.
+ """
+ signer: User
+
+ """
+ The state of this signature. `VALID` if signature is valid and verified by
+ GitHub, otherwise represents reason why signature is considered invalid.
+ """
+ state: GitSignatureState!
+
+ """
+ True if the signature was made with GitHub's signing key.
+ """
+ wasSignedByGitHub: Boolean!
+}
+
+"""
+The state of a Git signature.
+"""
+enum GitSignatureState {
+ """
+ The signing certificate or its chain could not be verified
+ """
+ BAD_CERT
+
+ """
+ Invalid email used for signing
+ """
+ BAD_EMAIL
+
+ """
+ Signing key expired
+ """
+ EXPIRED_KEY
+
+ """
+ Internal error - the GPG verification service misbehaved
+ """
+ GPGVERIFY_ERROR
+
+ """
+ Internal error - the GPG verification service is unavailable at the moment
+ """
+ GPGVERIFY_UNAVAILABLE
+
+ """
+ Invalid signature
+ """
+ INVALID
+
+ """
+ Malformed signature
+ """
+ MALFORMED_SIG
+
+ """
+ The usage flags for the key that signed this don't allow signing
+ """
+ NOT_SIGNING_KEY
+
+ """
+ Email used for signing not known to GitHub
+ """
+ NO_USER
+
+ """
+ Valid signature, though certificate revocation check failed
+ """
+ OCSP_ERROR
+
+ """
+ Valid signature, pending certificate revocation checking
+ """
+ OCSP_PENDING
+
+ """
+ One or more certificates in chain has been revoked
+ """
+ OCSP_REVOKED
+
+ """
+ Key used for signing not known to GitHub
+ """
+ UNKNOWN_KEY
+
+ """
+ Unknown signature type
+ """
+ UNKNOWN_SIG_TYPE
+
+ """
+ Unsigned
+ """
+ UNSIGNED
+
+ """
+ Email used for signing unverified on GitHub
+ """
+ UNVERIFIED_EMAIL
+
+ """
+ Valid signature and verified by GitHub
+ """
+ VALID
+}
+
+"""
+An ISO-8601 encoded date string. Unlike the DateTime type, GitTimestamp is not converted in UTC.
+"""
+scalar GitTimestamp
+
+"""
+Represents a GPG signature on a Commit or Tag.
+"""
+type GpgSignature implements GitSignature {
+ """
+ Email used to sign this object.
+ """
+ email: String!
+
+ """
+ True if the signature is valid and verified by GitHub.
+ """
+ isValid: Boolean!
+
+ """
+ Hex-encoded ID of the key that signed this object.
+ """
+ keyId: String
+
+ """
+ Payload for GPG signing object. Raw ODB object without the signature header.
+ """
+ payload: String!
+
+ """
+ ASCII-armored signature header from object.
+ """
+ signature: String!
+
+ """
+ GitHub user corresponding to the email signing this commit.
+ """
+ signer: User
+
+ """
+ The state of this signature. `VALID` if signature is valid and verified by
+ GitHub, otherwise represents reason why signature is considered invalid.
+ """
+ state: GitSignatureState!
+
+ """
+ True if the signature was made with GitHub's signing key.
+ """
+ wasSignedByGitHub: Boolean!
+}
+
+"""
+A string containing HTML code.
+"""
+scalar HTML
+
+"""
+Represents a 'head_ref_deleted' event on a given pull request.
+"""
+type HeadRefDeletedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the Ref associated with the `head_ref_deleted` event.
+ """
+ headRef: Ref
+
+ """
+ Identifies the name of the Ref associated with the `head_ref_deleted` event.
+ """
+ headRefName: String!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Represents a 'head_ref_force_pushed' event on a given pull request.
+"""
+type HeadRefForcePushedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the after commit SHA for the 'head_ref_force_pushed' event.
+ """
+ afterCommit: Commit
+
+ """
+ Identifies the before commit SHA for the 'head_ref_force_pushed' event.
+ """
+ beforeCommit: Commit
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the fully qualified ref name for the 'head_ref_force_pushed' event.
+ """
+ ref: Ref
+}
+
+"""
+Represents a 'head_ref_restored' event on a given pull request.
+"""
+type HeadRefRestoredEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+Detail needed to display a hovercard for a user
+"""
+type Hovercard {
+ """
+ Each of the contexts for this hovercard
+ """
+ contexts: [HovercardContext!]!
+}
+
+"""
+An individual line of a hovercard
+"""
+interface HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+}
+
+"""
+The possible states in which authentication can be configured with an identity provider.
+"""
+enum IdentityProviderConfigurationState {
+ """
+ Authentication with an identity provider is configured but not enforced.
+ """
+ CONFIGURED
+
+ """
+ Authentication with an identity provider is configured and enforced.
+ """
+ ENFORCED
+
+ """
+ Authentication with an identity provider is not configured.
+ """
+ UNCONFIGURED
+}
+
+"""
+Autogenerated input type of ImportProject
+"""
+input ImportProjectInput {
+ """
+ The description of Project.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A list of columns containing issues and pull requests.
+ """
+ columnImports: [ProjectColumnImport!]!
+
+ """
+ The name of Project.
+ """
+ name: String!
+
+ """
+ The name of the Organization or User to create the Project under.
+ """
+ ownerName: String!
+
+ """
+ Whether the Project is public or not.
+ """
+ public: Boolean = false
+}
+
+"""
+Autogenerated return type of ImportProject
+"""
+type ImportProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new Project!
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of InviteEnterpriseAdmin
+"""
+input InviteEnterpriseAdminInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The email of the person to invite as an administrator.
+ """
+ email: String
+
+ """
+ The ID of the enterprise to which you want to invite an administrator.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of a user to invite as an administrator.
+ """
+ invitee: String
+
+ """
+ The role of the administrator.
+ """
+ role: EnterpriseAdministratorRole
+}
+
+"""
+Autogenerated return type of InviteEnterpriseAdmin
+"""
+type InviteEnterpriseAdminPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The created enterprise administrator invitation.
+ """
+ invitation: EnterpriseAdministratorInvitation
+}
+
+"""
+The possible values for the IP allow list enabled setting.
+"""
+enum IpAllowListEnabledSettingValue {
+ """
+ The setting is disabled for the owner.
+ """
+ DISABLED
+
+ """
+ The setting is enabled for the owner.
+ """
+ ENABLED
+}
+
+"""
+An IP address or range of addresses that is allowed to access an owner's resources.
+"""
+type IpAllowListEntry implements Node {
+ """
+ A single IP address or range of IP addresses in CIDR notation.
+ """
+ allowListValue: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Whether the entry is currently active.
+ """
+ isActive: Boolean!
+
+ """
+ The name of the IP allow list entry.
+ """
+ name: String
+
+ """
+ The owner of the IP allow list entry.
+ """
+ owner: IpAllowListOwner!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+The connection type for IpAllowListEntry.
+"""
+type IpAllowListEntryConnection {
+ """
+ A list of edges.
+ """
+ edges: [IpAllowListEntryEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [IpAllowListEntry]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type IpAllowListEntryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: IpAllowListEntry
+}
+
+"""
+Ordering options for IP allow list entry connections.
+"""
+input IpAllowListEntryOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order IP allow list entries by.
+ """
+ field: IpAllowListEntryOrderField!
+}
+
+"""
+Properties by which IP allow list entry connections can be ordered.
+"""
+enum IpAllowListEntryOrderField {
+ """
+ Order IP allow list entries by the allow list value.
+ """
+ ALLOW_LIST_VALUE
+
+ """
+ Order IP allow list entries by creation time.
+ """
+ CREATED_AT
+}
+
+"""
+Types that can own an IP allow list.
+"""
+union IpAllowListOwner = Enterprise | Organization
+
+"""
+An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.
+"""
+type Issue implements Assignable & Closable & Comment & Labelable & Lockable & Node & Reactable & RepositoryNode & Subscribable & UniformResourceLocatable & Updatable & UpdatableComment {
+ """
+ Reason that the conversation was locked.
+ """
+ activeLockReason: LockReason
+
+ """
+ A list of Users assigned to this object.
+ """
+ assignees(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ Identifies the body of the issue.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The http path for this issue body
+ """
+ bodyResourcePath: URI!
+
+ """
+ Identifies the body of the issue rendered to text.
+ """
+ bodyText: String!
+
+ """
+ The http URL for this issue body
+ """
+ bodyUrl: URI!
+
+ """
+ `true` if the object is closed (definition of closed may depend on type)
+ """
+ closed: Boolean!
+
+ """
+ Identifies the date and time when the object was closed.
+ """
+ closedAt: DateTime
+
+ """
+ A list of comments associated with the Issue.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issue comments returned from the connection.
+ """
+ orderBy: IssueCommentOrder
+ ): IssueCommentConnection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+
+ """
+ The hovercard information for this issue
+ """
+ hovercard(
+ """
+ Whether or not to include notification contexts
+ """
+ includeNotificationContexts: Boolean = true
+ ): Hovercard!
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Is this issue read by the viewer
+ """
+ isReadByViewer: Boolean
+
+ """
+ A list of labels associated with the object.
+ """
+ labels(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for labels returned from the connection.
+ """
+ orderBy: LabelOrder = {field: CREATED_AT, direction: ASC}
+ ): LabelConnection
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ `true` if the object is locked
+ """
+ locked: Boolean!
+
+ """
+ Identifies the milestone associated with the issue.
+ """
+ milestone: Milestone
+
+ """
+ Identifies the issue number.
+ """
+ number: Int!
+
+ """
+ A list of Users that are participating in the Issue conversation.
+ """
+ participants(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ List of project cards associated with this issue.
+ """
+ projectCards(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ A list of archived states to filter the cards by
+ """
+ archivedStates: [ProjectCardArchivedState] = [ARCHIVED, NOT_ARCHIVED]
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ProjectCardConnection!
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this issue
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the state of the issue.
+ """
+ state: IssueState!
+
+ """
+ A list of events, comments, commits, etc. associated with the issue.
+ """
+ timeline(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows filtering timeline events by a `since` timestamp.
+ """
+ since: DateTime
+ ): IssueTimelineConnection! @deprecated(reason: "`timeline` will be removed Use Issue.timelineItems instead. Removal on 2020-10-01 UTC.")
+
+ """
+ A list of events, comments, commits, etc. associated with the issue.
+ """
+ timelineItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Filter timeline items by type.
+ """
+ itemTypes: [IssueTimelineItemsItemType!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter timeline items by a `since` timestamp.
+ """
+ since: DateTime
+
+ """
+ Skips the first _n_ elements in the list.
+ """
+ skip: Int
+ ): IssueTimelineItemsConnection!
+
+ """
+ Identifies the issue title.
+ """
+ title: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this issue
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+}
+
+"""
+Represents a comment on an Issue.
+"""
+type IssueComment implements Comment & Deletable & Minimizable & Node & Reactable & RepositoryNode & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ The body as Markdown.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Returns whether or not a comment has been minimized.
+ """
+ isMinimized: Boolean!
+
+ """
+ Identifies the issue associated with the comment.
+ """
+ issue: Issue!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Returns why the comment was minimized.
+ """
+ minimizedReason: String
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ Returns the pull request associated with the comment, if this comment was made on a
+ pull request.
+ """
+ pullRequest: PullRequest
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this issue comment
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this issue comment
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Check if the current viewer can minimize this object.
+ """
+ viewerCanMinimize: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+The connection type for IssueComment.
+"""
+type IssueCommentConnection {
+ """
+ A list of edges.
+ """
+ edges: [IssueCommentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [IssueComment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type IssueCommentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: IssueComment
+}
+
+"""
+Ways in which lists of issue comments can be ordered upon return.
+"""
+input IssueCommentOrder {
+ """
+ The direction in which to order issue comments by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order issue comments by.
+ """
+ field: IssueCommentOrderField!
+}
+
+"""
+Properties by which issue comment connections can be ordered.
+"""
+enum IssueCommentOrderField {
+ """
+ Order issue comments by update time
+ """
+ UPDATED_AT
+}
+
+"""
+The connection type for Issue.
+"""
+type IssueConnection {
+ """
+ A list of edges.
+ """
+ edges: [IssueEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Issue]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+This aggregates issues opened by a user within one repository.
+"""
+type IssueContributionsByRepository {
+ """
+ The issue contributions.
+ """
+ contributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedIssueContributionConnection!
+
+ """
+ The repository in which the issues were opened.
+ """
+ repository: Repository!
+}
+
+"""
+An edge in a connection.
+"""
+type IssueEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Issue
+}
+
+"""
+Ways in which to filter lists of issues.
+"""
+input IssueFilters {
+ """
+ List issues assigned to given name. Pass in `null` for issues with no assigned
+ user, and `*` for issues assigned to any user.
+ """
+ assignee: String
+
+ """
+ List issues created by given name.
+ """
+ createdBy: String
+
+ """
+ List issues where the list of label names exist on the issue.
+ """
+ labels: [String!]
+
+ """
+ List issues where the given name is mentioned in the issue.
+ """
+ mentioned: String
+
+ """
+ List issues by given milestone argument. If an string representation of an
+ integer is passed, it should refer to a milestone by its number field. Pass in
+ `null` for issues with no milestone, and `*` for issues that are assigned to any milestone.
+ """
+ milestone: String
+
+ """
+ List issues that have been updated at or after the given date.
+ """
+ since: DateTime
+
+ """
+ List issues filtered by the list of states given.
+ """
+ states: [IssueState!]
+
+ """
+ List issues subscribed to by viewer.
+ """
+ viewerSubscribed: Boolean = false
+}
+
+"""
+Used for return value of Repository.issueOrPullRequest.
+"""
+union IssueOrPullRequest = Issue | PullRequest
+
+"""
+Ways in which lists of issues can be ordered upon return.
+"""
+input IssueOrder {
+ """
+ The direction in which to order issues by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order issues by.
+ """
+ field: IssueOrderField!
+}
+
+"""
+Properties by which issue connections can be ordered.
+"""
+enum IssueOrderField {
+ """
+ Order issues by comment count
+ """
+ COMMENTS
+
+ """
+ Order issues by creation time
+ """
+ CREATED_AT
+
+ """
+ Order issues by update time
+ """
+ UPDATED_AT
+}
+
+"""
+The possible states of an issue.
+"""
+enum IssueState {
+ """
+ An issue that has been closed
+ """
+ CLOSED
+
+ """
+ An issue that is still open
+ """
+ OPEN
+}
+
+"""
+A repository issue template.
+"""
+type IssueTemplate {
+ """
+ The template purpose.
+ """
+ about: String
+
+ """
+ The suggested issue body.
+ """
+ body: String
+
+ """
+ The template name.
+ """
+ name: String!
+
+ """
+ The suggested issue title.
+ """
+ title: String
+}
+
+"""
+The connection type for IssueTimelineItem.
+"""
+type IssueTimelineConnection {
+ """
+ A list of edges.
+ """
+ edges: [IssueTimelineItemEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [IssueTimelineItem]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An item in an issue timeline
+"""
+union IssueTimelineItem = AssignedEvent | ClosedEvent | Commit | CrossReferencedEvent | DemilestonedEvent | IssueComment | LabeledEvent | LockedEvent | MilestonedEvent | ReferencedEvent | RenamedTitleEvent | ReopenedEvent | SubscribedEvent | TransferredEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UnsubscribedEvent | UserBlockedEvent
+
+"""
+An edge in a connection.
+"""
+type IssueTimelineItemEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: IssueTimelineItem
+}
+
+"""
+An item in an issue timeline
+"""
+union IssueTimelineItems = AddedToProjectEvent | AssignedEvent | ClosedEvent | CommentDeletedEvent | ConnectedEvent | ConvertedNoteToIssueEvent | CrossReferencedEvent | DemilestonedEvent | DisconnectedEvent | IssueComment | LabeledEvent | LockedEvent | MarkedAsDuplicateEvent | MentionedEvent | MilestonedEvent | MovedColumnsInProjectEvent | PinnedEvent | ReferencedEvent | RemovedFromProjectEvent | RenamedTitleEvent | ReopenedEvent | SubscribedEvent | TransferredEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UnmarkedAsDuplicateEvent | UnpinnedEvent | UnsubscribedEvent | UserBlockedEvent
+
+"""
+The connection type for IssueTimelineItems.
+"""
+type IssueTimelineItemsConnection {
+ """
+ A list of edges.
+ """
+ edges: [IssueTimelineItemsEdge]
+
+ """
+ Identifies the count of items after applying `before` and `after` filters.
+ """
+ filteredCount: Int!
+
+ """
+ A list of nodes.
+ """
+ nodes: [IssueTimelineItems]
+
+ """
+ Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.
+ """
+ pageCount: Int!
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ Identifies the date and time when the timeline was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+An edge in a connection.
+"""
+type IssueTimelineItemsEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: IssueTimelineItems
+}
+
+"""
+The possible item types found in a timeline.
+"""
+enum IssueTimelineItemsItemType {
+ """
+ Represents a 'added_to_project' event on a given issue or pull request.
+ """
+ ADDED_TO_PROJECT_EVENT
+
+ """
+ Represents an 'assigned' event on any assignable object.
+ """
+ ASSIGNED_EVENT
+
+ """
+ Represents a 'closed' event on any `Closable`.
+ """
+ CLOSED_EVENT
+
+ """
+ Represents a 'comment_deleted' event on a given issue or pull request.
+ """
+ COMMENT_DELETED_EVENT
+
+ """
+ Represents a 'connected' event on a given issue or pull request.
+ """
+ CONNECTED_EVENT
+
+ """
+ Represents a 'converted_note_to_issue' event on a given issue or pull request.
+ """
+ CONVERTED_NOTE_TO_ISSUE_EVENT
+
+ """
+ Represents a mention made by one issue or pull request to another.
+ """
+ CROSS_REFERENCED_EVENT
+
+ """
+ Represents a 'demilestoned' event on a given issue or pull request.
+ """
+ DEMILESTONED_EVENT
+
+ """
+ Represents a 'disconnected' event on a given issue or pull request.
+ """
+ DISCONNECTED_EVENT
+
+ """
+ Represents a comment on an Issue.
+ """
+ ISSUE_COMMENT
+
+ """
+ Represents a 'labeled' event on a given issue or pull request.
+ """
+ LABELED_EVENT
+
+ """
+ Represents a 'locked' event on a given issue or pull request.
+ """
+ LOCKED_EVENT
+
+ """
+ Represents a 'marked_as_duplicate' event on a given issue or pull request.
+ """
+ MARKED_AS_DUPLICATE_EVENT
+
+ """
+ Represents a 'mentioned' event on a given issue or pull request.
+ """
+ MENTIONED_EVENT
+
+ """
+ Represents a 'milestoned' event on a given issue or pull request.
+ """
+ MILESTONED_EVENT
+
+ """
+ Represents a 'moved_columns_in_project' event on a given issue or pull request.
+ """
+ MOVED_COLUMNS_IN_PROJECT_EVENT
+
+ """
+ Represents a 'pinned' event on a given issue or pull request.
+ """
+ PINNED_EVENT
+
+ """
+ Represents a 'referenced' event on a given `ReferencedSubject`.
+ """
+ REFERENCED_EVENT
+
+ """
+ Represents a 'removed_from_project' event on a given issue or pull request.
+ """
+ REMOVED_FROM_PROJECT_EVENT
+
+ """
+ Represents a 'renamed' event on a given issue or pull request
+ """
+ RENAMED_TITLE_EVENT
+
+ """
+ Represents a 'reopened' event on any `Closable`.
+ """
+ REOPENED_EVENT
+
+ """
+ Represents a 'subscribed' event on a given `Subscribable`.
+ """
+ SUBSCRIBED_EVENT
+
+ """
+ Represents a 'transferred' event on a given issue or pull request.
+ """
+ TRANSFERRED_EVENT
+
+ """
+ Represents an 'unassigned' event on any assignable object.
+ """
+ UNASSIGNED_EVENT
+
+ """
+ Represents an 'unlabeled' event on a given issue or pull request.
+ """
+ UNLABELED_EVENT
+
+ """
+ Represents an 'unlocked' event on a given issue or pull request.
+ """
+ UNLOCKED_EVENT
+
+ """
+ Represents an 'unmarked_as_duplicate' event on a given issue or pull request.
+ """
+ UNMARKED_AS_DUPLICATE_EVENT
+
+ """
+ Represents an 'unpinned' event on a given issue or pull request.
+ """
+ UNPINNED_EVENT
+
+ """
+ Represents an 'unsubscribed' event on a given `Subscribable`.
+ """
+ UNSUBSCRIBED_EVENT
+
+ """
+ Represents a 'user_blocked' event on a given user.
+ """
+ USER_BLOCKED_EVENT
+}
+
+"""
+Represents a user signing up for a GitHub account.
+"""
+type JoinedGitHubContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+A label for categorizing Issues or Milestones with a given Repository.
+"""
+type Label implements Node {
+ """
+ Identifies the label color.
+ """
+ color: String!
+
+ """
+ Identifies the date and time when the label was created.
+ """
+ createdAt: DateTime
+
+ """
+ A brief description of this label.
+ """
+ description: String
+ id: ID!
+
+ """
+ Indicates whether or not this is a default label.
+ """
+ isDefault: Boolean!
+
+ """
+ A list of issues associated with this label.
+ """
+ issues(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filtering options for issues returned from the connection.
+ """
+ filterBy: IssueFilters
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issues returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the issues by.
+ """
+ states: [IssueState!]
+ ): IssueConnection!
+
+ """
+ Identifies the label name.
+ """
+ name: String!
+
+ """
+ A list of pull requests associated with this label.
+ """
+ pullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection!
+
+ """
+ The repository associated with this label.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this label.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the label was last updated.
+ """
+ updatedAt: DateTime
+
+ """
+ The HTTP URL for this label.
+ """
+ url: URI!
+}
+
+"""
+The connection type for Label.
+"""
+type LabelConnection {
+ """
+ A list of edges.
+ """
+ edges: [LabelEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Label]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type LabelEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Label
+}
+
+"""
+Ways in which lists of labels can be ordered upon return.
+"""
+input LabelOrder {
+ """
+ The direction in which to order labels by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order labels by.
+ """
+ field: LabelOrderField!
+}
+
+"""
+Properties by which label connections can be ordered.
+"""
+enum LabelOrderField {
+ """
+ Order labels by creation time
+ """
+ CREATED_AT
+
+ """
+ Order labels by name
+ """
+ NAME
+}
+
+"""
+An object that can have labels assigned to it.
+"""
+interface Labelable {
+ """
+ A list of labels associated with the object.
+ """
+ labels(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for labels returned from the connection.
+ """
+ orderBy: LabelOrder = {field: CREATED_AT, direction: ASC}
+ ): LabelConnection
+}
+
+"""
+Represents a 'labeled' event on a given issue or pull request.
+"""
+type LabeledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the label associated with the 'labeled' event.
+ """
+ label: Label!
+
+ """
+ Identifies the `Labelable` associated with the event.
+ """
+ labelable: Labelable!
+}
+
+"""
+Represents a given language found in repositories.
+"""
+type Language implements Node {
+ """
+ The color defined for the current language.
+ """
+ color: String
+ id: ID!
+
+ """
+ The name of the current language.
+ """
+ name: String!
+}
+
+"""
+A list of languages associated with the parent.
+"""
+type LanguageConnection {
+ """
+ A list of edges.
+ """
+ edges: [LanguageEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Language]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ The total size in bytes of files written in that language.
+ """
+ totalSize: Int!
+}
+
+"""
+Represents the language of a repository.
+"""
+type LanguageEdge {
+ cursor: String!
+ node: Language!
+
+ """
+ The number of bytes of code written in the language.
+ """
+ size: Int!
+}
+
+"""
+Ordering options for language connections.
+"""
+input LanguageOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order languages by.
+ """
+ field: LanguageOrderField!
+}
+
+"""
+Properties by which language connections can be ordered.
+"""
+enum LanguageOrderField {
+ """
+ Order languages by the size of all files containing the language
+ """
+ SIZE
+}
+
+"""
+A repository's open source license
+"""
+type License implements Node {
+ """
+ The full text of the license
+ """
+ body: String!
+
+ """
+ The conditions set by the license
+ """
+ conditions: [LicenseRule]!
+
+ """
+ A human-readable description of the license
+ """
+ description: String
+
+ """
+ Whether the license should be featured
+ """
+ featured: Boolean!
+
+ """
+ Whether the license should be displayed in license pickers
+ """
+ hidden: Boolean!
+ id: ID!
+
+ """
+ Instructions on how to implement the license
+ """
+ implementation: String
+
+ """
+ The lowercased SPDX ID of the license
+ """
+ key: String!
+
+ """
+ The limitations set by the license
+ """
+ limitations: [LicenseRule]!
+
+ """
+ The license full name specified by
+ """
+ name: String!
+
+ """
+ Customary short name if applicable (e.g, GPLv3)
+ """
+ nickname: String
+
+ """
+ The permissions set by the license
+ """
+ permissions: [LicenseRule]!
+
+ """
+ Whether the license is a pseudo-license placeholder (e.g., other, no-license)
+ """
+ pseudoLicense: Boolean!
+
+ """
+ Short identifier specified by
+ """
+ spdxId: String
+
+ """
+ URL to the license on
+ """
+ url: URI
+}
+
+"""
+Describes a License's conditions, permissions, and limitations
+"""
+type LicenseRule {
+ """
+ A description of the rule
+ """
+ description: String!
+
+ """
+ The machine-readable rule key
+ """
+ key: String!
+
+ """
+ The human-readable rule label
+ """
+ label: String!
+}
+
+"""
+Autogenerated input type of LinkRepositoryToProject
+"""
+input LinkRepositoryToProjectInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Project to link to a Repository
+ """
+ projectId: ID! @possibleTypes(concreteTypes: ["Project"])
+
+ """
+ The ID of the Repository to link to a Project.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of LinkRepositoryToProject
+"""
+type LinkRepositoryToProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The linked Project.
+ """
+ project: Project
+
+ """
+ The linked Repository.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of LockLockable
+"""
+input LockLockableInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A reason for why the item will be locked.
+ """
+ lockReason: LockReason
+
+ """
+ ID of the item to be locked.
+ """
+ lockableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Lockable")
+}
+
+"""
+Autogenerated return type of LockLockable
+"""
+type LockLockablePayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The item that was locked.
+ """
+ lockedRecord: Lockable
+}
+
+"""
+The possible reasons that an issue or pull request was locked.
+"""
+enum LockReason {
+ """
+ The issue or pull request was locked because the conversation was off-topic.
+ """
+ OFF_TOPIC
+
+ """
+ The issue or pull request was locked because the conversation was resolved.
+ """
+ RESOLVED
+
+ """
+ The issue or pull request was locked because the conversation was spam.
+ """
+ SPAM
+
+ """
+ The issue or pull request was locked because the conversation was too heated.
+ """
+ TOO_HEATED
+}
+
+"""
+An object that can be locked.
+"""
+interface Lockable {
+ """
+ Reason that the conversation was locked.
+ """
+ activeLockReason: LockReason
+
+ """
+ `true` if the object is locked
+ """
+ locked: Boolean!
+}
+
+"""
+Represents a 'locked' event on a given issue or pull request.
+"""
+type LockedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Reason that the conversation was locked (optional).
+ """
+ lockReason: LockReason
+
+ """
+ Object that was locked.
+ """
+ lockable: Lockable!
+}
+
+"""
+A placeholder user for attribution of imported data on GitHub.
+"""
+type Mannequin implements Actor & Node & UniformResourceLocatable {
+ """
+ A URL pointing to the GitHub App's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The mannequin's email on the source instance.
+ """
+ email: String
+ id: ID!
+
+ """
+ The username of the actor.
+ """
+ login: String!
+
+ """
+ The HTML path to this resource.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The URL to this resource.
+ """
+ url: URI!
+}
+
+"""
+Autogenerated input type of MarkFileAsViewed
+"""
+input MarkFileAsViewedInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The path of the file to mark as viewed
+ """
+ path: String!
+
+ """
+ The Node ID of the pull request.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of MarkFileAsViewed
+"""
+type MarkFileAsViewedPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated pull request.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Autogenerated input type of MarkPullRequestReadyForReview
+"""
+input MarkPullRequestReadyForReviewInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the pull request to be marked as ready for review.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of MarkPullRequestReadyForReview
+"""
+type MarkPullRequestReadyForReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request that is ready for review.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'marked_as_duplicate' event on a given issue or pull request.
+"""
+type MarkedAsDuplicateEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ The authoritative issue or pull request which has been duplicated by another.
+ """
+ canonical: IssueOrPullRequest
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The issue or pull request which has been marked as a duplicate of another.
+ """
+ duplicate: IssueOrPullRequest
+ id: ID!
+
+ """
+ Canonical and duplicate belong to different repositories.
+ """
+ isCrossRepository: Boolean!
+}
+
+"""
+A public description of a Marketplace category.
+"""
+type MarketplaceCategory implements Node {
+ """
+ The category's description.
+ """
+ description: String
+
+ """
+ The technical description of how apps listed in this category work with GitHub.
+ """
+ howItWorks: String
+ id: ID!
+
+ """
+ The category's name.
+ """
+ name: String!
+
+ """
+ How many Marketplace listings have this as their primary category.
+ """
+ primaryListingCount: Int!
+
+ """
+ The HTTP path for this Marketplace category.
+ """
+ resourcePath: URI!
+
+ """
+ How many Marketplace listings have this as their secondary category.
+ """
+ secondaryListingCount: Int!
+
+ """
+ The short name of the category used in its URL.
+ """
+ slug: String!
+
+ """
+ The HTTP URL for this Marketplace category.
+ """
+ url: URI!
+}
+
+"""
+A listing in the GitHub integration marketplace.
+"""
+type MarketplaceListing implements Node {
+ """
+ The GitHub App this listing represents.
+ """
+ app: App
+
+ """
+ URL to the listing owner's company site.
+ """
+ companyUrl: URI
+
+ """
+ The HTTP path for configuring access to the listing's integration or OAuth app
+ """
+ configurationResourcePath: URI!
+
+ """
+ The HTTP URL for configuring access to the listing's integration or OAuth app
+ """
+ configurationUrl: URI!
+
+ """
+ URL to the listing's documentation.
+ """
+ documentationUrl: URI
+
+ """
+ The listing's detailed description.
+ """
+ extendedDescription: String
+
+ """
+ The listing's detailed description rendered to HTML.
+ """
+ extendedDescriptionHTML: HTML!
+
+ """
+ The listing's introductory description.
+ """
+ fullDescription: String!
+
+ """
+ The listing's introductory description rendered to HTML.
+ """
+ fullDescriptionHTML: HTML!
+
+ """
+ Does this listing have any plans with a free trial?
+ """
+ hasPublishedFreeTrialPlans: Boolean!
+
+ """
+ Does this listing have a terms of service link?
+ """
+ hasTermsOfService: Boolean!
+
+ """
+ Whether the creator of the app is a verified org
+ """
+ hasVerifiedOwner: Boolean!
+
+ """
+ A technical description of how this app works with GitHub.
+ """
+ howItWorks: String
+
+ """
+ The listing's technical description rendered to HTML.
+ """
+ howItWorksHTML: HTML!
+ id: ID!
+
+ """
+ URL to install the product to the viewer's account or organization.
+ """
+ installationUrl: URI
+
+ """
+ Whether this listing's app has been installed for the current viewer
+ """
+ installedForViewer: Boolean!
+
+ """
+ Whether this listing has been removed from the Marketplace.
+ """
+ isArchived: Boolean!
+
+ """
+ Whether this listing is still an editable draft that has not been submitted
+ for review and is not publicly visible in the Marketplace.
+ """
+ isDraft: Boolean!
+
+ """
+ Whether the product this listing represents is available as part of a paid plan.
+ """
+ isPaid: Boolean!
+
+ """
+ Whether this listing has been approved for display in the Marketplace.
+ """
+ isPublic: Boolean!
+
+ """
+ Whether this listing has been rejected by GitHub for display in the Marketplace.
+ """
+ isRejected: Boolean!
+
+ """
+ Whether this listing has been approved for unverified display in the Marketplace.
+ """
+ isUnverified: Boolean!
+
+ """
+ Whether this draft listing has been submitted for review for approval to be unverified in the Marketplace.
+ """
+ isUnverifiedPending: Boolean!
+
+ """
+ Whether this draft listing has been submitted for review from GitHub for approval to be verified in the Marketplace.
+ """
+ isVerificationPendingFromDraft: Boolean!
+
+ """
+ Whether this unverified listing has been submitted for review from GitHub for approval to be verified in the Marketplace.
+ """
+ isVerificationPendingFromUnverified: Boolean!
+
+ """
+ Whether this listing has been approved for verified display in the Marketplace.
+ """
+ isVerified: Boolean!
+
+ """
+ The hex color code, without the leading '#', for the logo background.
+ """
+ logoBackgroundColor: String!
+
+ """
+ URL for the listing's logo image.
+ """
+ logoUrl(
+ """
+ The size in pixels of the resulting square image.
+ """
+ size: Int = 400
+ ): URI
+
+ """
+ The listing's full name.
+ """
+ name: String!
+
+ """
+ The listing's very short description without a trailing period or ampersands.
+ """
+ normalizedShortDescription: String!
+
+ """
+ URL to the listing's detailed pricing.
+ """
+ pricingUrl: URI
+
+ """
+ The category that best describes the listing.
+ """
+ primaryCategory: MarketplaceCategory!
+
+ """
+ URL to the listing's privacy policy, may return an empty string for listings that do not require a privacy policy URL.
+ """
+ privacyPolicyUrl: URI!
+
+ """
+ The HTTP path for the Marketplace listing.
+ """
+ resourcePath: URI!
+
+ """
+ The URLs for the listing's screenshots.
+ """
+ screenshotUrls: [String]!
+
+ """
+ An alternate category that describes the listing.
+ """
+ secondaryCategory: MarketplaceCategory
+
+ """
+ The listing's very short description.
+ """
+ shortDescription: String!
+
+ """
+ The short name of the listing used in its URL.
+ """
+ slug: String!
+
+ """
+ URL to the listing's status page.
+ """
+ statusUrl: URI
+
+ """
+ An email address for support for this listing's app.
+ """
+ supportEmail: String
+
+ """
+ Either a URL or an email address for support for this listing's app, may
+ return an empty string for listings that do not require a support URL.
+ """
+ supportUrl: URI!
+
+ """
+ URL to the listing's terms of service.
+ """
+ termsOfServiceUrl: URI
+
+ """
+ The HTTP URL for the Marketplace listing.
+ """
+ url: URI!
+
+ """
+ Can the current viewer add plans for this Marketplace listing.
+ """
+ viewerCanAddPlans: Boolean!
+
+ """
+ Can the current viewer approve this Marketplace listing.
+ """
+ viewerCanApprove: Boolean!
+
+ """
+ Can the current viewer delist this Marketplace listing.
+ """
+ viewerCanDelist: Boolean!
+
+ """
+ Can the current viewer edit this Marketplace listing.
+ """
+ viewerCanEdit: Boolean!
+
+ """
+ Can the current viewer edit the primary and secondary category of this
+ Marketplace listing.
+ """
+ viewerCanEditCategories: Boolean!
+
+ """
+ Can the current viewer edit the plans for this Marketplace listing.
+ """
+ viewerCanEditPlans: Boolean!
+
+ """
+ Can the current viewer return this Marketplace listing to draft state
+ so it becomes editable again.
+ """
+ viewerCanRedraft: Boolean!
+
+ """
+ Can the current viewer reject this Marketplace listing by returning it to
+ an editable draft state or rejecting it entirely.
+ """
+ viewerCanReject: Boolean!
+
+ """
+ Can the current viewer request this listing be reviewed for display in
+ the Marketplace as verified.
+ """
+ viewerCanRequestApproval: Boolean!
+
+ """
+ Indicates whether the current user has an active subscription to this Marketplace listing.
+ """
+ viewerHasPurchased: Boolean!
+
+ """
+ Indicates if the current user has purchased a subscription to this Marketplace listing
+ for all of the organizations the user owns.
+ """
+ viewerHasPurchasedForAllOrganizations: Boolean!
+
+ """
+ Does the current viewer role allow them to administer this Marketplace listing.
+ """
+ viewerIsListingAdmin: Boolean!
+}
+
+"""
+Look up Marketplace Listings
+"""
+type MarketplaceListingConnection {
+ """
+ A list of edges.
+ """
+ edges: [MarketplaceListingEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [MarketplaceListing]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type MarketplaceListingEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: MarketplaceListing
+}
+
+"""
+Entities that have members who can set status messages.
+"""
+interface MemberStatusable {
+ """
+ Get the status messages members of this entity have set that are either public or visible only to the organization.
+ """
+ memberStatuses(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for user statuses returned from the connection.
+ """
+ orderBy: UserStatusOrder = {field: UPDATED_AT, direction: DESC}
+ ): UserStatusConnection!
+}
+
+"""
+Audit log entry for a members_can_delete_repos.clear event.
+"""
+type MembersCanDeleteReposClearAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a members_can_delete_repos.disable event.
+"""
+type MembersCanDeleteReposDisableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a members_can_delete_repos.enable event.
+"""
+type MembersCanDeleteReposEnableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Represents a 'mentioned' event on a given issue or pull request.
+"""
+type MentionedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+}
+
+"""
+Autogenerated input type of MergeBranch
+"""
+input MergeBranchInput {
+ """
+ The email address to associate with this commit.
+ """
+ authorEmail: String
+
+ """
+ The name of the base branch that the provided head will be merged into.
+ """
+ base: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Message to use for the merge commit. If omitted, a default will be used.
+ """
+ commitMessage: String
+
+ """
+ The head to merge into the base branch. This can be a branch name or a commit GitObjectID.
+ """
+ head: String!
+
+ """
+ The Node ID of the Repository containing the base branch that will be modified.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of MergeBranch
+"""
+type MergeBranchPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The resulting merge Commit.
+ """
+ mergeCommit: Commit
+}
+
+"""
+Autogenerated input type of MergePullRequest
+"""
+input MergePullRequestInput {
+ """
+ The email address to associate with this merge.
+ """
+ authorEmail: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Commit body to use for the merge commit; if omitted, a default message will be used
+ """
+ commitBody: String
+
+ """
+ Commit headline to use for the merge commit; if omitted, a default message will be used.
+ """
+ commitHeadline: String
+
+ """
+ OID that the pull request head ref must match to allow merge; if omitted, no check is performed.
+ """
+ expectedHeadOid: GitObjectID
+
+ """
+ The merge method to use. If omitted, defaults to 'MERGE'
+ """
+ mergeMethod: PullRequestMergeMethod = MERGE
+
+ """
+ ID of the pull request to be merged.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of MergePullRequest
+"""
+type MergePullRequestPayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request that was merged.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Detailed status information about a pull request merge.
+"""
+enum MergeStateStatus {
+ """
+ The head ref is out of date.
+ """
+ BEHIND
+
+ """
+ The merge is blocked.
+ """
+ BLOCKED
+
+ """
+ Mergeable and passing commit status.
+ """
+ CLEAN
+
+ """
+ The merge commit cannot be cleanly created.
+ """
+ DIRTY
+
+ """
+ The merge is blocked due to the pull request being a draft.
+ """
+ DRAFT @deprecated(reason: "DRAFT state will be removed from this enum and `isDraft` should be used instead Use PullRequest.isDraft instead. Removal on 2021-01-01 UTC.")
+
+ """
+ Mergeable with passing commit status and pre-receive hooks.
+ """
+ HAS_HOOKS
+
+ """
+ The state cannot currently be determined.
+ """
+ UNKNOWN
+
+ """
+ Mergeable with non-passing commit status.
+ """
+ UNSTABLE
+}
+
+"""
+Whether or not a PullRequest can be merged.
+"""
+enum MergeableState {
+ """
+ The pull request cannot be merged due to merge conflicts.
+ """
+ CONFLICTING
+
+ """
+ The pull request can be merged.
+ """
+ MERGEABLE
+
+ """
+ The mergeability of the pull request is still being calculated.
+ """
+ UNKNOWN
+}
+
+"""
+Represents a 'merged' event on a given pull request.
+"""
+type MergedEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the commit associated with the `merge` event.
+ """
+ commit: Commit
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the Ref associated with the `merge` event.
+ """
+ mergeRef: Ref
+
+ """
+ Identifies the name of the Ref associated with the `merge` event.
+ """
+ mergeRefName: String!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The HTTP path for this merged event.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this merged event.
+ """
+ url: URI!
+}
+
+"""
+Represents a Milestone object on a given repository.
+"""
+type Milestone implements Closable & Node & UniformResourceLocatable {
+ """
+ `true` if the object is closed (definition of closed may depend on type)
+ """
+ closed: Boolean!
+
+ """
+ Identifies the date and time when the object was closed.
+ """
+ closedAt: DateTime
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the actor who created the milestone.
+ """
+ creator: Actor
+
+ """
+ Identifies the description of the milestone.
+ """
+ description: String
+
+ """
+ Identifies the due date of the milestone.
+ """
+ dueOn: DateTime
+ id: ID!
+
+ """
+ A list of issues associated with the milestone.
+ """
+ issues(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filtering options for issues returned from the connection.
+ """
+ filterBy: IssueFilters
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issues returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the issues by.
+ """
+ states: [IssueState!]
+ ): IssueConnection!
+
+ """
+ Identifies the number of the milestone.
+ """
+ number: Int!
+
+ """
+ Identifies the percentage complete for the milestone
+ """
+ progressPercentage: Float!
+
+ """
+ A list of pull requests associated with the milestone.
+ """
+ pullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection!
+
+ """
+ The repository associated with this milestone.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this milestone
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the state of the milestone.
+ """
+ state: MilestoneState!
+
+ """
+ Identifies the title of the milestone.
+ """
+ title: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this milestone
+ """
+ url: URI!
+}
+
+"""
+The connection type for Milestone.
+"""
+type MilestoneConnection {
+ """
+ A list of edges.
+ """
+ edges: [MilestoneEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Milestone]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type MilestoneEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Milestone
+}
+
+"""
+Types that can be inside a Milestone.
+"""
+union MilestoneItem = Issue | PullRequest
+
+"""
+Ordering options for milestone connections.
+"""
+input MilestoneOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order milestones by.
+ """
+ field: MilestoneOrderField!
+}
+
+"""
+Properties by which milestone connections can be ordered.
+"""
+enum MilestoneOrderField {
+ """
+ Order milestones by when they were created.
+ """
+ CREATED_AT
+
+ """
+ Order milestones by when they are due.
+ """
+ DUE_DATE
+
+ """
+ Order milestones by their number.
+ """
+ NUMBER
+
+ """
+ Order milestones by when they were last updated.
+ """
+ UPDATED_AT
+}
+
+"""
+The possible states of a milestone.
+"""
+enum MilestoneState {
+ """
+ A milestone that has been closed.
+ """
+ CLOSED
+
+ """
+ A milestone that is still open.
+ """
+ OPEN
+}
+
+"""
+Represents a 'milestoned' event on a given issue or pull request.
+"""
+type MilestonedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the milestone title associated with the 'milestoned' event.
+ """
+ milestoneTitle: String!
+
+ """
+ Object referenced by event.
+ """
+ subject: MilestoneItem!
+}
+
+"""
+Entities that can be minimized.
+"""
+interface Minimizable {
+ """
+ Returns whether or not a comment has been minimized.
+ """
+ isMinimized: Boolean!
+
+ """
+ Returns why the comment was minimized.
+ """
+ minimizedReason: String
+
+ """
+ Check if the current viewer can minimize this object.
+ """
+ viewerCanMinimize: Boolean!
+}
+
+"""
+Autogenerated input type of MinimizeComment
+"""
+input MinimizeCommentInput {
+ """
+ The classification of comment
+ """
+ classifier: ReportedContentClassifiers!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the subject to modify.
+ """
+ subjectId: ID! @possibleTypes(concreteTypes: ["CommitComment", "GistComment", "IssueComment", "PullRequestReviewComment"], abstractType: "Minimizable")
+}
+
+"""
+Autogenerated return type of MinimizeComment
+"""
+type MinimizeCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The comment that was minimized.
+ """
+ minimizedComment: Minimizable
+}
+
+"""
+Autogenerated input type of MoveProjectCard
+"""
+input MoveProjectCardInput {
+ """
+ Place the new card after the card with this id. Pass null to place it at the top.
+ """
+ afterCardId: ID @possibleTypes(concreteTypes: ["ProjectCard"])
+
+ """
+ The id of the card to move.
+ """
+ cardId: ID! @possibleTypes(concreteTypes: ["ProjectCard"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the column to move it into.
+ """
+ columnId: ID! @possibleTypes(concreteTypes: ["ProjectColumn"])
+}
+
+"""
+Autogenerated return type of MoveProjectCard
+"""
+type MoveProjectCardPayload {
+ """
+ The new edge of the moved card.
+ """
+ cardEdge: ProjectCardEdge
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of MoveProjectColumn
+"""
+input MoveProjectColumnInput {
+ """
+ Place the new column after the column with this id. Pass null to place it at the front.
+ """
+ afterColumnId: ID @possibleTypes(concreteTypes: ["ProjectColumn"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The id of the column to move.
+ """
+ columnId: ID! @possibleTypes(concreteTypes: ["ProjectColumn"])
+}
+
+"""
+Autogenerated return type of MoveProjectColumn
+"""
+type MoveProjectColumnPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new edge of the moved column.
+ """
+ columnEdge: ProjectColumnEdge
+}
+
+"""
+Represents a 'moved_columns_in_project' event on a given issue or pull request.
+"""
+type MovedColumnsInProjectEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Column name the issue or pull request was moved from.
+ """
+ previousProjectColumnName: String! @preview(toggledBy: "starfox-preview")
+
+ """
+ Project referenced by event.
+ """
+ project: Project @preview(toggledBy: "starfox-preview")
+
+ """
+ Project card referenced by this project event.
+ """
+ projectCard: ProjectCard @preview(toggledBy: "starfox-preview")
+
+ """
+ Column name the issue or pull request was moved to.
+ """
+ projectColumnName: String! @preview(toggledBy: "starfox-preview")
+}
+
+"""
+The root query for implementing GraphQL mutations.
+"""
+type Mutation {
+ """
+ Accepts a pending invitation for a user to become an administrator of an enterprise.
+ """
+ acceptEnterpriseAdministratorInvitation(input: AcceptEnterpriseAdministratorInvitationInput!): AcceptEnterpriseAdministratorInvitationPayload
+
+ """
+ Applies a suggested topic to the repository.
+ """
+ acceptTopicSuggestion(input: AcceptTopicSuggestionInput!): AcceptTopicSuggestionPayload
+
+ """
+ Adds assignees to an assignable object.
+ """
+ addAssigneesToAssignable(input: AddAssigneesToAssignableInput!): AddAssigneesToAssignablePayload
+
+ """
+ Adds a comment to an Issue or Pull Request.
+ """
+ addComment(input: AddCommentInput!): AddCommentPayload
+
+ """
+ Adds a support entitlement to an enterprise member.
+ """
+ addEnterpriseSupportEntitlement(input: AddEnterpriseSupportEntitlementInput!): AddEnterpriseSupportEntitlementPayload
+
+ """
+ Adds labels to a labelable object.
+ """
+ addLabelsToLabelable(input: AddLabelsToLabelableInput!): AddLabelsToLabelablePayload
+
+ """
+ Adds a card to a ProjectColumn. Either `contentId` or `note` must be provided but **not** both.
+ """
+ addProjectCard(input: AddProjectCardInput!): AddProjectCardPayload
+
+ """
+ Adds a column to a Project.
+ """
+ addProjectColumn(input: AddProjectColumnInput!): AddProjectColumnPayload
+
+ """
+ Adds a review to a Pull Request.
+ """
+ addPullRequestReview(input: AddPullRequestReviewInput!): AddPullRequestReviewPayload
+
+ """
+ Adds a comment to a review.
+ """
+ addPullRequestReviewComment(input: AddPullRequestReviewCommentInput!): AddPullRequestReviewCommentPayload
+
+ """
+ Adds a new thread to a pending Pull Request Review.
+ """
+ addPullRequestReviewThread(input: AddPullRequestReviewThreadInput!): AddPullRequestReviewThreadPayload
+
+ """
+ Adds a reaction to a subject.
+ """
+ addReaction(input: AddReactionInput!): AddReactionPayload
+
+ """
+ Adds a star to a Starrable.
+ """
+ addStar(input: AddStarInput!): AddStarPayload
+
+ """
+ Adds a verifiable domain to an owning account.
+ """
+ addVerifiableDomain(input: AddVerifiableDomainInput!): AddVerifiableDomainPayload
+
+ """
+ Marks a repository as archived.
+ """
+ archiveRepository(input: ArchiveRepositoryInput!): ArchiveRepositoryPayload
+
+ """
+ Cancels a pending invitation for an administrator to join an enterprise.
+ """
+ cancelEnterpriseAdminInvitation(input: CancelEnterpriseAdminInvitationInput!): CancelEnterpriseAdminInvitationPayload
+
+ """
+ Update your status on GitHub.
+ """
+ changeUserStatus(input: ChangeUserStatusInput!): ChangeUserStatusPayload
+
+ """
+ Clears all labels from a labelable object.
+ """
+ clearLabelsFromLabelable(input: ClearLabelsFromLabelableInput!): ClearLabelsFromLabelablePayload
+
+ """
+ Creates a new project by cloning configuration from an existing project.
+ """
+ cloneProject(input: CloneProjectInput!): CloneProjectPayload
+
+ """
+ Create a new repository with the same files and directory structure as a template repository.
+ """
+ cloneTemplateRepository(input: CloneTemplateRepositoryInput!): CloneTemplateRepositoryPayload
+
+ """
+ Close an issue.
+ """
+ closeIssue(input: CloseIssueInput!): CloseIssuePayload
+
+ """
+ Close a pull request.
+ """
+ closePullRequest(input: ClosePullRequestInput!): ClosePullRequestPayload
+
+ """
+ Convert a project note card to one associated with a newly created issue.
+ """
+ convertProjectCardNoteToIssue(input: ConvertProjectCardNoteToIssueInput!): ConvertProjectCardNoteToIssuePayload
+
+ """
+ Create a new branch protection rule
+ """
+ createBranchProtectionRule(input: CreateBranchProtectionRuleInput!): CreateBranchProtectionRulePayload
+
+ """
+ Create a check run.
+ """
+ createCheckRun(input: CreateCheckRunInput!): CreateCheckRunPayload
+
+ """
+ Create a check suite
+ """
+ createCheckSuite(input: CreateCheckSuiteInput!): CreateCheckSuitePayload
+
+ """
+ Create a content attachment.
+ """
+ createContentAttachment(input: CreateContentAttachmentInput!): CreateContentAttachmentPayload @preview(toggledBy: "corsair-preview")
+
+ """
+ Creates a new deployment event.
+ """
+ createDeployment(input: CreateDeploymentInput!): CreateDeploymentPayload @preview(toggledBy: "flash-preview")
+
+ """
+ Create a deployment status.
+ """
+ createDeploymentStatus(input: CreateDeploymentStatusInput!): CreateDeploymentStatusPayload @preview(toggledBy: "flash-preview")
+
+ """
+ Creates an organization as part of an enterprise account.
+ """
+ createEnterpriseOrganization(input: CreateEnterpriseOrganizationInput!): CreateEnterpriseOrganizationPayload
+
+ """
+ Creates a new IP allow list entry.
+ """
+ createIpAllowListEntry(input: CreateIpAllowListEntryInput!): CreateIpAllowListEntryPayload
+
+ """
+ Creates a new issue.
+ """
+ createIssue(input: CreateIssueInput!): CreateIssuePayload
+
+ """
+ Creates a new label.
+ """
+ createLabel(input: CreateLabelInput!): CreateLabelPayload @preview(toggledBy: "bane-preview")
+
+ """
+ Creates a new project.
+ """
+ createProject(input: CreateProjectInput!): CreateProjectPayload
+
+ """
+ Create a new pull request
+ """
+ createPullRequest(input: CreatePullRequestInput!): CreatePullRequestPayload
+
+ """
+ Create a new Git Ref.
+ """
+ createRef(input: CreateRefInput!): CreateRefPayload
+
+ """
+ Create a new repository.
+ """
+ createRepository(input: CreateRepositoryInput!): CreateRepositoryPayload
+
+ """
+ Creates a new team discussion.
+ """
+ createTeamDiscussion(input: CreateTeamDiscussionInput!): CreateTeamDiscussionPayload
+
+ """
+ Creates a new team discussion comment.
+ """
+ createTeamDiscussionComment(input: CreateTeamDiscussionCommentInput!): CreateTeamDiscussionCommentPayload
+
+ """
+ Rejects a suggested topic for the repository.
+ """
+ declineTopicSuggestion(input: DeclineTopicSuggestionInput!): DeclineTopicSuggestionPayload
+
+ """
+ Delete a branch protection rule
+ """
+ deleteBranchProtectionRule(input: DeleteBranchProtectionRuleInput!): DeleteBranchProtectionRulePayload
+
+ """
+ Deletes a deployment.
+ """
+ deleteDeployment(input: DeleteDeploymentInput!): DeleteDeploymentPayload
+
+ """
+ Deletes an IP allow list entry.
+ """
+ deleteIpAllowListEntry(input: DeleteIpAllowListEntryInput!): DeleteIpAllowListEntryPayload
+
+ """
+ Deletes an Issue object.
+ """
+ deleteIssue(input: DeleteIssueInput!): DeleteIssuePayload
+
+ """
+ Deletes an IssueComment object.
+ """
+ deleteIssueComment(input: DeleteIssueCommentInput!): DeleteIssueCommentPayload
+
+ """
+ Deletes a label.
+ """
+ deleteLabel(input: DeleteLabelInput!): DeleteLabelPayload @preview(toggledBy: "bane-preview")
+
+ """
+ Delete a package version.
+ """
+ deletePackageVersion(input: DeletePackageVersionInput!): DeletePackageVersionPayload @preview(toggledBy: "package-deletes-preview")
+
+ """
+ Deletes a project.
+ """
+ deleteProject(input: DeleteProjectInput!): DeleteProjectPayload
+
+ """
+ Deletes a project card.
+ """
+ deleteProjectCard(input: DeleteProjectCardInput!): DeleteProjectCardPayload
+
+ """
+ Deletes a project column.
+ """
+ deleteProjectColumn(input: DeleteProjectColumnInput!): DeleteProjectColumnPayload
+
+ """
+ Deletes a pull request review.
+ """
+ deletePullRequestReview(input: DeletePullRequestReviewInput!): DeletePullRequestReviewPayload
+
+ """
+ Deletes a pull request review comment.
+ """
+ deletePullRequestReviewComment(input: DeletePullRequestReviewCommentInput!): DeletePullRequestReviewCommentPayload
+
+ """
+ Delete a Git Ref.
+ """
+ deleteRef(input: DeleteRefInput!): DeleteRefPayload
+
+ """
+ Deletes a team discussion.
+ """
+ deleteTeamDiscussion(input: DeleteTeamDiscussionInput!): DeleteTeamDiscussionPayload
+
+ """
+ Deletes a team discussion comment.
+ """
+ deleteTeamDiscussionComment(input: DeleteTeamDiscussionCommentInput!): DeleteTeamDiscussionCommentPayload
+
+ """
+ Deletes a verifiable domain.
+ """
+ deleteVerifiableDomain(input: DeleteVerifiableDomainInput!): DeleteVerifiableDomainPayload
+
+ """
+ Dismisses an approved or rejected pull request review.
+ """
+ dismissPullRequestReview(input: DismissPullRequestReviewInput!): DismissPullRequestReviewPayload
+
+ """
+ Follow a user.
+ """
+ followUser(input: FollowUserInput!): FollowUserPayload
+
+ """
+ Creates a new project by importing columns and a list of issues/PRs.
+ """
+ importProject(input: ImportProjectInput!): ImportProjectPayload @preview(toggledBy: "slothette-preview")
+
+ """
+ Invite someone to become an administrator of the enterprise.
+ """
+ inviteEnterpriseAdmin(input: InviteEnterpriseAdminInput!): InviteEnterpriseAdminPayload
+
+ """
+ Creates a repository link for a project.
+ """
+ linkRepositoryToProject(input: LinkRepositoryToProjectInput!): LinkRepositoryToProjectPayload
+
+ """
+ Lock a lockable object
+ """
+ lockLockable(input: LockLockableInput!): LockLockablePayload
+
+ """
+ Mark a pull request file as viewed
+ """
+ markFileAsViewed(input: MarkFileAsViewedInput!): MarkFileAsViewedPayload
+
+ """
+ Marks a pull request ready for review.
+ """
+ markPullRequestReadyForReview(input: MarkPullRequestReadyForReviewInput!): MarkPullRequestReadyForReviewPayload
+
+ """
+ Merge a head into a branch.
+ """
+ mergeBranch(input: MergeBranchInput!): MergeBranchPayload
+
+ """
+ Merge a pull request.
+ """
+ mergePullRequest(input: MergePullRequestInput!): MergePullRequestPayload
+
+ """
+ Minimizes a comment on an Issue, Commit, Pull Request, or Gist
+ """
+ minimizeComment(input: MinimizeCommentInput!): MinimizeCommentPayload
+
+ """
+ Moves a project card to another place.
+ """
+ moveProjectCard(input: MoveProjectCardInput!): MoveProjectCardPayload
+
+ """
+ Moves a project column to another place.
+ """
+ moveProjectColumn(input: MoveProjectColumnInput!): MoveProjectColumnPayload
+
+ """
+ Pin an issue to a repository
+ """
+ pinIssue(input: PinIssueInput!): PinIssuePayload @preview(toggledBy: "elektra-preview")
+
+ """
+ Regenerates the identity provider recovery codes for an enterprise
+ """
+ regenerateEnterpriseIdentityProviderRecoveryCodes(input: RegenerateEnterpriseIdentityProviderRecoveryCodesInput!): RegenerateEnterpriseIdentityProviderRecoveryCodesPayload
+
+ """
+ Regenerates a verifiable domain's verification token.
+ """
+ regenerateVerifiableDomainToken(input: RegenerateVerifiableDomainTokenInput!): RegenerateVerifiableDomainTokenPayload
+
+ """
+ Removes assignees from an assignable object.
+ """
+ removeAssigneesFromAssignable(input: RemoveAssigneesFromAssignableInput!): RemoveAssigneesFromAssignablePayload
+
+ """
+ Removes an administrator from the enterprise.
+ """
+ removeEnterpriseAdmin(input: RemoveEnterpriseAdminInput!): RemoveEnterpriseAdminPayload
+
+ """
+ Removes the identity provider from an enterprise
+ """
+ removeEnterpriseIdentityProvider(input: RemoveEnterpriseIdentityProviderInput!): RemoveEnterpriseIdentityProviderPayload
+
+ """
+ Removes an organization from the enterprise
+ """
+ removeEnterpriseOrganization(input: RemoveEnterpriseOrganizationInput!): RemoveEnterpriseOrganizationPayload
+
+ """
+ Removes a support entitlement from an enterprise member.
+ """
+ removeEnterpriseSupportEntitlement(input: RemoveEnterpriseSupportEntitlementInput!): RemoveEnterpriseSupportEntitlementPayload
+
+ """
+ Removes labels from a Labelable object.
+ """
+ removeLabelsFromLabelable(input: RemoveLabelsFromLabelableInput!): RemoveLabelsFromLabelablePayload
+
+ """
+ Removes outside collaborator from all repositories in an organization.
+ """
+ removeOutsideCollaborator(input: RemoveOutsideCollaboratorInput!): RemoveOutsideCollaboratorPayload
+
+ """
+ Removes a reaction from a subject.
+ """
+ removeReaction(input: RemoveReactionInput!): RemoveReactionPayload
+
+ """
+ Removes a star from a Starrable.
+ """
+ removeStar(input: RemoveStarInput!): RemoveStarPayload
+
+ """
+ Reopen a issue.
+ """
+ reopenIssue(input: ReopenIssueInput!): ReopenIssuePayload
+
+ """
+ Reopen a pull request.
+ """
+ reopenPullRequest(input: ReopenPullRequestInput!): ReopenPullRequestPayload
+
+ """
+ Set review requests on a pull request.
+ """
+ requestReviews(input: RequestReviewsInput!): RequestReviewsPayload
+
+ """
+ Rerequests an existing check suite.
+ """
+ rerequestCheckSuite(input: RerequestCheckSuiteInput!): RerequestCheckSuitePayload
+
+ """
+ Marks a review thread as resolved.
+ """
+ resolveReviewThread(input: ResolveReviewThreadInput!): ResolveReviewThreadPayload
+
+ """
+ Creates or updates the identity provider for an enterprise.
+ """
+ setEnterpriseIdentityProvider(input: SetEnterpriseIdentityProviderInput!): SetEnterpriseIdentityProviderPayload
+
+ """
+ Set an organization level interaction limit for an organization's public repositories.
+ """
+ setOrganizationInteractionLimit(input: SetOrganizationInteractionLimitInput!): SetOrganizationInteractionLimitPayload
+
+ """
+ Sets an interaction limit setting for a repository.
+ """
+ setRepositoryInteractionLimit(input: SetRepositoryInteractionLimitInput!): SetRepositoryInteractionLimitPayload
+
+ """
+ Set a user level interaction limit for an user's public repositories.
+ """
+ setUserInteractionLimit(input: SetUserInteractionLimitInput!): SetUserInteractionLimitPayload
+
+ """
+ Submits a pending pull request review.
+ """
+ submitPullRequestReview(input: SubmitPullRequestReviewInput!): SubmitPullRequestReviewPayload
+
+ """
+ Transfer an issue to a different repository
+ """
+ transferIssue(input: TransferIssueInput!): TransferIssuePayload
+
+ """
+ Unarchives a repository.
+ """
+ unarchiveRepository(input: UnarchiveRepositoryInput!): UnarchiveRepositoryPayload
+
+ """
+ Unfollow a user.
+ """
+ unfollowUser(input: UnfollowUserInput!): UnfollowUserPayload
+
+ """
+ Deletes a repository link from a project.
+ """
+ unlinkRepositoryFromProject(input: UnlinkRepositoryFromProjectInput!): UnlinkRepositoryFromProjectPayload
+
+ """
+ Unlock a lockable object
+ """
+ unlockLockable(input: UnlockLockableInput!): UnlockLockablePayload
+
+ """
+ Unmark a pull request file as viewed
+ """
+ unmarkFileAsViewed(input: UnmarkFileAsViewedInput!): UnmarkFileAsViewedPayload
+
+ """
+ Unmark an issue as a duplicate of another issue.
+ """
+ unmarkIssueAsDuplicate(input: UnmarkIssueAsDuplicateInput!): UnmarkIssueAsDuplicatePayload
+
+ """
+ Unminimizes a comment on an Issue, Commit, Pull Request, or Gist
+ """
+ unminimizeComment(input: UnminimizeCommentInput!): UnminimizeCommentPayload
+
+ """
+ Unpin a pinned issue from a repository
+ """
+ unpinIssue(input: UnpinIssueInput!): UnpinIssuePayload @preview(toggledBy: "elektra-preview")
+
+ """
+ Marks a review thread as unresolved.
+ """
+ unresolveReviewThread(input: UnresolveReviewThreadInput!): UnresolveReviewThreadPayload
+
+ """
+ Create a new branch protection rule
+ """
+ updateBranchProtectionRule(input: UpdateBranchProtectionRuleInput!): UpdateBranchProtectionRulePayload
+
+ """
+ Update a check run
+ """
+ updateCheckRun(input: UpdateCheckRunInput!): UpdateCheckRunPayload
+
+ """
+ Modifies the settings of an existing check suite
+ """
+ updateCheckSuitePreferences(input: UpdateCheckSuitePreferencesInput!): UpdateCheckSuitePreferencesPayload
+
+ """
+ Updates the role of an enterprise administrator.
+ """
+ updateEnterpriseAdministratorRole(input: UpdateEnterpriseAdministratorRoleInput!): UpdateEnterpriseAdministratorRolePayload
+
+ """
+ Sets whether private repository forks are enabled for an enterprise.
+ """
+ updateEnterpriseAllowPrivateRepositoryForkingSetting(input: UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput!): UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload
+
+ """
+ Sets the default repository permission for organizations in an enterprise.
+ """
+ updateEnterpriseDefaultRepositoryPermissionSetting(input: UpdateEnterpriseDefaultRepositoryPermissionSettingInput!): UpdateEnterpriseDefaultRepositoryPermissionSettingPayload
+
+ """
+ Sets whether organization members with admin permissions on a repository can change repository visibility.
+ """
+ updateEnterpriseMembersCanChangeRepositoryVisibilitySetting(input: UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput!): UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload
+
+ """
+ Sets the members can create repositories setting for an enterprise.
+ """
+ updateEnterpriseMembersCanCreateRepositoriesSetting(input: UpdateEnterpriseMembersCanCreateRepositoriesSettingInput!): UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload
+
+ """
+ Sets the members can delete issues setting for an enterprise.
+ """
+ updateEnterpriseMembersCanDeleteIssuesSetting(input: UpdateEnterpriseMembersCanDeleteIssuesSettingInput!): UpdateEnterpriseMembersCanDeleteIssuesSettingPayload
+
+ """
+ Sets the members can delete repositories setting for an enterprise.
+ """
+ updateEnterpriseMembersCanDeleteRepositoriesSetting(input: UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput!): UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload
+
+ """
+ Sets whether members can invite collaborators are enabled for an enterprise.
+ """
+ updateEnterpriseMembersCanInviteCollaboratorsSetting(input: UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput!): UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload
+
+ """
+ Sets whether or not an organization admin can make purchases.
+ """
+ updateEnterpriseMembersCanMakePurchasesSetting(input: UpdateEnterpriseMembersCanMakePurchasesSettingInput!): UpdateEnterpriseMembersCanMakePurchasesSettingPayload
+
+ """
+ Sets the members can update protected branches setting for an enterprise.
+ """
+ updateEnterpriseMembersCanUpdateProtectedBranchesSetting(input: UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput!): UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload
+
+ """
+ Sets the members can view dependency insights for an enterprise.
+ """
+ updateEnterpriseMembersCanViewDependencyInsightsSetting(input: UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput!): UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload
+
+ """
+ Sets whether organization projects are enabled for an enterprise.
+ """
+ updateEnterpriseOrganizationProjectsSetting(input: UpdateEnterpriseOrganizationProjectsSettingInput!): UpdateEnterpriseOrganizationProjectsSettingPayload
+
+ """
+ Updates an enterprise's profile.
+ """
+ updateEnterpriseProfile(input: UpdateEnterpriseProfileInput!): UpdateEnterpriseProfilePayload
+
+ """
+ Sets whether repository projects are enabled for a enterprise.
+ """
+ updateEnterpriseRepositoryProjectsSetting(input: UpdateEnterpriseRepositoryProjectsSettingInput!): UpdateEnterpriseRepositoryProjectsSettingPayload
+
+ """
+ Sets whether team discussions are enabled for an enterprise.
+ """
+ updateEnterpriseTeamDiscussionsSetting(input: UpdateEnterpriseTeamDiscussionsSettingInput!): UpdateEnterpriseTeamDiscussionsSettingPayload
+
+ """
+ Sets whether two factor authentication is required for all users in an enterprise.
+ """
+ updateEnterpriseTwoFactorAuthenticationRequiredSetting(input: UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput!): UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload
+
+ """
+ Sets whether an IP allow list is enabled on an owner.
+ """
+ updateIpAllowListEnabledSetting(input: UpdateIpAllowListEnabledSettingInput!): UpdateIpAllowListEnabledSettingPayload
+
+ """
+ Updates an IP allow list entry.
+ """
+ updateIpAllowListEntry(input: UpdateIpAllowListEntryInput!): UpdateIpAllowListEntryPayload
+
+ """
+ Updates an Issue.
+ """
+ updateIssue(input: UpdateIssueInput!): UpdateIssuePayload
+
+ """
+ Updates an IssueComment object.
+ """
+ updateIssueComment(input: UpdateIssueCommentInput!): UpdateIssueCommentPayload
+
+ """
+ Updates an existing label.
+ """
+ updateLabel(input: UpdateLabelInput!): UpdateLabelPayload @preview(toggledBy: "bane-preview")
+
+ """
+ Updates an existing project.
+ """
+ updateProject(input: UpdateProjectInput!): UpdateProjectPayload
+
+ """
+ Updates an existing project card.
+ """
+ updateProjectCard(input: UpdateProjectCardInput!): UpdateProjectCardPayload
+
+ """
+ Updates an existing project column.
+ """
+ updateProjectColumn(input: UpdateProjectColumnInput!): UpdateProjectColumnPayload
+
+ """
+ Update a pull request
+ """
+ updatePullRequest(input: UpdatePullRequestInput!): UpdatePullRequestPayload
+
+ """
+ Updates the body of a pull request review.
+ """
+ updatePullRequestReview(input: UpdatePullRequestReviewInput!): UpdatePullRequestReviewPayload
+
+ """
+ Updates a pull request review comment.
+ """
+ updatePullRequestReviewComment(input: UpdatePullRequestReviewCommentInput!): UpdatePullRequestReviewCommentPayload
+
+ """
+ Update a Git Ref.
+ """
+ updateRef(input: UpdateRefInput!): UpdateRefPayload
+
+ """
+ Creates, updates and/or deletes multiple refs in a repository.
+
+ This mutation takes a list of `RefUpdate`s and performs these updates
+ on the repository. All updates are performed atomically, meaning that
+ if one of them is rejected, no other ref will be modified.
+
+ `RefUpdate.beforeOid` specifies that the given reference needs to point
+ to the given value before performing any updates. A value of
+ `0000000000000000000000000000000000000000` can be used to verify that
+ the references should not exist.
+
+ `RefUpdate.afterOid` specifies the value that the given reference
+ will point to after performing all updates. A value of
+ `0000000000000000000000000000000000000000` can be used to delete a
+ reference.
+
+ If `RefUpdate.force` is set to `true`, a non-fast-forward updates
+ for the given reference will be allowed.
+ """
+ updateRefs(input: UpdateRefsInput!): UpdateRefsPayload @preview(toggledBy: "update-refs-preview")
+
+ """
+ Update information about a repository.
+ """
+ updateRepository(input: UpdateRepositoryInput!): UpdateRepositoryPayload
+
+ """
+ Updates the state for subscribable subjects.
+ """
+ updateSubscription(input: UpdateSubscriptionInput!): UpdateSubscriptionPayload
+
+ """
+ Updates a team discussion.
+ """
+ updateTeamDiscussion(input: UpdateTeamDiscussionInput!): UpdateTeamDiscussionPayload
+
+ """
+ Updates a discussion comment.
+ """
+ updateTeamDiscussionComment(input: UpdateTeamDiscussionCommentInput!): UpdateTeamDiscussionCommentPayload
+
+ """
+ Updates team review assignment.
+ """
+ updateTeamReviewAssignment(input: UpdateTeamReviewAssignmentInput!): UpdateTeamReviewAssignmentPayload @preview(toggledBy: "stone-crop-preview")
+
+ """
+ Replaces the repository's topics with the given topics.
+ """
+ updateTopics(input: UpdateTopicsInput!): UpdateTopicsPayload
+
+ """
+ Verify that a verifiable domain has the expected DNS record.
+ """
+ verifyVerifiableDomain(input: VerifyVerifiableDomainInput!): VerifyVerifiableDomainPayload
+}
+
+"""
+An object with an ID.
+"""
+interface Node {
+ """
+ ID of the object.
+ """
+ id: ID!
+}
+
+"""
+Metadata for an audit entry with action oauth_application.*
+"""
+interface OauthApplicationAuditEntryData {
+ """
+ The name of the OAuth Application.
+ """
+ oauthApplicationName: String
+
+ """
+ The HTTP path for the OAuth Application
+ """
+ oauthApplicationResourcePath: URI
+
+ """
+ The HTTP URL for the OAuth Application
+ """
+ oauthApplicationUrl: URI
+}
+
+"""
+Audit log entry for a oauth_application.create event.
+"""
+type OauthApplicationCreateAuditEntry implements AuditEntry & Node & OauthApplicationAuditEntryData & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The application URL of the OAuth Application.
+ """
+ applicationUrl: URI
+
+ """
+ The callback URL of the OAuth Application.
+ """
+ callbackUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The name of the OAuth Application.
+ """
+ oauthApplicationName: String
+
+ """
+ The HTTP path for the OAuth Application
+ """
+ oauthApplicationResourcePath: URI
+
+ """
+ The HTTP URL for the OAuth Application
+ """
+ oauthApplicationUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The rate limit of the OAuth Application.
+ """
+ rateLimit: Int
+
+ """
+ The state of the OAuth Application.
+ """
+ state: OauthApplicationCreateAuditEntryState
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The state of an OAuth Application when it was created.
+"""
+enum OauthApplicationCreateAuditEntryState {
+ """
+ The OAuth Application was active and allowed to have OAuth Accesses.
+ """
+ ACTIVE
+
+ """
+ The OAuth Application was in the process of being deleted.
+ """
+ PENDING_DELETION
+
+ """
+ The OAuth Application was suspended from generating OAuth Accesses due to abuse or security concerns.
+ """
+ SUSPENDED
+}
+
+"""
+The corresponding operation type for the action
+"""
+enum OperationType {
+ """
+ An existing resource was accessed
+ """
+ ACCESS
+
+ """
+ A resource performed an authentication event
+ """
+ AUTHENTICATION
+
+ """
+ A new resource was created
+ """
+ CREATE
+
+ """
+ An existing resource was modified
+ """
+ MODIFY
+
+ """
+ An existing resource was removed
+ """
+ REMOVE
+
+ """
+ An existing resource was restored
+ """
+ RESTORE
+
+ """
+ An existing resource was transferred between multiple resources
+ """
+ TRANSFER
+}
+
+"""
+Possible directions in which to order a list of items when provided an `orderBy` argument.
+"""
+enum OrderDirection {
+ """
+ Specifies an ascending order for a given `orderBy` argument.
+ """
+ ASC
+
+ """
+ Specifies a descending order for a given `orderBy` argument.
+ """
+ DESC
+}
+
+"""
+Audit log entry for a org.add_billing_manager
+"""
+type OrgAddBillingManagerAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The email address used to invite a billing manager for the organization.
+ """
+ invitationEmail: String
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.add_member
+"""
+type OrgAddMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The permission level of the member added to the organization.
+ """
+ permission: OrgAddMemberAuditEntryPermission
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The permissions available to members on an Organization.
+"""
+enum OrgAddMemberAuditEntryPermission {
+ """
+ Can read, clone, push, and add collaborators to repositories.
+ """
+ ADMIN
+
+ """
+ Can read and clone repositories.
+ """
+ READ
+}
+
+"""
+Audit log entry for a org.block_user
+"""
+type OrgBlockUserAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The blocked user.
+ """
+ blockedUser: User
+
+ """
+ The username of the blocked user.
+ """
+ blockedUserName: String
+
+ """
+ The HTTP path for the blocked user.
+ """
+ blockedUserResourcePath: URI
+
+ """
+ The HTTP URL for the blocked user.
+ """
+ blockedUserUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.config.disable_collaborators_only event.
+"""
+type OrgConfigDisableCollaboratorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.config.enable_collaborators_only event.
+"""
+type OrgConfigEnableCollaboratorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.create event.
+"""
+type OrgCreateAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The billing plan for the Organization.
+ """
+ billingPlan: OrgCreateAuditEntryBillingPlan
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The billing plans available for organizations.
+"""
+enum OrgCreateAuditEntryBillingPlan {
+ """
+ Team Plan
+ """
+ BUSINESS
+
+ """
+ Enterprise Cloud Plan
+ """
+ BUSINESS_PLUS
+
+ """
+ Free Plan
+ """
+ FREE
+
+ """
+ Tiered Per Seat Plan
+ """
+ TIERED_PER_SEAT
+
+ """
+ Legacy Unlimited Plan
+ """
+ UNLIMITED
+}
+
+"""
+Audit log entry for a org.disable_oauth_app_restrictions event.
+"""
+type OrgDisableOauthAppRestrictionsAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.disable_saml event.
+"""
+type OrgDisableSamlAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The SAML provider's digest algorithm URL.
+ """
+ digestMethodUrl: URI
+ id: ID!
+
+ """
+ The SAML provider's issuer URL.
+ """
+ issuerUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The SAML provider's signature algorithm URL.
+ """
+ signatureMethodUrl: URI
+
+ """
+ The SAML provider's single sign-on URL.
+ """
+ singleSignOnUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.disable_two_factor_requirement event.
+"""
+type OrgDisableTwoFactorRequirementAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.enable_oauth_app_restrictions event.
+"""
+type OrgEnableOauthAppRestrictionsAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.enable_saml event.
+"""
+type OrgEnableSamlAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The SAML provider's digest algorithm URL.
+ """
+ digestMethodUrl: URI
+ id: ID!
+
+ """
+ The SAML provider's issuer URL.
+ """
+ issuerUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The SAML provider's signature algorithm URL.
+ """
+ signatureMethodUrl: URI
+
+ """
+ The SAML provider's single sign-on URL.
+ """
+ singleSignOnUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.enable_two_factor_requirement event.
+"""
+type OrgEnableTwoFactorRequirementAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.invite_member event.
+"""
+type OrgInviteMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The email address of the organization invitation.
+ """
+ email: String
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The organization invitation.
+ """
+ organizationInvitation: OrganizationInvitation
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.invite_to_business event.
+"""
+type OrgInviteToBusinessAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.oauth_app_access_approved event.
+"""
+type OrgOauthAppAccessApprovedAuditEntry implements AuditEntry & Node & OauthApplicationAuditEntryData & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The name of the OAuth Application.
+ """
+ oauthApplicationName: String
+
+ """
+ The HTTP path for the OAuth Application
+ """
+ oauthApplicationResourcePath: URI
+
+ """
+ The HTTP URL for the OAuth Application
+ """
+ oauthApplicationUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.oauth_app_access_denied event.
+"""
+type OrgOauthAppAccessDeniedAuditEntry implements AuditEntry & Node & OauthApplicationAuditEntryData & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The name of the OAuth Application.
+ """
+ oauthApplicationName: String
+
+ """
+ The HTTP path for the OAuth Application
+ """
+ oauthApplicationResourcePath: URI
+
+ """
+ The HTTP URL for the OAuth Application
+ """
+ oauthApplicationUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.oauth_app_access_requested event.
+"""
+type OrgOauthAppAccessRequestedAuditEntry implements AuditEntry & Node & OauthApplicationAuditEntryData & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The name of the OAuth Application.
+ """
+ oauthApplicationName: String
+
+ """
+ The HTTP path for the OAuth Application
+ """
+ oauthApplicationResourcePath: URI
+
+ """
+ The HTTP URL for the OAuth Application
+ """
+ oauthApplicationUrl: URI
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.remove_billing_manager event.
+"""
+type OrgRemoveBillingManagerAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The reason for the billing manager being removed.
+ """
+ reason: OrgRemoveBillingManagerAuditEntryReason
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The reason a billing manager was removed from an Organization.
+"""
+enum OrgRemoveBillingManagerAuditEntryReason {
+ """
+ SAML external identity missing
+ """
+ SAML_EXTERNAL_IDENTITY_MISSING
+
+ """
+ SAML SSO enforcement requires an external identity
+ """
+ SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY
+
+ """
+ The organization required 2FA of its billing managers and this user did not have 2FA enabled.
+ """
+ TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE
+}
+
+"""
+Audit log entry for a org.remove_member event.
+"""
+type OrgRemoveMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The types of membership the member has with the organization.
+ """
+ membershipTypes: [OrgRemoveMemberAuditEntryMembershipType!]
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The reason for the member being removed.
+ """
+ reason: OrgRemoveMemberAuditEntryReason
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The type of membership a user has with an Organization.
+"""
+enum OrgRemoveMemberAuditEntryMembershipType {
+ """
+ Organization administrators have full access and can change several settings,
+ including the names of repositories that belong to the Organization and Owners
+ team membership. In addition, organization admins can delete the organization
+ and all of its repositories.
+ """
+ ADMIN
+
+ """
+ A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.
+ """
+ BILLING_MANAGER
+
+ """
+ A direct member is a user that is a member of the Organization.
+ """
+ DIRECT_MEMBER
+
+ """
+ An outside collaborator is a person who isn't explicitly a member of the
+ Organization, but who has Read, Write, or Admin permissions to one or more
+ repositories in the organization.
+ """
+ OUTSIDE_COLLABORATOR
+
+ """
+ An unaffiliated collaborator is a person who is not a member of the
+ Organization and does not have access to any repositories in the Organization.
+ """
+ UNAFFILIATED
+}
+
+"""
+The reason a member was removed from an Organization.
+"""
+enum OrgRemoveMemberAuditEntryReason {
+ """
+ SAML external identity missing
+ """
+ SAML_EXTERNAL_IDENTITY_MISSING
+
+ """
+ SAML SSO enforcement requires an external identity
+ """
+ SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY
+
+ """
+ User was removed from organization during account recovery
+ """
+ TWO_FACTOR_ACCOUNT_RECOVERY
+
+ """
+ The organization required 2FA of its billing managers and this user did not have 2FA enabled.
+ """
+ TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE
+
+ """
+ User account has been deleted
+ """
+ USER_ACCOUNT_DELETED
+}
+
+"""
+Audit log entry for a org.remove_outside_collaborator event.
+"""
+type OrgRemoveOutsideCollaboratorAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The types of membership the outside collaborator has with the organization.
+ """
+ membershipTypes: [OrgRemoveOutsideCollaboratorAuditEntryMembershipType!]
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The reason for the outside collaborator being removed from the Organization.
+ """
+ reason: OrgRemoveOutsideCollaboratorAuditEntryReason
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The type of membership a user has with an Organization.
+"""
+enum OrgRemoveOutsideCollaboratorAuditEntryMembershipType {
+ """
+ A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.
+ """
+ BILLING_MANAGER
+
+ """
+ An outside collaborator is a person who isn't explicitly a member of the
+ Organization, but who has Read, Write, or Admin permissions to one or more
+ repositories in the organization.
+ """
+ OUTSIDE_COLLABORATOR
+
+ """
+ An unaffiliated collaborator is a person who is not a member of the
+ Organization and does not have access to any repositories in the organization.
+ """
+ UNAFFILIATED
+}
+
+"""
+The reason an outside collaborator was removed from an Organization.
+"""
+enum OrgRemoveOutsideCollaboratorAuditEntryReason {
+ """
+ SAML external identity missing
+ """
+ SAML_EXTERNAL_IDENTITY_MISSING
+
+ """
+ The organization required 2FA of its billing managers and this user did not have 2FA enabled.
+ """
+ TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE
+}
+
+"""
+Audit log entry for a org.restore_member event.
+"""
+type OrgRestoreMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The number of custom email routings for the restored member.
+ """
+ restoredCustomEmailRoutingsCount: Int
+
+ """
+ The number of issue assignments for the restored member.
+ """
+ restoredIssueAssignmentsCount: Int
+
+ """
+ Restored organization membership objects.
+ """
+ restoredMemberships: [OrgRestoreMemberAuditEntryMembership!]
+
+ """
+ The number of restored memberships.
+ """
+ restoredMembershipsCount: Int
+
+ """
+ The number of repositories of the restored member.
+ """
+ restoredRepositoriesCount: Int
+
+ """
+ The number of starred repositories for the restored member.
+ """
+ restoredRepositoryStarsCount: Int
+
+ """
+ The number of watched repositories for the restored member.
+ """
+ restoredRepositoryWatchesCount: Int
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Types of memberships that can be restored for an Organization member.
+"""
+union OrgRestoreMemberAuditEntryMembership = OrgRestoreMemberMembershipOrganizationAuditEntryData | OrgRestoreMemberMembershipRepositoryAuditEntryData | OrgRestoreMemberMembershipTeamAuditEntryData
+
+"""
+Metadata for an organization membership for org.restore_member actions
+"""
+type OrgRestoreMemberMembershipOrganizationAuditEntryData implements OrganizationAuditEntryData {
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+}
+
+"""
+Metadata for a repository membership for org.restore_member actions
+"""
+type OrgRestoreMemberMembershipRepositoryAuditEntryData implements RepositoryAuditEntryData {
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+}
+
+"""
+Metadata for a team membership for org.restore_member actions
+"""
+type OrgRestoreMemberMembershipTeamAuditEntryData implements TeamAuditEntryData {
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+}
+
+"""
+Audit log entry for a org.unblock_user
+"""
+type OrgUnblockUserAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The user being unblocked by the organization.
+ """
+ blockedUser: User
+
+ """
+ The username of the blocked user.
+ """
+ blockedUserName: String
+
+ """
+ The HTTP path for the blocked user.
+ """
+ blockedUserResourcePath: URI
+
+ """
+ The HTTP URL for the blocked user.
+ """
+ blockedUserUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a org.update_default_repository_permission
+"""
+type OrgUpdateDefaultRepositoryPermissionAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The new default repository permission level for the organization.
+ """
+ permission: OrgUpdateDefaultRepositoryPermissionAuditEntryPermission
+
+ """
+ The former default repository permission level for the organization.
+ """
+ permissionWas: OrgUpdateDefaultRepositoryPermissionAuditEntryPermission
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The default permission a repository can have in an Organization.
+"""
+enum OrgUpdateDefaultRepositoryPermissionAuditEntryPermission {
+ """
+ Can read, clone, push, and add collaborators to repositories.
+ """
+ ADMIN
+
+ """
+ No default permission value.
+ """
+ NONE
+
+ """
+ Can read and clone repositories.
+ """
+ READ
+
+ """
+ Can read, clone and push to repositories.
+ """
+ WRITE
+}
+
+"""
+Audit log entry for a org.update_member event.
+"""
+type OrgUpdateMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The new member permission level for the organization.
+ """
+ permission: OrgUpdateMemberAuditEntryPermission
+
+ """
+ The former member permission level for the organization.
+ """
+ permissionWas: OrgUpdateMemberAuditEntryPermission
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The permissions available to members on an Organization.
+"""
+enum OrgUpdateMemberAuditEntryPermission {
+ """
+ Can read, clone, push, and add collaborators to repositories.
+ """
+ ADMIN
+
+ """
+ Can read and clone repositories.
+ """
+ READ
+}
+
+"""
+Audit log entry for a org.update_member_repository_creation_permission event.
+"""
+type OrgUpdateMemberRepositoryCreationPermissionAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ Can members create repositories in the organization.
+ """
+ canCreateRepositories: Boolean
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The permission for visibility level of repositories for this organization.
+ """
+ visibility: OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility
+}
+
+"""
+The permissions available for repository creation on an Organization.
+"""
+enum OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility {
+ """
+ All organization members are restricted from creating any repositories.
+ """
+ ALL
+
+ """
+ All organization members are restricted from creating internal repositories.
+ """
+ INTERNAL
+
+ """
+ All organization members are allowed to create any repositories.
+ """
+ NONE
+
+ """
+ All organization members are restricted from creating private repositories.
+ """
+ PRIVATE
+
+ """
+ All organization members are restricted from creating private or internal repositories.
+ """
+ PRIVATE_INTERNAL
+
+ """
+ All organization members are restricted from creating public repositories.
+ """
+ PUBLIC
+
+ """
+ All organization members are restricted from creating public or internal repositories.
+ """
+ PUBLIC_INTERNAL
+
+ """
+ All organization members are restricted from creating public or private repositories.
+ """
+ PUBLIC_PRIVATE
+}
+
+"""
+Audit log entry for a org.update_member_repository_invitation_permission event.
+"""
+type OrgUpdateMemberRepositoryInvitationPermissionAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ Can outside collaborators be invited to repositories in the organization.
+ """
+ canInviteOutsideCollaboratorsToRepositories: Boolean
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+An account on GitHub, with one or more owners, that has repositories, members and teams.
+"""
+type Organization implements Actor & MemberStatusable & Node & PackageOwner & ProfileOwner & ProjectOwner & RepositoryOwner & Sponsorable & UniformResourceLocatable {
+ """
+ Determine if this repository owner has any items that can be pinned to their profile.
+ """
+ anyPinnableItems(
+ """
+ Filter to only a particular kind of pinnable item.
+ """
+ type: PinnableItemType
+ ): Boolean!
+
+ """
+ Audit log entries of the organization
+ """
+ auditLog(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for the returned audit log entries.
+ """
+ orderBy: AuditLogOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ The query string to filter audit entries
+ """
+ query: String
+ ): OrganizationAuditEntryConnection!
+
+ """
+ A URL pointing to the organization's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The organization's public profile description.
+ """
+ description: String
+
+ """
+ The organization's public profile description rendered to HTML.
+ """
+ descriptionHTML: String
+
+ """
+ A list of domains owned by the organization.
+ """
+ domains(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Filter by if the domain is verified.
+ """
+ isVerified: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for verifiable domains returned.
+ """
+ orderBy: VerifiableDomainOrder = {field: DOMAIN, direction: ASC}
+ ): VerifiableDomainConnection
+
+ """
+ The organization's public email.
+ """
+ email: String
+
+ """
+ True if this user/organization has a GitHub Sponsors listing.
+ """
+ hasSponsorsListing: Boolean!
+ id: ID!
+
+ """
+ The interaction ability settings for this organization.
+ """
+ interactionAbility: RepositoryInteractionAbility
+
+ """
+ The setting value for whether the organization has an IP allow list enabled.
+ """
+ ipAllowListEnabledSetting: IpAllowListEnabledSettingValue!
+
+ """
+ The IP addresses that are allowed to access resources owned by the organization.
+ """
+ ipAllowListEntries(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for IP allow list entries returned.
+ """
+ orderBy: IpAllowListEntryOrder = {field: ALLOW_LIST_VALUE, direction: ASC}
+ ): IpAllowListEntryConnection!
+
+ """
+ True if the viewer is sponsored by this user/organization.
+ """
+ isSponsoringViewer: Boolean!
+
+ """
+ Whether the organization has verified its profile email and website, always false on Enterprise.
+ """
+ isVerified: Boolean!
+
+ """
+ Showcases a selection of repositories and gists that the profile owner has
+ either curated or that have been selected automatically based on popularity.
+ """
+ itemShowcase: ProfileItemShowcase!
+
+ """
+ The organization's public profile location.
+ """
+ location: String
+
+ """
+ The organization's login name.
+ """
+ login: String!
+
+ """
+ Get the status messages members of this entity have set that are either public or visible only to the organization.
+ """
+ memberStatuses(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for user statuses returned from the connection.
+ """
+ orderBy: UserStatusOrder = {field: UPDATED_AT, direction: DESC}
+ ): UserStatusConnection!
+
+ """
+ A list of users who are members of this organization.
+ """
+ membersWithRole(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): OrganizationMemberConnection!
+
+ """
+ The organization's public profile name.
+ """
+ name: String
+
+ """
+ The HTTP path creating a new team
+ """
+ newTeamResourcePath: URI!
+
+ """
+ The HTTP URL creating a new team
+ """
+ newTeamUrl: URI!
+
+ """
+ The billing email for the organization.
+ """
+ organizationBillingEmail: String
+
+ """
+ A list of packages under the owner.
+ """
+ packages(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Find packages by their names.
+ """
+ names: [String]
+
+ """
+ Ordering of the returned packages.
+ """
+ orderBy: PackageOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ Filter registry package by type.
+ """
+ packageType: PackageType
+
+ """
+ Find packages in a repository by ID.
+ """
+ repositoryId: ID
+ ): PackageConnection!
+
+ """
+ A list of users who have been invited to join this organization.
+ """
+ pendingMembers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ A list of repositories and gists this profile owner can pin to their profile.
+ """
+ pinnableItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinnable items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ A list of repositories and gists this profile owner has pinned to their profile
+ """
+ pinnedItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinned items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ Returns how many more items this profile owner can pin to their profile.
+ """
+ pinnedItemsRemaining: Int!
+
+ """
+ Find project by number.
+ """
+ project(
+ """
+ The project number to find.
+ """
+ number: Int!
+ ): Project
+
+ """
+ A list of projects under the owner.
+ """
+ projects(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for projects returned from the connection
+ """
+ orderBy: ProjectOrder
+
+ """
+ Query to search projects by, currently only searching by name.
+ """
+ search: String
+
+ """
+ A list of states to filter the projects by.
+ """
+ states: [ProjectState!]
+ ): ProjectConnection!
+
+ """
+ The HTTP path listing organization's projects
+ """
+ projectsResourcePath: URI!
+
+ """
+ The HTTP URL listing organization's projects
+ """
+ projectsUrl: URI!
+
+ """
+ A list of repositories that the user owns.
+ """
+ repositories(
+ """
+ Array of viewer's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ current viewer owns.
+ """
+ affiliations: [RepositoryAffiliation]
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If non-null, filters repositories according to whether they are forks of another repository
+ """
+ isFork: Boolean
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ Array of owner's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ organization or user being viewed owns.
+ """
+ ownerAffiliations: [RepositoryAffiliation] = [OWNER, COLLABORATOR]
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ Find Repository.
+ """
+ repository(
+ """
+ Name of Repository to find.
+ """
+ name: String!
+ ): Repository
+
+ """
+ When true the organization requires all members, billing managers, and outside
+ collaborators to enable two-factor authentication.
+ """
+ requiresTwoFactorAuthentication: Boolean
+
+ """
+ The HTTP path for this organization.
+ """
+ resourcePath: URI!
+
+ """
+ The Organization's SAML identity providers
+ """
+ samlIdentityProvider: OrganizationIdentityProvider
+
+ """
+ The GitHub Sponsors listing for this user or organization.
+ """
+ sponsorsListing: SponsorsListing
+
+ """
+ This object's sponsorships as the maintainer.
+ """
+ sponsorshipsAsMaintainer(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Whether or not to include private sponsorships in the result set
+ """
+ includePrivate: Boolean = false
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ This object's sponsorships as the sponsor.
+ """
+ sponsorshipsAsSponsor(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ Find an organization's team by its slug.
+ """
+ team(
+ """
+ The name or slug of the team to find.
+ """
+ slug: String!
+ ): Team
+
+ """
+ A list of teams in this organization.
+ """
+ teams(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ If true, filters teams that are mapped to an LDAP Group (Enterprise only)
+ """
+ ldapMapped: Boolean
+
+ """
+ Ordering options for teams returned from the connection
+ """
+ orderBy: TeamOrder
+
+ """
+ If non-null, filters teams according to privacy
+ """
+ privacy: TeamPrivacy
+
+ """
+ If non-null, filters teams with query on team name and team slug
+ """
+ query: String
+
+ """
+ If non-null, filters teams according to whether the viewer is an admin or member on team
+ """
+ role: TeamRole
+
+ """
+ If true, restrict to only root teams
+ """
+ rootTeamsOnly: Boolean = false
+
+ """
+ User logins to filter by
+ """
+ userLogins: [String!]
+ ): TeamConnection!
+
+ """
+ The HTTP path listing organization's teams
+ """
+ teamsResourcePath: URI!
+
+ """
+ The HTTP URL listing organization's teams
+ """
+ teamsUrl: URI!
+
+ """
+ The organization's Twitter username.
+ """
+ twitterUsername: String
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this organization.
+ """
+ url: URI!
+
+ """
+ Organization is adminable by the viewer.
+ """
+ viewerCanAdminister: Boolean!
+
+ """
+ Can the viewer pin repositories and gists to the profile?
+ """
+ viewerCanChangePinnedItems: Boolean!
+
+ """
+ Can the current viewer create new projects on this owner.
+ """
+ viewerCanCreateProjects: Boolean!
+
+ """
+ Viewer can create repositories on this organization
+ """
+ viewerCanCreateRepositories: Boolean!
+
+ """
+ Viewer can create teams on this organization.
+ """
+ viewerCanCreateTeams: Boolean!
+
+ """
+ Whether or not the viewer is able to sponsor this user/organization.
+ """
+ viewerCanSponsor: Boolean!
+
+ """
+ Viewer is an active member of this organization.
+ """
+ viewerIsAMember: Boolean!
+
+ """
+ True if the viewer is sponsoring this user/organization.
+ """
+ viewerIsSponsoring: Boolean!
+
+ """
+ The organization's public profile URL.
+ """
+ websiteUrl: URI
+}
+
+"""
+An audit entry in an organization audit log.
+"""
+union OrganizationAuditEntry = MembersCanDeleteReposClearAuditEntry | MembersCanDeleteReposDisableAuditEntry | MembersCanDeleteReposEnableAuditEntry | OauthApplicationCreateAuditEntry | OrgAddBillingManagerAuditEntry | OrgAddMemberAuditEntry | OrgBlockUserAuditEntry | OrgConfigDisableCollaboratorsOnlyAuditEntry | OrgConfigEnableCollaboratorsOnlyAuditEntry | OrgCreateAuditEntry | OrgDisableOauthAppRestrictionsAuditEntry | OrgDisableSamlAuditEntry | OrgDisableTwoFactorRequirementAuditEntry | OrgEnableOauthAppRestrictionsAuditEntry | OrgEnableSamlAuditEntry | OrgEnableTwoFactorRequirementAuditEntry | OrgInviteMemberAuditEntry | OrgInviteToBusinessAuditEntry | OrgOauthAppAccessApprovedAuditEntry | OrgOauthAppAccessDeniedAuditEntry | OrgOauthAppAccessRequestedAuditEntry | OrgRemoveBillingManagerAuditEntry | OrgRemoveMemberAuditEntry | OrgRemoveOutsideCollaboratorAuditEntry | OrgRestoreMemberAuditEntry | OrgUnblockUserAuditEntry | OrgUpdateDefaultRepositoryPermissionAuditEntry | OrgUpdateMemberAuditEntry | OrgUpdateMemberRepositoryCreationPermissionAuditEntry | OrgUpdateMemberRepositoryInvitationPermissionAuditEntry | PrivateRepositoryForkingDisableAuditEntry | PrivateRepositoryForkingEnableAuditEntry | RepoAccessAuditEntry | RepoAddMemberAuditEntry | RepoAddTopicAuditEntry | RepoArchivedAuditEntry | RepoChangeMergeSettingAuditEntry | RepoConfigDisableAnonymousGitAccessAuditEntry | RepoConfigDisableCollaboratorsOnlyAuditEntry | RepoConfigDisableContributorsOnlyAuditEntry | RepoConfigDisableSockpuppetDisallowedAuditEntry | RepoConfigEnableAnonymousGitAccessAuditEntry | RepoConfigEnableCollaboratorsOnlyAuditEntry | RepoConfigEnableContributorsOnlyAuditEntry | RepoConfigEnableSockpuppetDisallowedAuditEntry | RepoConfigLockAnonymousGitAccessAuditEntry | RepoConfigUnlockAnonymousGitAccessAuditEntry | RepoCreateAuditEntry | RepoDestroyAuditEntry | RepoRemoveMemberAuditEntry | RepoRemoveTopicAuditEntry | RepositoryVisibilityChangeDisableAuditEntry | RepositoryVisibilityChangeEnableAuditEntry | TeamAddMemberAuditEntry | TeamAddRepositoryAuditEntry | TeamChangeParentTeamAuditEntry | TeamRemoveMemberAuditEntry | TeamRemoveRepositoryAuditEntry
+
+"""
+The connection type for OrganizationAuditEntry.
+"""
+type OrganizationAuditEntryConnection {
+ """
+ A list of edges.
+ """
+ edges: [OrganizationAuditEntryEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [OrganizationAuditEntry]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Metadata for an audit entry with action org.*
+"""
+interface OrganizationAuditEntryData {
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+}
+
+"""
+An edge in a connection.
+"""
+type OrganizationAuditEntryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: OrganizationAuditEntry
+}
+
+"""
+The connection type for Organization.
+"""
+type OrganizationConnection {
+ """
+ A list of edges.
+ """
+ edges: [OrganizationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Organization]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type OrganizationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Organization
+}
+
+"""
+An Identity Provider configured to provision SAML and SCIM identities for Organizations
+"""
+type OrganizationIdentityProvider implements Node {
+ """
+ The digest algorithm used to sign SAML requests for the Identity Provider.
+ """
+ digestMethod: URI
+
+ """
+ External Identities provisioned by this Identity Provider
+ """
+ externalIdentities(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ExternalIdentityConnection!
+ id: ID!
+
+ """
+ The x509 certificate used by the Identity Provider to sign assertions and responses.
+ """
+ idpCertificate: X509Certificate
+
+ """
+ The Issuer Entity ID for the SAML Identity Provider
+ """
+ issuer: String
+
+ """
+ Organization this Identity Provider belongs to
+ """
+ organization: Organization
+
+ """
+ The signature algorithm used to sign SAML requests for the Identity Provider.
+ """
+ signatureMethod: URI
+
+ """
+ The URL endpoint for the Identity Provider's SAML SSO.
+ """
+ ssoUrl: URI
+}
+
+"""
+An Invitation for a user to an organization.
+"""
+type OrganizationInvitation implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The email address of the user invited to the organization.
+ """
+ email: String
+ id: ID!
+
+ """
+ The type of invitation that was sent (e.g. email, user).
+ """
+ invitationType: OrganizationInvitationType!
+
+ """
+ The user who was invited to the organization.
+ """
+ invitee: User
+
+ """
+ The user who created the invitation.
+ """
+ inviter: User!
+
+ """
+ The organization the invite is for
+ """
+ organization: Organization!
+
+ """
+ The user's pending role in the organization (e.g. member, owner).
+ """
+ role: OrganizationInvitationRole!
+}
+
+"""
+The connection type for OrganizationInvitation.
+"""
+type OrganizationInvitationConnection {
+ """
+ A list of edges.
+ """
+ edges: [OrganizationInvitationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [OrganizationInvitation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type OrganizationInvitationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: OrganizationInvitation
+}
+
+"""
+The possible organization invitation roles.
+"""
+enum OrganizationInvitationRole {
+ """
+ The user is invited to be an admin of the organization.
+ """
+ ADMIN
+
+ """
+ The user is invited to be a billing manager of the organization.
+ """
+ BILLING_MANAGER
+
+ """
+ The user is invited to be a direct member of the organization.
+ """
+ DIRECT_MEMBER
+
+ """
+ The user's previous role will be reinstated.
+ """
+ REINSTATE
+}
+
+"""
+The possible organization invitation types.
+"""
+enum OrganizationInvitationType {
+ """
+ The invitation was to an email address.
+ """
+ EMAIL
+
+ """
+ The invitation was to an existing user.
+ """
+ USER
+}
+
+"""
+The connection type for User.
+"""
+type OrganizationMemberConnection {
+ """
+ A list of edges.
+ """
+ edges: [OrganizationMemberEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a user within an organization.
+"""
+type OrganizationMemberEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ Whether the organization member has two factor enabled or not. Returns null if information is not available to viewer.
+ """
+ hasTwoFactorEnabled: Boolean
+
+ """
+ The item at the end of the edge.
+ """
+ node: User
+
+ """
+ The role this user has in the organization.
+ """
+ role: OrganizationMemberRole
+}
+
+"""
+The possible roles within an organization for its members.
+"""
+enum OrganizationMemberRole {
+ """
+ The user is an administrator of the organization.
+ """
+ ADMIN
+
+ """
+ The user is a member of the organization.
+ """
+ MEMBER
+}
+
+"""
+The possible values for the members can create repositories setting on an organization.
+"""
+enum OrganizationMembersCanCreateRepositoriesSettingValue {
+ """
+ Members will be able to create public and private repositories.
+ """
+ ALL
+
+ """
+ Members will not be able to create public or private repositories.
+ """
+ DISABLED
+
+ """
+ Members will be able to create only private repositories.
+ """
+ PRIVATE
+}
+
+"""
+Ordering options for organization connections.
+"""
+input OrganizationOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order organizations by.
+ """
+ field: OrganizationOrderField!
+}
+
+"""
+Properties by which organization connections can be ordered.
+"""
+enum OrganizationOrderField {
+ """
+ Order organizations by creation time
+ """
+ CREATED_AT
+
+ """
+ Order organizations by login
+ """
+ LOGIN
+}
+
+"""
+An organization teams hovercard context
+"""
+type OrganizationTeamsHovercardContext implements HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+
+ """
+ Teams in this organization the user is a member of that are relevant
+ """
+ relevantTeams(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): TeamConnection!
+
+ """
+ The path for the full team list for this user
+ """
+ teamsResourcePath: URI!
+
+ """
+ The URL for the full team list for this user
+ """
+ teamsUrl: URI!
+
+ """
+ The total number of teams the user is on in the organization
+ """
+ totalTeamCount: Int!
+}
+
+"""
+An organization list hovercard context
+"""
+type OrganizationsHovercardContext implements HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+
+ """
+ Organizations this user is a member of that are relevant
+ """
+ relevantOrganizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): OrganizationConnection!
+
+ """
+ The total number of organizations this user is in
+ """
+ totalOrganizationCount: Int!
+}
+
+"""
+Information for an uploaded package.
+"""
+type Package implements Node {
+ id: ID!
+
+ """
+ Find the latest version for the package.
+ """
+ latestVersion: PackageVersion
+
+ """
+ Identifies the name of the package.
+ """
+ name: String!
+
+ """
+ Identifies the type of the package.
+ """
+ packageType: PackageType!
+
+ """
+ The repository this package belongs to.
+ """
+ repository: Repository
+
+ """
+ Statistics about package activity.
+ """
+ statistics: PackageStatistics
+
+ """
+ Find package version by version string.
+ """
+ version(
+ """
+ The package version.
+ """
+ version: String!
+ ): PackageVersion
+
+ """
+ list of versions for this package
+ """
+ versions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering of the returned packages.
+ """
+ orderBy: PackageVersionOrder = {field: CREATED_AT, direction: DESC}
+ ): PackageVersionConnection!
+}
+
+"""
+The connection type for Package.
+"""
+type PackageConnection {
+ """
+ A list of edges.
+ """
+ edges: [PackageEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Package]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PackageEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Package
+}
+
+"""
+A file in a package version.
+"""
+type PackageFile implements Node {
+ id: ID!
+
+ """
+ MD5 hash of the file.
+ """
+ md5: String
+
+ """
+ Name of the file.
+ """
+ name: String!
+
+ """
+ The package version this file belongs to.
+ """
+ packageVersion: PackageVersion
+
+ """
+ SHA1 hash of the file.
+ """
+ sha1: String
+
+ """
+ SHA256 hash of the file.
+ """
+ sha256: String
+
+ """
+ Size of the file in bytes.
+ """
+ size: Int
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ URL to download the asset.
+ """
+ url: URI
+}
+
+"""
+The connection type for PackageFile.
+"""
+type PackageFileConnection {
+ """
+ A list of edges.
+ """
+ edges: [PackageFileEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PackageFile]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PackageFileEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PackageFile
+}
+
+"""
+Ways in which lists of package files can be ordered upon return.
+"""
+input PackageFileOrder {
+ """
+ The direction in which to order package files by the specified field.
+ """
+ direction: OrderDirection
+
+ """
+ The field in which to order package files by.
+ """
+ field: PackageFileOrderField
+}
+
+"""
+Properties by which package file connections can be ordered.
+"""
+enum PackageFileOrderField {
+ """
+ Order package files by creation time
+ """
+ CREATED_AT
+}
+
+"""
+Ways in which lists of packages can be ordered upon return.
+"""
+input PackageOrder {
+ """
+ The direction in which to order packages by the specified field.
+ """
+ direction: OrderDirection
+
+ """
+ The field in which to order packages by.
+ """
+ field: PackageOrderField
+}
+
+"""
+Properties by which package connections can be ordered.
+"""
+enum PackageOrderField {
+ """
+ Order packages by creation time
+ """
+ CREATED_AT
+}
+
+"""
+Represents an owner of a package.
+"""
+interface PackageOwner {
+ id: ID!
+
+ """
+ A list of packages under the owner.
+ """
+ packages(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Find packages by their names.
+ """
+ names: [String]
+
+ """
+ Ordering of the returned packages.
+ """
+ orderBy: PackageOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ Filter registry package by type.
+ """
+ packageType: PackageType
+
+ """
+ Find packages in a repository by ID.
+ """
+ repositoryId: ID
+ ): PackageConnection!
+}
+
+"""
+Represents a object that contains package activity statistics such as downloads.
+"""
+type PackageStatistics {
+ """
+ Number of times the package was downloaded since it was created.
+ """
+ downloadsTotalCount: Int!
+}
+
+"""
+A version tag contains the mapping between a tag name and a version.
+"""
+type PackageTag implements Node {
+ id: ID!
+
+ """
+ Identifies the tag name of the version.
+ """
+ name: String!
+
+ """
+ Version that the tag is associated with.
+ """
+ version: PackageVersion
+}
+
+"""
+The possible types of a package.
+"""
+enum PackageType {
+ """
+ A debian package.
+ """
+ DEBIAN
+
+ """
+ A docker image.
+ """
+ DOCKER
+
+ """
+ A maven package.
+ """
+ MAVEN
+
+ """
+ An npm package.
+ """
+ NPM
+
+ """
+ A nuget package.
+ """
+ NUGET
+
+ """
+ A python package.
+ """
+ PYPI
+
+ """
+ A rubygems package.
+ """
+ RUBYGEMS
+}
+
+"""
+Information about a specific package version.
+"""
+type PackageVersion implements Node {
+ """
+ List of files associated with this package version
+ """
+ files(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering of the returned package files.
+ """
+ orderBy: PackageFileOrder = {field: CREATED_AT, direction: ASC}
+ ): PackageFileConnection!
+ id: ID!
+
+ """
+ The package associated with this version.
+ """
+ package: Package
+
+ """
+ The platform this version was built for.
+ """
+ platform: String
+
+ """
+ Whether or not this version is a pre-release.
+ """
+ preRelease: Boolean!
+
+ """
+ The README of this package version.
+ """
+ readme: String
+
+ """
+ The release associated with this package version.
+ """
+ release: Release
+
+ """
+ Statistics about package activity.
+ """
+ statistics: PackageVersionStatistics
+
+ """
+ The package version summary.
+ """
+ summary: String
+
+ """
+ The version string.
+ """
+ version: String!
+}
+
+"""
+The connection type for PackageVersion.
+"""
+type PackageVersionConnection {
+ """
+ A list of edges.
+ """
+ edges: [PackageVersionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PackageVersion]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PackageVersionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PackageVersion
+}
+
+"""
+Ways in which lists of package versions can be ordered upon return.
+"""
+input PackageVersionOrder {
+ """
+ The direction in which to order package versions by the specified field.
+ """
+ direction: OrderDirection
+
+ """
+ The field in which to order package versions by.
+ """
+ field: PackageVersionOrderField
+}
+
+"""
+Properties by which package version connections can be ordered.
+"""
+enum PackageVersionOrderField {
+ """
+ Order package versions by creation time
+ """
+ CREATED_AT
+}
+
+"""
+Represents a object that contains package version activity statistics such as downloads.
+"""
+type PackageVersionStatistics {
+ """
+ Number of times the package was downloaded since it was created.
+ """
+ downloadsTotalCount: Int!
+}
+
+"""
+Information about pagination in a connection.
+"""
+type PageInfo {
+ """
+ When paginating forwards, the cursor to continue.
+ """
+ endCursor: String
+
+ """
+ When paginating forwards, are there more items?
+ """
+ hasNextPage: Boolean!
+
+ """
+ When paginating backwards, are there more items?
+ """
+ hasPreviousPage: Boolean!
+
+ """
+ When paginating backwards, the cursor to continue.
+ """
+ startCursor: String
+}
+
+"""
+Types that can grant permissions on a repository to a user
+"""
+union PermissionGranter = Organization | Repository | Team
+
+"""
+A level of permission and source for a user's access to a repository.
+"""
+type PermissionSource {
+ """
+ The organization the repository belongs to.
+ """
+ organization: Organization!
+
+ """
+ The level of access this source has granted to the user.
+ """
+ permission: DefaultRepositoryPermissionField!
+
+ """
+ The source of this permission.
+ """
+ source: PermissionGranter!
+}
+
+"""
+Autogenerated input type of PinIssue
+"""
+input PinIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the issue to be pinned
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+}
+
+"""
+Autogenerated return type of PinIssue
+"""
+type PinIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue that was pinned
+ """
+ issue: Issue
+}
+
+"""
+Types that can be pinned to a profile page.
+"""
+union PinnableItem = Gist | Repository
+
+"""
+The connection type for PinnableItem.
+"""
+type PinnableItemConnection {
+ """
+ A list of edges.
+ """
+ edges: [PinnableItemEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PinnableItem]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PinnableItemEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PinnableItem
+}
+
+"""
+Represents items that can be pinned to a profile page or dashboard.
+"""
+enum PinnableItemType {
+ """
+ A gist.
+ """
+ GIST
+
+ """
+ An issue.
+ """
+ ISSUE
+
+ """
+ An organization.
+ """
+ ORGANIZATION
+
+ """
+ A project.
+ """
+ PROJECT
+
+ """
+ A pull request.
+ """
+ PULL_REQUEST
+
+ """
+ A repository.
+ """
+ REPOSITORY
+
+ """
+ A team.
+ """
+ TEAM
+
+ """
+ A user.
+ """
+ USER
+}
+
+"""
+Represents a 'pinned' event on a given issue or pull request.
+"""
+type PinnedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the issue associated with the event.
+ """
+ issue: Issue!
+}
+
+"""
+A Pinned Issue is a issue pinned to a repository's index page.
+"""
+type PinnedIssue implements Node @preview(toggledBy: "elektra-preview") {
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The issue that was pinned.
+ """
+ issue: Issue!
+
+ """
+ The actor that pinned this issue.
+ """
+ pinnedBy: Actor!
+
+ """
+ The repository that this issue was pinned to.
+ """
+ repository: Repository!
+}
+
+"""
+The connection type for PinnedIssue.
+"""
+type PinnedIssueConnection @preview(toggledBy: "elektra-preview") {
+ """
+ A list of edges.
+ """
+ edges: [PinnedIssueEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PinnedIssue]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PinnedIssueEdge @preview(toggledBy: "elektra-preview") {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PinnedIssue
+}
+
+"""
+An ISO-8601 encoded UTC date string with millisecond precision.
+"""
+scalar PreciseDateTime
+
+"""
+Audit log entry for a private_repository_forking.disable event.
+"""
+type PrivateRepositoryForkingDisableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a private_repository_forking.enable event.
+"""
+type PrivateRepositoryForkingEnableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+A curatable list of repositories relating to a repository owner, which defaults
+to showing the most popular repositories they own.
+"""
+type ProfileItemShowcase {
+ """
+ Whether or not the owner has pinned any repositories or gists.
+ """
+ hasPinnedItems: Boolean!
+
+ """
+ The repositories and gists in the showcase. If the profile owner has any
+ pinned items, those will be returned. Otherwise, the profile owner's popular
+ repositories will be returned.
+ """
+ items(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PinnableItemConnection!
+}
+
+"""
+Represents any entity on GitHub that has a profile page.
+"""
+interface ProfileOwner {
+ """
+ Determine if this repository owner has any items that can be pinned to their profile.
+ """
+ anyPinnableItems(
+ """
+ Filter to only a particular kind of pinnable item.
+ """
+ type: PinnableItemType
+ ): Boolean!
+
+ """
+ The public profile email.
+ """
+ email: String
+ id: ID!
+
+ """
+ Showcases a selection of repositories and gists that the profile owner has
+ either curated or that have been selected automatically based on popularity.
+ """
+ itemShowcase: ProfileItemShowcase!
+
+ """
+ The public profile location.
+ """
+ location: String
+
+ """
+ The username used to login.
+ """
+ login: String!
+
+ """
+ The public profile name.
+ """
+ name: String
+
+ """
+ A list of repositories and gists this profile owner can pin to their profile.
+ """
+ pinnableItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinnable items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ A list of repositories and gists this profile owner has pinned to their profile
+ """
+ pinnedItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinned items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ Returns how many more items this profile owner can pin to their profile.
+ """
+ pinnedItemsRemaining: Int!
+
+ """
+ Can the viewer pin repositories and gists to the profile?
+ """
+ viewerCanChangePinnedItems: Boolean!
+
+ """
+ The public profile website URL.
+ """
+ websiteUrl: URI
+}
+
+"""
+Projects manage issues, pull requests and notes within a project owner.
+"""
+type Project implements Closable & Node & Updatable {
+ """
+ The project's description body.
+ """
+ body: String
+
+ """
+ The projects description body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ `true` if the object is closed (definition of closed may depend on type)
+ """
+ closed: Boolean!
+
+ """
+ Identifies the date and time when the object was closed.
+ """
+ closedAt: DateTime
+
+ """
+ List of columns in the project
+ """
+ columns(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ProjectColumnConnection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The actor who originally created the project.
+ """
+ creator: Actor
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The project's name.
+ """
+ name: String!
+
+ """
+ The project's number.
+ """
+ number: Int!
+
+ """
+ The project's owner. Currently limited to repositories, organizations, and users.
+ """
+ owner: ProjectOwner!
+
+ """
+ List of pending cards in this project
+ """
+ pendingCards(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ A list of archived states to filter the cards by
+ """
+ archivedStates: [ProjectCardArchivedState] = [ARCHIVED, NOT_ARCHIVED]
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ProjectCardConnection!
+
+ """
+ Project progress details.
+ """
+ progress: ProjectProgress!
+
+ """
+ The HTTP path for this project
+ """
+ resourcePath: URI!
+
+ """
+ Whether the project is open or closed.
+ """
+ state: ProjectState!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this project
+ """
+ url: URI!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+}
+
+"""
+A card in a project.
+"""
+type ProjectCard implements Node {
+ """
+ The project column this card is associated under. A card may only belong to one
+ project column at a time. The column field will be null if the card is created
+ in a pending state and has yet to be associated with a column. Once cards are
+ associated with a column, they will not become pending in the future.
+ """
+ column: ProjectColumn
+
+ """
+ The card content item
+ """
+ content: ProjectCardItem
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The actor who created this card
+ """
+ creator: Actor
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Whether the card is archived
+ """
+ isArchived: Boolean!
+
+ """
+ The card note
+ """
+ note: String
+
+ """
+ The project that contains this card.
+ """
+ project: Project!
+
+ """
+ The HTTP path for this card
+ """
+ resourcePath: URI!
+
+ """
+ The state of ProjectCard
+ """
+ state: ProjectCardState
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this card
+ """
+ url: URI!
+}
+
+"""
+The possible archived states of a project card.
+"""
+enum ProjectCardArchivedState {
+ """
+ A project card that is archived
+ """
+ ARCHIVED
+
+ """
+ A project card that is not archived
+ """
+ NOT_ARCHIVED
+}
+
+"""
+The connection type for ProjectCard.
+"""
+type ProjectCardConnection {
+ """
+ A list of edges.
+ """
+ edges: [ProjectCardEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ProjectCard]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ProjectCardEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ProjectCard
+}
+
+"""
+An issue or PR and its owning repository to be used in a project card.
+"""
+input ProjectCardImport {
+ """
+ The issue or pull request number.
+ """
+ number: Int!
+
+ """
+ Repository name with owner (owner/repository).
+ """
+ repository: String!
+}
+
+"""
+Types that can be inside Project Cards.
+"""
+union ProjectCardItem = Issue | PullRequest
+
+"""
+Various content states of a ProjectCard
+"""
+enum ProjectCardState {
+ """
+ The card has content only.
+ """
+ CONTENT_ONLY
+
+ """
+ The card has a note only.
+ """
+ NOTE_ONLY
+
+ """
+ The card is redacted.
+ """
+ REDACTED
+}
+
+"""
+A column inside a project.
+"""
+type ProjectColumn implements Node {
+ """
+ List of cards in the column
+ """
+ cards(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ A list of archived states to filter the cards by
+ """
+ archivedStates: [ProjectCardArchivedState] = [ARCHIVED, NOT_ARCHIVED]
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ProjectCardConnection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The project column's name.
+ """
+ name: String!
+
+ """
+ The project that contains this column.
+ """
+ project: Project!
+
+ """
+ The semantic purpose of the column
+ """
+ purpose: ProjectColumnPurpose
+
+ """
+ The HTTP path for this project column
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this project column
+ """
+ url: URI!
+}
+
+"""
+The connection type for ProjectColumn.
+"""
+type ProjectColumnConnection {
+ """
+ A list of edges.
+ """
+ edges: [ProjectColumnEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ProjectColumn]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ProjectColumnEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ProjectColumn
+}
+
+"""
+A project column and a list of its issues and PRs.
+"""
+input ProjectColumnImport {
+ """
+ The name of the column.
+ """
+ columnName: String!
+
+ """
+ A list of issues and pull requests in the column.
+ """
+ issues: [ProjectCardImport!]
+
+ """
+ The position of the column, starting from 0.
+ """
+ position: Int!
+}
+
+"""
+The semantic purpose of the column - todo, in progress, or done.
+"""
+enum ProjectColumnPurpose {
+ """
+ The column contains cards which are complete
+ """
+ DONE
+
+ """
+ The column contains cards which are currently being worked on
+ """
+ IN_PROGRESS
+
+ """
+ The column contains cards still to be worked on
+ """
+ TODO
+}
+
+"""
+A list of projects associated with the owner.
+"""
+type ProjectConnection {
+ """
+ A list of edges.
+ """
+ edges: [ProjectEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Project]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ProjectEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Project
+}
+
+"""
+Ways in which lists of projects can be ordered upon return.
+"""
+input ProjectOrder {
+ """
+ The direction in which to order projects by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order projects by.
+ """
+ field: ProjectOrderField!
+}
+
+"""
+Properties by which project connections can be ordered.
+"""
+enum ProjectOrderField {
+ """
+ Order projects by creation time
+ """
+ CREATED_AT
+
+ """
+ Order projects by name
+ """
+ NAME
+
+ """
+ Order projects by update time
+ """
+ UPDATED_AT
+}
+
+"""
+Represents an owner of a Project.
+"""
+interface ProjectOwner {
+ id: ID!
+
+ """
+ Find project by number.
+ """
+ project(
+ """
+ The project number to find.
+ """
+ number: Int!
+ ): Project
+
+ """
+ A list of projects under the owner.
+ """
+ projects(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for projects returned from the connection
+ """
+ orderBy: ProjectOrder
+
+ """
+ Query to search projects by, currently only searching by name.
+ """
+ search: String
+
+ """
+ A list of states to filter the projects by.
+ """
+ states: [ProjectState!]
+ ): ProjectConnection!
+
+ """
+ The HTTP path listing owners projects
+ """
+ projectsResourcePath: URI!
+
+ """
+ The HTTP URL listing owners projects
+ """
+ projectsUrl: URI!
+
+ """
+ Can the current viewer create new projects on this owner.
+ """
+ viewerCanCreateProjects: Boolean!
+}
+
+"""
+Project progress stats.
+"""
+type ProjectProgress {
+ """
+ The number of done cards.
+ """
+ doneCount: Int!
+
+ """
+ The percentage of done cards.
+ """
+ donePercentage: Float!
+
+ """
+ Whether progress tracking is enabled and cards with purpose exist for this project
+ """
+ enabled: Boolean!
+
+ """
+ The number of in-progress cards.
+ """
+ inProgressCount: Int!
+
+ """
+ The percentage of in-progress cards.
+ """
+ inProgressPercentage: Float!
+
+ """
+ The number of to do cards.
+ """
+ todoCount: Int!
+
+ """
+ The percentage of to do cards.
+ """
+ todoPercentage: Float!
+}
+
+"""
+State of the project; either 'open' or 'closed'
+"""
+enum ProjectState {
+ """
+ The project is closed.
+ """
+ CLOSED
+
+ """
+ The project is open.
+ """
+ OPEN
+}
+
+"""
+GitHub-provided templates for Projects
+"""
+enum ProjectTemplate {
+ """
+ Create a board with v2 triggers to automatically move cards across To do, In progress and Done columns.
+ """
+ AUTOMATED_KANBAN_V2
+
+ """
+ Create a board with triggers to automatically move cards across columns with review automation.
+ """
+ AUTOMATED_REVIEWS_KANBAN
+
+ """
+ Create a board with columns for To do, In progress and Done.
+ """
+ BASIC_KANBAN
+
+ """
+ Create a board to triage and prioritize bugs with To do, priority, and Done columns.
+ """
+ BUG_TRIAGE
+}
+
+"""
+A user's public key.
+"""
+type PublicKey implements Node {
+ """
+ The last time this authorization was used to perform an action. Values will be null for keys not owned by the user.
+ """
+ accessedAt: DateTime
+
+ """
+ Identifies the date and time when the key was created. Keys created before
+ March 5th, 2014 have inaccurate values. Values will be null for keys not owned by the user.
+ """
+ createdAt: DateTime
+
+ """
+ The fingerprint for this PublicKey.
+ """
+ fingerprint: String!
+ id: ID!
+
+ """
+ Whether this PublicKey is read-only or not. Values will be null for keys not owned by the user.
+ """
+ isReadOnly: Boolean
+
+ """
+ The public key string.
+ """
+ key: String!
+
+ """
+ Identifies the date and time when the key was updated. Keys created before
+ March 5th, 2014 may have inaccurate values. Values will be null for keys not
+ owned by the user.
+ """
+ updatedAt: DateTime
+}
+
+"""
+The connection type for PublicKey.
+"""
+type PublicKeyConnection {
+ """
+ A list of edges.
+ """
+ edges: [PublicKeyEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PublicKey]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PublicKeyEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PublicKey
+}
+
+"""
+A repository pull request.
+"""
+type PullRequest implements Assignable & Closable & Comment & Labelable & Lockable & Node & Reactable & RepositoryNode & Subscribable & UniformResourceLocatable & Updatable & UpdatableComment {
+ """
+ Reason that the conversation was locked.
+ """
+ activeLockReason: LockReason
+
+ """
+ The number of additions in this pull request.
+ """
+ additions: Int!
+
+ """
+ A list of Users assigned to this object.
+ """
+ assignees(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ Identifies the base Ref associated with the pull request.
+ """
+ baseRef: Ref
+
+ """
+ Identifies the name of the base Ref associated with the pull request, even if the ref has been deleted.
+ """
+ baseRefName: String!
+
+ """
+ Identifies the oid of the base ref associated with the pull request, even if the ref has been deleted.
+ """
+ baseRefOid: GitObjectID!
+
+ """
+ The repository associated with this pull request's base Ref.
+ """
+ baseRepository: Repository
+
+ """
+ The body as Markdown.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Whether or not the pull request is rebaseable.
+ """
+ canBeRebased: Boolean! @preview(toggledBy: "merge-info-preview")
+
+ """
+ The number of changed files in this pull request.
+ """
+ changedFiles: Int!
+
+ """
+ The HTTP path for the checks of this pull request.
+ """
+ checksResourcePath: URI!
+
+ """
+ The HTTP URL for the checks of this pull request.
+ """
+ checksUrl: URI!
+
+ """
+ `true` if the pull request is closed
+ """
+ closed: Boolean!
+
+ """
+ Identifies the date and time when the object was closed.
+ """
+ closedAt: DateTime
+
+ """
+ A list of comments associated with the pull request.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issue comments returned from the connection.
+ """
+ orderBy: IssueCommentOrder
+ ): IssueCommentConnection!
+
+ """
+ A list of commits present in this pull request's head branch not present in the base branch.
+ """
+ commits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PullRequestCommitConnection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The number of deletions in this pull request.
+ """
+ deletions: Int!
+
+ """
+ The actor who edited this pull request's body.
+ """
+ editor: Actor
+
+ """
+ Lists the files changed within this pull request.
+ """
+ files(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PullRequestChangedFileConnection
+
+ """
+ Identifies the head Ref associated with the pull request.
+ """
+ headRef: Ref
+
+ """
+ Identifies the name of the head Ref associated with the pull request, even if the ref has been deleted.
+ """
+ headRefName: String!
+
+ """
+ Identifies the oid of the head ref associated with the pull request, even if the ref has been deleted.
+ """
+ headRefOid: GitObjectID!
+
+ """
+ The repository associated with this pull request's head Ref.
+ """
+ headRepository: Repository
+
+ """
+ The owner of the repository associated with this pull request's head Ref.
+ """
+ headRepositoryOwner: RepositoryOwner
+
+ """
+ The hovercard information for this issue
+ """
+ hovercard(
+ """
+ Whether or not to include notification contexts
+ """
+ includeNotificationContexts: Boolean = true
+ ): Hovercard!
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ The head and base repositories are different.
+ """
+ isCrossRepository: Boolean!
+
+ """
+ Identifies if the pull request is a draft.
+ """
+ isDraft: Boolean!
+
+ """
+ Is this pull request read by the viewer
+ """
+ isReadByViewer: Boolean
+
+ """
+ A list of labels associated with the object.
+ """
+ labels(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for labels returned from the connection.
+ """
+ orderBy: LabelOrder = {field: CREATED_AT, direction: ASC}
+ ): LabelConnection
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ A list of latest reviews per user associated with the pull request.
+ """
+ latestOpinionatedReviews(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Only return reviews from user who have write access to the repository
+ """
+ writersOnly: Boolean = false
+ ): PullRequestReviewConnection
+
+ """
+ A list of latest reviews per user associated with the pull request that are not also pending review.
+ """
+ latestReviews(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PullRequestReviewConnection
+
+ """
+ `true` if the pull request is locked
+ """
+ locked: Boolean!
+
+ """
+ Indicates whether maintainers can modify the pull request.
+ """
+ maintainerCanModify: Boolean!
+
+ """
+ The commit that was created when this pull request was merged.
+ """
+ mergeCommit: Commit
+
+ """
+ Detailed information about the current pull request merge state status.
+ """
+ mergeStateStatus: MergeStateStatus! @preview(toggledBy: "merge-info-preview")
+
+ """
+ Whether or not the pull request can be merged based on the existence of merge conflicts.
+ """
+ mergeable: MergeableState!
+
+ """
+ Whether or not the pull request was merged.
+ """
+ merged: Boolean!
+
+ """
+ The date and time that the pull request was merged.
+ """
+ mergedAt: DateTime
+
+ """
+ The actor who merged the pull request.
+ """
+ mergedBy: Actor
+
+ """
+ Identifies the milestone associated with the pull request.
+ """
+ milestone: Milestone
+
+ """
+ Identifies the pull request number.
+ """
+ number: Int!
+
+ """
+ A list of Users that are participating in the Pull Request conversation.
+ """
+ participants(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+
+ """
+ The permalink to the pull request.
+ """
+ permalink: URI!
+
+ """
+ The commit that GitHub automatically generated to test if this pull request
+ could be merged. This field will not return a value if the pull request is
+ merged, or if the test merge commit is still being generated. See the
+ `mergeable` field for more details on the mergeability of the pull request.
+ """
+ potentialMergeCommit: Commit
+
+ """
+ List of project cards associated with this pull request.
+ """
+ projectCards(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ A list of archived states to filter the cards by
+ """
+ archivedStates: [ProjectCardArchivedState] = [ARCHIVED, NOT_ARCHIVED]
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ProjectCardConnection!
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path for this pull request.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP path for reverting this pull request.
+ """
+ revertResourcePath: URI!
+
+ """
+ The HTTP URL for reverting this pull request.
+ """
+ revertUrl: URI!
+
+ """
+ The current status of this pull request with respect to code review.
+ """
+ reviewDecision: PullRequestReviewDecision
+
+ """
+ A list of review requests associated with the pull request.
+ """
+ reviewRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ReviewRequestConnection
+
+ """
+ The list of all review threads for this pull request.
+ """
+ reviewThreads(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PullRequestReviewThreadConnection!
+
+ """
+ A list of reviews associated with the pull request.
+ """
+ reviews(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Filter by author of the review.
+ """
+ author: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ A list of states to filter the reviews.
+ """
+ states: [PullRequestReviewState!]
+ ): PullRequestReviewConnection
+
+ """
+ Identifies the state of the pull request.
+ """
+ state: PullRequestState!
+
+ """
+ A list of reviewer suggestions based on commit history and past review comments.
+ """
+ suggestedReviewers: [SuggestedReviewer]!
+
+ """
+ A list of events, comments, commits, etc. associated with the pull request.
+ """
+ timeline(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows filtering timeline events by a `since` timestamp.
+ """
+ since: DateTime
+ ): PullRequestTimelineConnection! @deprecated(reason: "`timeline` will be removed Use PullRequest.timelineItems instead. Removal on 2020-10-01 UTC.")
+
+ """
+ A list of events, comments, commits, etc. associated with the pull request.
+ """
+ timelineItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Filter timeline items by type.
+ """
+ itemTypes: [PullRequestTimelineItemsItemType!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter timeline items by a `since` timestamp.
+ """
+ since: DateTime
+
+ """
+ Skips the first _n_ elements in the list.
+ """
+ skip: Int
+ ): PullRequestTimelineItemsConnection!
+
+ """
+ Identifies the pull request title.
+ """
+ title: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this pull request.
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Whether or not the viewer can apply suggestion.
+ """
+ viewerCanApplySuggestion: Boolean!
+
+ """
+ Check if the viewer can restore the deleted head ref.
+ """
+ viewerCanDeleteHeadRef: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+
+ """
+ The merge body text for the viewer and method.
+ """
+ viewerMergeBodyText(
+ """
+ The merge method for the message.
+ """
+ mergeType: PullRequestMergeMethod
+ ): String!
+
+ """
+ The merge headline text for the viewer and method.
+ """
+ viewerMergeHeadlineText(
+ """
+ The merge method for the message.
+ """
+ mergeType: PullRequestMergeMethod
+ ): String!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+}
+
+"""
+A file changed in a pull request.
+"""
+type PullRequestChangedFile {
+ """
+ The number of additions to the file.
+ """
+ additions: Int!
+
+ """
+ The number of deletions to the file.
+ """
+ deletions: Int!
+
+ """
+ The path of the file.
+ """
+ path: String!
+
+ """
+ The state of the file for the viewer.
+ """
+ viewerViewedState: FileViewedState!
+}
+
+"""
+The connection type for PullRequestChangedFile.
+"""
+type PullRequestChangedFileConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestChangedFileEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestChangedFile]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestChangedFileEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestChangedFile
+}
+
+"""
+Represents a Git commit part of a pull request.
+"""
+type PullRequestCommit implements Node & UniformResourceLocatable {
+ """
+ The Git commit object
+ """
+ commit: Commit!
+ id: ID!
+
+ """
+ The pull request this commit belongs to
+ """
+ pullRequest: PullRequest!
+
+ """
+ The HTTP path for this pull request commit
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this pull request commit
+ """
+ url: URI!
+}
+
+"""
+Represents a commit comment thread part of a pull request.
+"""
+type PullRequestCommitCommentThread implements Node & RepositoryNode {
+ """
+ The comments that exist in this thread.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitCommentConnection!
+
+ """
+ The commit the comments were made on.
+ """
+ commit: Commit!
+ id: ID!
+
+ """
+ The file the comments were made on.
+ """
+ path: String
+
+ """
+ The position in the diff for the commit that the comment was made on.
+ """
+ position: Int
+
+ """
+ The pull request this commit comment thread belongs to
+ """
+ pullRequest: PullRequest!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+}
+
+"""
+The connection type for PullRequestCommit.
+"""
+type PullRequestCommitConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestCommitEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestCommit]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestCommitEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestCommit
+}
+
+"""
+The connection type for PullRequest.
+"""
+type PullRequestConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequest]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+This aggregates pull requests opened by a user within one repository.
+"""
+type PullRequestContributionsByRepository {
+ """
+ The pull request contributions.
+ """
+ contributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedPullRequestContributionConnection!
+
+ """
+ The repository in which the pull requests were opened.
+ """
+ repository: Repository!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequest
+}
+
+"""
+Represents available types of methods to use when merging a pull request.
+"""
+enum PullRequestMergeMethod {
+ """
+ Add all commits from the head branch to the base branch with a merge commit.
+ """
+ MERGE
+
+ """
+ Add all commits from the head branch onto the base branch individually.
+ """
+ REBASE
+
+ """
+ Combine all commits from the head branch into a single commit in the base branch.
+ """
+ SQUASH
+}
+
+"""
+Ways in which lists of issues can be ordered upon return.
+"""
+input PullRequestOrder {
+ """
+ The direction in which to order pull requests by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order pull requests by.
+ """
+ field: PullRequestOrderField!
+}
+
+"""
+Properties by which pull_requests connections can be ordered.
+"""
+enum PullRequestOrderField {
+ """
+ Order pull_requests by creation time
+ """
+ CREATED_AT
+
+ """
+ Order pull_requests by update time
+ """
+ UPDATED_AT
+}
+
+"""
+A review object for a given pull request.
+"""
+type PullRequestReview implements Comment & Deletable & Node & Reactable & RepositoryNode & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ Indicates whether the author of this review has push access to the repository.
+ """
+ authorCanPushToRepository: Boolean!
+
+ """
+ Identifies the pull request review body.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body of this review rendered as plain text.
+ """
+ bodyText: String!
+
+ """
+ A list of review comments for the current pull request review.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PullRequestReviewCommentConnection!
+
+ """
+ Identifies the commit associated with this pull request review.
+ """
+ commit: Commit
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ A list of teams that this review was made on behalf of.
+ """
+ onBehalfOf(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): TeamConnection!
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ Identifies the pull request associated with this pull request review.
+ """
+ pullRequest: PullRequest!
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path permalink for this PullRequestReview.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the current state of the pull request review.
+ """
+ state: PullRequestReviewState!
+
+ """
+ Identifies when the Pull Request Review was submitted
+ """
+ submittedAt: DateTime
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL permalink for this PullRequestReview.
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+A review comment associated with a given repository pull request.
+"""
+type PullRequestReviewComment implements Comment & Deletable & Minimizable & Node & Reactable & RepositoryNode & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the subject of the comment.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ The comment body of this review comment.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The comment body of this review comment rendered as plain text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the commit associated with the comment.
+ """
+ commit: Commit
+
+ """
+ Identifies when the comment was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The diff hunk to which the comment applies.
+ """
+ diffHunk: String!
+
+ """
+ Identifies when the comment was created in a draft state.
+ """
+ draftedAt: DateTime!
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Returns whether or not a comment has been minimized.
+ """
+ isMinimized: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Returns why the comment was minimized.
+ """
+ minimizedReason: String
+
+ """
+ Identifies the original commit associated with the comment.
+ """
+ originalCommit: Commit
+
+ """
+ The original line index in the diff to which the comment applies.
+ """
+ originalPosition: Int!
+
+ """
+ Identifies when the comment body is outdated
+ """
+ outdated: Boolean!
+
+ """
+ The path to which the comment applies.
+ """
+ path: String!
+
+ """
+ The line index in the diff to which the comment applies.
+ """
+ position: Int
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ The pull request associated with this review comment.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The pull request review associated with this review comment.
+ """
+ pullRequestReview: PullRequestReview
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The comment this is a reply to.
+ """
+ replyTo: PullRequestReviewComment
+
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+
+ """
+ The HTTP path permalink for this review comment.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the state of the comment.
+ """
+ state: PullRequestReviewCommentState!
+
+ """
+ Identifies when the comment was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL permalink for this review comment.
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Check if the current viewer can minimize this object.
+ """
+ viewerCanMinimize: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+The connection type for PullRequestReviewComment.
+"""
+type PullRequestReviewCommentConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestReviewCommentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestReviewComment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestReviewCommentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestReviewComment
+}
+
+"""
+The possible states of a pull request review comment.
+"""
+enum PullRequestReviewCommentState {
+ """
+ A comment that is part of a pending review
+ """
+ PENDING
+
+ """
+ A comment that is part of a submitted review
+ """
+ SUBMITTED
+}
+
+"""
+The connection type for PullRequestReview.
+"""
+type PullRequestReviewConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestReviewEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestReview]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+This aggregates pull request reviews made by a user within one repository.
+"""
+type PullRequestReviewContributionsByRepository {
+ """
+ The pull request review contributions.
+ """
+ contributions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for contributions returned from the connection.
+ """
+ orderBy: ContributionOrder = {direction: DESC}
+ ): CreatedPullRequestReviewContributionConnection!
+
+ """
+ The repository in which the pull request reviews were made.
+ """
+ repository: Repository!
+}
+
+"""
+The review status of a pull request.
+"""
+enum PullRequestReviewDecision {
+ """
+ The pull request has received an approving review.
+ """
+ APPROVED
+
+ """
+ Changes have been requested on the pull request.
+ """
+ CHANGES_REQUESTED
+
+ """
+ A review is required before the pull request can be merged.
+ """
+ REVIEW_REQUIRED
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestReviewEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestReview
+}
+
+"""
+The possible events to perform on a pull request review.
+"""
+enum PullRequestReviewEvent {
+ """
+ Submit feedback and approve merging these changes.
+ """
+ APPROVE
+
+ """
+ Submit general feedback without explicit approval.
+ """
+ COMMENT
+
+ """
+ Dismiss review so it now longer effects merging.
+ """
+ DISMISS
+
+ """
+ Submit feedback that must be addressed before merging.
+ """
+ REQUEST_CHANGES
+}
+
+"""
+The possible states of a pull request review.
+"""
+enum PullRequestReviewState {
+ """
+ A review allowing the pull request to merge.
+ """
+ APPROVED
+
+ """
+ A review blocking the pull request from merging.
+ """
+ CHANGES_REQUESTED
+
+ """
+ An informational review.
+ """
+ COMMENTED
+
+ """
+ A review that has been dismissed.
+ """
+ DISMISSED
+
+ """
+ A review that has not yet been submitted.
+ """
+ PENDING
+}
+
+"""
+A threaded list of comments for a given pull request.
+"""
+type PullRequestReviewThread implements Node {
+ """
+ A list of pull request comments associated with the thread.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Skips the first _n_ elements in the list.
+ """
+ skip: Int
+ ): PullRequestReviewCommentConnection!
+
+ """
+ The side of the diff on which this thread was placed.
+ """
+ diffSide: DiffSide!
+ id: ID!
+
+ """
+ Whether or not the thread has been collapsed (outdated or resolved)
+ """
+ isCollapsed: Boolean!
+
+ """
+ Indicates whether this thread was outdated by newer changes.
+ """
+ isOutdated: Boolean!
+
+ """
+ Whether this thread has been resolved
+ """
+ isResolved: Boolean!
+
+ """
+ The line in the file to which this thread refers
+ """
+ line: Int
+
+ """
+ The original line in the file to which this thread refers.
+ """
+ originalLine: Int
+
+ """
+ The original start line in the file to which this thread refers (multi-line only).
+ """
+ originalStartLine: Int
+
+ """
+ Identifies the file path of this thread.
+ """
+ path: String!
+
+ """
+ Identifies the pull request associated with this thread.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the repository associated with this thread.
+ """
+ repository: Repository!
+
+ """
+ The user who resolved this thread
+ """
+ resolvedBy: User
+
+ """
+ The side of the diff that the first line of the thread starts on (multi-line only)
+ """
+ startDiffSide: DiffSide
+
+ """
+ The start line in the file to which this thread refers (multi-line only)
+ """
+ startLine: Int
+
+ """
+ Indicates whether the current viewer can reply to this thread.
+ """
+ viewerCanReply: Boolean!
+
+ """
+ Whether or not the viewer can resolve this thread
+ """
+ viewerCanResolve: Boolean!
+
+ """
+ Whether or not the viewer can unresolve this thread
+ """
+ viewerCanUnresolve: Boolean!
+}
+
+"""
+Review comment threads for a pull request review.
+"""
+type PullRequestReviewThreadConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestReviewThreadEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestReviewThread]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestReviewThreadEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestReviewThread
+}
+
+"""
+Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.
+"""
+type PullRequestRevisionMarker {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The last commit the viewer has seen.
+ """
+ lastSeenCommit: Commit!
+
+ """
+ The pull request to which the marker belongs.
+ """
+ pullRequest: PullRequest!
+}
+
+"""
+The possible states of a pull request.
+"""
+enum PullRequestState {
+ """
+ A pull request that has been closed without being merged.
+ """
+ CLOSED
+
+ """
+ A pull request that has been closed by being merged.
+ """
+ MERGED
+
+ """
+ A pull request that is still open.
+ """
+ OPEN
+}
+
+"""
+The connection type for PullRequestTimelineItem.
+"""
+type PullRequestTimelineConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestTimelineItemEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestTimelineItem]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An item in a pull request timeline
+"""
+union PullRequestTimelineItem = AssignedEvent | BaseRefDeletedEvent | BaseRefForcePushedEvent | ClosedEvent | Commit | CommitCommentThread | CrossReferencedEvent | DemilestonedEvent | DeployedEvent | DeploymentEnvironmentChangedEvent | HeadRefDeletedEvent | HeadRefForcePushedEvent | HeadRefRestoredEvent | IssueComment | LabeledEvent | LockedEvent | MergedEvent | MilestonedEvent | PullRequestReview | PullRequestReviewComment | PullRequestReviewThread | ReferencedEvent | RenamedTitleEvent | ReopenedEvent | ReviewDismissedEvent | ReviewRequestRemovedEvent | ReviewRequestedEvent | SubscribedEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UnsubscribedEvent | UserBlockedEvent
+
+"""
+An edge in a connection.
+"""
+type PullRequestTimelineItemEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestTimelineItem
+}
+
+"""
+An item in a pull request timeline
+"""
+union PullRequestTimelineItems = AddedToProjectEvent | AssignedEvent | AutoMergeDisabledEvent | AutoMergeEnabledEvent | AutoRebaseEnabledEvent | AutoSquashEnabledEvent | AutomaticBaseChangeFailedEvent | AutomaticBaseChangeSucceededEvent | BaseRefChangedEvent | BaseRefDeletedEvent | BaseRefForcePushedEvent | ClosedEvent | CommentDeletedEvent | ConnectedEvent | ConvertToDraftEvent | ConvertedNoteToIssueEvent | CrossReferencedEvent | DemilestonedEvent | DeployedEvent | DeploymentEnvironmentChangedEvent | DisconnectedEvent | HeadRefDeletedEvent | HeadRefForcePushedEvent | HeadRefRestoredEvent | IssueComment | LabeledEvent | LockedEvent | MarkedAsDuplicateEvent | MentionedEvent | MergedEvent | MilestonedEvent | MovedColumnsInProjectEvent | PinnedEvent | PullRequestCommit | PullRequestCommitCommentThread | PullRequestReview | PullRequestReviewThread | PullRequestRevisionMarker | ReadyForReviewEvent | ReferencedEvent | RemovedFromProjectEvent | RenamedTitleEvent | ReopenedEvent | ReviewDismissedEvent | ReviewRequestRemovedEvent | ReviewRequestedEvent | SubscribedEvent | TransferredEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UnmarkedAsDuplicateEvent | UnpinnedEvent | UnsubscribedEvent | UserBlockedEvent
+
+"""
+The connection type for PullRequestTimelineItems.
+"""
+type PullRequestTimelineItemsConnection {
+ """
+ A list of edges.
+ """
+ edges: [PullRequestTimelineItemsEdge]
+
+ """
+ Identifies the count of items after applying `before` and `after` filters.
+ """
+ filteredCount: Int!
+
+ """
+ A list of nodes.
+ """
+ nodes: [PullRequestTimelineItems]
+
+ """
+ Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.
+ """
+ pageCount: Int!
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ Identifies the date and time when the timeline was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+An edge in a connection.
+"""
+type PullRequestTimelineItemsEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PullRequestTimelineItems
+}
+
+"""
+The possible item types found in a timeline.
+"""
+enum PullRequestTimelineItemsItemType {
+ """
+ Represents a 'added_to_project' event on a given issue or pull request.
+ """
+ ADDED_TO_PROJECT_EVENT
+
+ """
+ Represents an 'assigned' event on any assignable object.
+ """
+ ASSIGNED_EVENT
+
+ """
+ Represents a 'automatic_base_change_failed' event on a given pull request.
+ """
+ AUTOMATIC_BASE_CHANGE_FAILED_EVENT
+
+ """
+ Represents a 'automatic_base_change_succeeded' event on a given pull request.
+ """
+ AUTOMATIC_BASE_CHANGE_SUCCEEDED_EVENT
+
+ """
+ Represents a 'auto_merge_disabled' event on a given pull request.
+ """
+ AUTO_MERGE_DISABLED_EVENT
+
+ """
+ Represents a 'auto_merge_enabled' event on a given pull request.
+ """
+ AUTO_MERGE_ENABLED_EVENT
+
+ """
+ Represents a 'auto_rebase_enabled' event on a given pull request.
+ """
+ AUTO_REBASE_ENABLED_EVENT
+
+ """
+ Represents a 'auto_squash_enabled' event on a given pull request.
+ """
+ AUTO_SQUASH_ENABLED_EVENT
+
+ """
+ Represents a 'base_ref_changed' event on a given issue or pull request.
+ """
+ BASE_REF_CHANGED_EVENT
+
+ """
+ Represents a 'base_ref_deleted' event on a given pull request.
+ """
+ BASE_REF_DELETED_EVENT
+
+ """
+ Represents a 'base_ref_force_pushed' event on a given pull request.
+ """
+ BASE_REF_FORCE_PUSHED_EVENT
+
+ """
+ Represents a 'closed' event on any `Closable`.
+ """
+ CLOSED_EVENT
+
+ """
+ Represents a 'comment_deleted' event on a given issue or pull request.
+ """
+ COMMENT_DELETED_EVENT
+
+ """
+ Represents a 'connected' event on a given issue or pull request.
+ """
+ CONNECTED_EVENT
+
+ """
+ Represents a 'converted_note_to_issue' event on a given issue or pull request.
+ """
+ CONVERTED_NOTE_TO_ISSUE_EVENT
+
+ """
+ Represents a 'convert_to_draft' event on a given pull request.
+ """
+ CONVERT_TO_DRAFT_EVENT
+
+ """
+ Represents a mention made by one issue or pull request to another.
+ """
+ CROSS_REFERENCED_EVENT
+
+ """
+ Represents a 'demilestoned' event on a given issue or pull request.
+ """
+ DEMILESTONED_EVENT
+
+ """
+ Represents a 'deployed' event on a given pull request.
+ """
+ DEPLOYED_EVENT
+
+ """
+ Represents a 'deployment_environment_changed' event on a given pull request.
+ """
+ DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT
+
+ """
+ Represents a 'disconnected' event on a given issue or pull request.
+ """
+ DISCONNECTED_EVENT
+
+ """
+ Represents a 'head_ref_deleted' event on a given pull request.
+ """
+ HEAD_REF_DELETED_EVENT
+
+ """
+ Represents a 'head_ref_force_pushed' event on a given pull request.
+ """
+ HEAD_REF_FORCE_PUSHED_EVENT
+
+ """
+ Represents a 'head_ref_restored' event on a given pull request.
+ """
+ HEAD_REF_RESTORED_EVENT
+
+ """
+ Represents a comment on an Issue.
+ """
+ ISSUE_COMMENT
+
+ """
+ Represents a 'labeled' event on a given issue or pull request.
+ """
+ LABELED_EVENT
+
+ """
+ Represents a 'locked' event on a given issue or pull request.
+ """
+ LOCKED_EVENT
+
+ """
+ Represents a 'marked_as_duplicate' event on a given issue or pull request.
+ """
+ MARKED_AS_DUPLICATE_EVENT
+
+ """
+ Represents a 'mentioned' event on a given issue or pull request.
+ """
+ MENTIONED_EVENT
+
+ """
+ Represents a 'merged' event on a given pull request.
+ """
+ MERGED_EVENT
+
+ """
+ Represents a 'milestoned' event on a given issue or pull request.
+ """
+ MILESTONED_EVENT
+
+ """
+ Represents a 'moved_columns_in_project' event on a given issue or pull request.
+ """
+ MOVED_COLUMNS_IN_PROJECT_EVENT
+
+ """
+ Represents a 'pinned' event on a given issue or pull request.
+ """
+ PINNED_EVENT
+
+ """
+ Represents a Git commit part of a pull request.
+ """
+ PULL_REQUEST_COMMIT
+
+ """
+ Represents a commit comment thread part of a pull request.
+ """
+ PULL_REQUEST_COMMIT_COMMENT_THREAD
+
+ """
+ A review object for a given pull request.
+ """
+ PULL_REQUEST_REVIEW
+
+ """
+ A threaded list of comments for a given pull request.
+ """
+ PULL_REQUEST_REVIEW_THREAD
+
+ """
+ Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.
+ """
+ PULL_REQUEST_REVISION_MARKER
+
+ """
+ Represents a 'ready_for_review' event on a given pull request.
+ """
+ READY_FOR_REVIEW_EVENT
+
+ """
+ Represents a 'referenced' event on a given `ReferencedSubject`.
+ """
+ REFERENCED_EVENT
+
+ """
+ Represents a 'removed_from_project' event on a given issue or pull request.
+ """
+ REMOVED_FROM_PROJECT_EVENT
+
+ """
+ Represents a 'renamed' event on a given issue or pull request
+ """
+ RENAMED_TITLE_EVENT
+
+ """
+ Represents a 'reopened' event on any `Closable`.
+ """
+ REOPENED_EVENT
+
+ """
+ Represents a 'review_dismissed' event on a given issue or pull request.
+ """
+ REVIEW_DISMISSED_EVENT
+
+ """
+ Represents an 'review_requested' event on a given pull request.
+ """
+ REVIEW_REQUESTED_EVENT
+
+ """
+ Represents an 'review_request_removed' event on a given pull request.
+ """
+ REVIEW_REQUEST_REMOVED_EVENT
+
+ """
+ Represents a 'subscribed' event on a given `Subscribable`.
+ """
+ SUBSCRIBED_EVENT
+
+ """
+ Represents a 'transferred' event on a given issue or pull request.
+ """
+ TRANSFERRED_EVENT
+
+ """
+ Represents an 'unassigned' event on any assignable object.
+ """
+ UNASSIGNED_EVENT
+
+ """
+ Represents an 'unlabeled' event on a given issue or pull request.
+ """
+ UNLABELED_EVENT
+
+ """
+ Represents an 'unlocked' event on a given issue or pull request.
+ """
+ UNLOCKED_EVENT
+
+ """
+ Represents an 'unmarked_as_duplicate' event on a given issue or pull request.
+ """
+ UNMARKED_AS_DUPLICATE_EVENT
+
+ """
+ Represents an 'unpinned' event on a given issue or pull request.
+ """
+ UNPINNED_EVENT
+
+ """
+ Represents an 'unsubscribed' event on a given `Subscribable`.
+ """
+ UNSUBSCRIBED_EVENT
+
+ """
+ Represents a 'user_blocked' event on a given user.
+ """
+ USER_BLOCKED_EVENT
+}
+
+"""
+The possible target states when updating a pull request.
+"""
+enum PullRequestUpdateState {
+ """
+ A pull request that has been closed without being merged.
+ """
+ CLOSED
+
+ """
+ A pull request that is still open.
+ """
+ OPEN
+}
+
+"""
+A Git push.
+"""
+type Push implements Node {
+ id: ID!
+
+ """
+ The SHA after the push
+ """
+ nextSha: GitObjectID
+
+ """
+ The permalink for this push.
+ """
+ permalink: URI!
+
+ """
+ The SHA before the push
+ """
+ previousSha: GitObjectID
+
+ """
+ The user who pushed
+ """
+ pusher: User!
+
+ """
+ The repository that was pushed to
+ """
+ repository: Repository!
+}
+
+"""
+A team, user or app who has the ability to push to a protected branch.
+"""
+type PushAllowance implements Node {
+ """
+ The actor that can push.
+ """
+ actor: PushAllowanceActor
+
+ """
+ Identifies the branch protection rule associated with the allowed user or team.
+ """
+ branchProtectionRule: BranchProtectionRule
+ id: ID!
+}
+
+"""
+Types that can be an actor.
+"""
+union PushAllowanceActor = App | Team | User
+
+"""
+The connection type for PushAllowance.
+"""
+type PushAllowanceConnection {
+ """
+ A list of edges.
+ """
+ edges: [PushAllowanceEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [PushAllowance]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type PushAllowanceEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: PushAllowance
+}
+
+"""
+The query root of GitHub's GraphQL interface.
+"""
+type Query {
+ """
+ Look up a code of conduct by its key
+ """
+ codeOfConduct(
+ """
+ The code of conduct's key
+ """
+ key: String!
+ ): CodeOfConduct
+
+ """
+ Look up a code of conduct by its key
+ """
+ codesOfConduct: [CodeOfConduct]
+
+ """
+ Look up an enterprise by URL slug.
+ """
+ enterprise(
+ """
+ The enterprise invitation token.
+ """
+ invitationToken: String
+
+ """
+ The enterprise URL slug.
+ """
+ slug: String!
+ ): Enterprise
+
+ """
+ Look up a pending enterprise administrator invitation by invitee, enterprise and role.
+ """
+ enterpriseAdministratorInvitation(
+ """
+ The slug of the enterprise the user was invited to join.
+ """
+ enterpriseSlug: String!
+
+ """
+ The role for the business member invitation.
+ """
+ role: EnterpriseAdministratorRole!
+
+ """
+ The login of the user invited to join the business.
+ """
+ userLogin: String!
+ ): EnterpriseAdministratorInvitation
+
+ """
+ Look up a pending enterprise administrator invitation by invitation token.
+ """
+ enterpriseAdministratorInvitationByToken(
+ """
+ The invitation token sent with the invitation email.
+ """
+ invitationToken: String!
+ ): EnterpriseAdministratorInvitation
+
+ """
+ Look up an open source license by its key
+ """
+ license(
+ """
+ The license's downcased SPDX ID
+ """
+ key: String!
+ ): License
+
+ """
+ Return a list of known open source licenses
+ """
+ licenses: [License]!
+
+ """
+ Get alphabetically sorted list of Marketplace categories
+ """
+ marketplaceCategories(
+ """
+ Exclude categories with no listings.
+ """
+ excludeEmpty: Boolean
+
+ """
+ Returns top level categories only, excluding any subcategories.
+ """
+ excludeSubcategories: Boolean
+
+ """
+ Return only the specified categories.
+ """
+ includeCategories: [String!]
+ ): [MarketplaceCategory!]!
+
+ """
+ Look up a Marketplace category by its slug.
+ """
+ marketplaceCategory(
+ """
+ The URL slug of the category.
+ """
+ slug: String!
+
+ """
+ Also check topic aliases for the category slug
+ """
+ useTopicAliases: Boolean
+ ): MarketplaceCategory
+
+ """
+ Look up a single Marketplace listing
+ """
+ marketplaceListing(
+ """
+ Select the listing that matches this slug. It's the short name of the listing used in its URL.
+ """
+ slug: String!
+ ): MarketplaceListing
+
+ """
+ Look up Marketplace listings
+ """
+ marketplaceListings(
+ """
+ Select listings that can be administered by the specified user.
+ """
+ adminId: ID
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Select listings visible to the viewer even if they are not approved. If omitted or
+ false, only approved listings will be returned.
+ """
+ allStates: Boolean
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Select only listings with the given category.
+ """
+ categorySlug: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Select listings for products owned by the specified organization.
+ """
+ organizationId: ID
+
+ """
+ Select only listings where the primary category matches the given category slug.
+ """
+ primaryCategoryOnly: Boolean = false
+
+ """
+ Select the listings with these slugs, if they are visible to the viewer.
+ """
+ slugs: [String]
+
+ """
+ Also check topic aliases for the category slug
+ """
+ useTopicAliases: Boolean
+
+ """
+ Select listings to which user has admin access. If omitted, listings visible to the
+ viewer are returned.
+ """
+ viewerCanAdmin: Boolean
+
+ """
+ Select only listings that offer a free trial.
+ """
+ withFreeTrialsOnly: Boolean = false
+ ): MarketplaceListingConnection!
+
+ """
+ Return information about the GitHub instance
+ """
+ meta: GitHubMetadata!
+
+ """
+ Fetches an object given its ID.
+ """
+ node(
+ """
+ ID of the object.
+ """
+ id: ID!
+ ): Node
+
+ """
+ Lookup nodes by a list of IDs.
+ """
+ nodes(
+ """
+ The list of node IDs.
+ """
+ ids: [ID!]!
+ ): [Node]!
+
+ """
+ Lookup a organization by login.
+ """
+ organization(
+ """
+ The organization's login.
+ """
+ login: String!
+ ): Organization
+
+ """
+ The client's rate limit information.
+ """
+ rateLimit(
+ """
+ If true, calculate the cost for the query without evaluating it
+ """
+ dryRun: Boolean = false
+ ): RateLimit
+
+ """
+ Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object
+ """
+ relay: Query!
+
+ """
+ Lookup a given repository by the owner and repository name.
+ """
+ repository(
+ """
+ The name of the repository
+ """
+ name: String!
+
+ """
+ The login field of a user or organization
+ """
+ owner: String!
+ ): Repository
+
+ """
+ Lookup a repository owner (ie. either a User or an Organization) by login.
+ """
+ repositoryOwner(
+ """
+ The username to lookup the owner by.
+ """
+ login: String!
+ ): RepositoryOwner
+
+ """
+ Lookup resource by a URL.
+ """
+ resource(
+ """
+ The URL.
+ """
+ url: URI!
+ ): UniformResourceLocatable
+
+ """
+ Perform a search across resources.
+ """
+ search(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ The search string to look for.
+ """
+ query: String!
+
+ """
+ The types of search items to search within.
+ """
+ type: SearchType!
+ ): SearchResultItemConnection!
+
+ """
+ GitHub Security Advisories
+ """
+ securityAdvisories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Filter advisories by identifier, e.g. GHSA or CVE.
+ """
+ identifier: SecurityAdvisoryIdentifierFilter
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for the returned topics.
+ """
+ orderBy: SecurityAdvisoryOrder = {field: UPDATED_AT, direction: DESC}
+
+ """
+ Filter advisories to those published since a time in the past.
+ """
+ publishedSince: DateTime
+
+ """
+ Filter advisories to those updated since a time in the past.
+ """
+ updatedSince: DateTime
+ ): SecurityAdvisoryConnection!
+
+ """
+ Fetch a Security Advisory by its GHSA ID
+ """
+ securityAdvisory(
+ """
+ GitHub Security Advisory ID.
+ """
+ ghsaId: String!
+ ): SecurityAdvisory
+
+ """
+ Software Vulnerabilities documented by GitHub Security Advisories
+ """
+ securityVulnerabilities(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ An ecosystem to filter vulnerabilities by.
+ """
+ ecosystem: SecurityAdvisoryEcosystem
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for the returned topics.
+ """
+ orderBy: SecurityVulnerabilityOrder = {field: UPDATED_AT, direction: DESC}
+
+ """
+ A package name to filter vulnerabilities by.
+ """
+ package: String
+
+ """
+ A list of severities to filter vulnerabilities by.
+ """
+ severities: [SecurityAdvisorySeverity!]
+ ): SecurityVulnerabilityConnection!
+
+ """
+ Look up a single Sponsors Listing
+ """
+ sponsorsListing(
+ """
+ Select the Sponsors listing which matches this slug
+ """
+ slug: String!
+ ): SponsorsListing @deprecated(reason: "`Query.sponsorsListing` will be removed. Use `Sponsorable.sponsorsListing` instead. Removal on 2020-04-01 UTC.")
+
+ """
+ Look up a topic by name.
+ """
+ topic(
+ """
+ The topic's name.
+ """
+ name: String!
+ ): Topic
+
+ """
+ Lookup a user by login.
+ """
+ user(
+ """
+ The user's login.
+ """
+ login: String!
+ ): User
+
+ """
+ The currently authenticated user.
+ """
+ viewer: User!
+}
+
+"""
+Represents the client's rate limit.
+"""
+type RateLimit {
+ """
+ The point cost for the current query counting against the rate limit.
+ """
+ cost: Int!
+
+ """
+ The maximum number of points the client is permitted to consume in a 60 minute window.
+ """
+ limit: Int!
+
+ """
+ The maximum number of nodes this query may return
+ """
+ nodeCount: Int!
+
+ """
+ The number of points remaining in the current rate limit window.
+ """
+ remaining: Int!
+
+ """
+ The time at which the current rate limit window resets in UTC epoch seconds.
+ """
+ resetAt: DateTime!
+
+ """
+ The number of points used in the current rate limit window.
+ """
+ used: Int!
+}
+
+"""
+Represents a subject that can be reacted on.
+"""
+interface Reactable {
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+}
+
+"""
+The connection type for User.
+"""
+type ReactingUserConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReactingUserEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a user that's made a reaction.
+"""
+type ReactingUserEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+ node: User!
+
+ """
+ The moment when the user made the reaction.
+ """
+ reactedAt: DateTime!
+}
+
+"""
+An emoji reaction to a particular piece of content.
+"""
+type Reaction implements Node {
+ """
+ Identifies the emoji reaction.
+ """
+ content: ReactionContent!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The reactable piece of content
+ """
+ reactable: Reactable!
+
+ """
+ Identifies the user who created this reaction.
+ """
+ user: User
+}
+
+"""
+A list of reactions that have been left on the subject.
+"""
+type ReactionConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReactionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Reaction]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ Whether or not the authenticated user has left a reaction on the subject.
+ """
+ viewerHasReacted: Boolean!
+}
+
+"""
+Emojis that can be attached to Issues, Pull Requests and Comments.
+"""
+enum ReactionContent {
+ """
+ Represents the `:confused:` emoji.
+ """
+ CONFUSED
+
+ """
+ Represents the `:eyes:` emoji.
+ """
+ EYES
+
+ """
+ Represents the `:heart:` emoji.
+ """
+ HEART
+
+ """
+ Represents the `:hooray:` emoji.
+ """
+ HOORAY
+
+ """
+ Represents the `:laugh:` emoji.
+ """
+ LAUGH
+
+ """
+ Represents the `:rocket:` emoji.
+ """
+ ROCKET
+
+ """
+ Represents the `:-1:` emoji.
+ """
+ THUMBS_DOWN
+
+ """
+ Represents the `:+1:` emoji.
+ """
+ THUMBS_UP
+}
+
+"""
+An edge in a connection.
+"""
+type ReactionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Reaction
+}
+
+"""
+A group of emoji reactions to a particular piece of content.
+"""
+type ReactionGroup {
+ """
+ Identifies the emoji reaction.
+ """
+ content: ReactionContent!
+
+ """
+ Identifies when the reaction was created.
+ """
+ createdAt: DateTime
+
+ """
+ The subject that was reacted to.
+ """
+ subject: Reactable!
+
+ """
+ Users who have reacted to the reaction subject with the emotion represented by this reaction group
+ """
+ users(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ReactingUserConnection!
+
+ """
+ Whether or not the authenticated user has left a reaction on the subject.
+ """
+ viewerHasReacted: Boolean!
+}
+
+"""
+Ways in which lists of reactions can be ordered upon return.
+"""
+input ReactionOrder {
+ """
+ The direction in which to order reactions by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order reactions by.
+ """
+ field: ReactionOrderField!
+}
+
+"""
+A list of fields that reactions can be ordered by.
+"""
+enum ReactionOrderField {
+ """
+ Allows ordering a list of reactions by when they were created.
+ """
+ CREATED_AT
+}
+
+"""
+Represents a 'ready_for_review' event on a given pull request.
+"""
+type ReadyForReviewEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The HTTP path for this ready for review event.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this ready for review event.
+ """
+ url: URI!
+}
+
+"""
+Represents a Git reference.
+"""
+type Ref implements Node {
+ """
+ A list of pull requests with this ref as the head ref.
+ """
+ associatedPullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection!
+
+ """
+ Branch protection rules for this ref
+ """
+ branchProtectionRule: BranchProtectionRule
+ id: ID!
+
+ """
+ The ref name.
+ """
+ name: String!
+
+ """
+ The ref's prefix, such as `refs/heads/` or `refs/tags/`.
+ """
+ prefix: String!
+
+ """
+ Branch protection rules that are viewable by non-admins
+ """
+ refUpdateRule: RefUpdateRule
+
+ """
+ The repository the ref belongs to.
+ """
+ repository: Repository!
+
+ """
+ The object the ref points to. Returns null when object does not exist.
+ """
+ target: GitObject
+}
+
+"""
+The connection type for Ref.
+"""
+type RefConnection {
+ """
+ A list of edges.
+ """
+ edges: [RefEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Ref]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type RefEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Ref
+}
+
+"""
+Ways in which lists of git refs can be ordered upon return.
+"""
+input RefOrder {
+ """
+ The direction in which to order refs by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order refs by.
+ """
+ field: RefOrderField!
+}
+
+"""
+Properties by which ref connections can be ordered.
+"""
+enum RefOrderField {
+ """
+ Order refs by their alphanumeric name
+ """
+ ALPHABETICAL
+
+ """
+ Order refs by underlying commit date if the ref prefix is refs/tags/
+ """
+ TAG_COMMIT_DATE
+}
+
+"""
+A ref update
+"""
+input RefUpdate @preview(toggledBy: "update-refs-preview") {
+ """
+ The value this ref should be updated to.
+ """
+ afterOid: GitObjectID!
+
+ """
+ The value this ref needs to point to before the update.
+ """
+ beforeOid: GitObjectID
+
+ """
+ Force a non fast-forward update.
+ """
+ force: Boolean = false
+
+ """
+ The fully qualified name of the ref to be update. For example `refs/heads/branch-name`
+ """
+ name: GitRefname!
+}
+
+"""
+A ref update rules for a viewer.
+"""
+type RefUpdateRule {
+ """
+ Can this branch be deleted.
+ """
+ allowsDeletions: Boolean!
+
+ """
+ Are force pushes allowed on this branch.
+ """
+ allowsForcePushes: Boolean!
+
+ """
+ Identifies the protection rule pattern.
+ """
+ pattern: String!
+
+ """
+ Number of approving reviews required to update matching branches.
+ """
+ requiredApprovingReviewCount: Int
+
+ """
+ List of required status check contexts that must pass for commits to be accepted to matching branches.
+ """
+ requiredStatusCheckContexts: [String]
+
+ """
+ Are merge commits prohibited from being pushed to this branch.
+ """
+ requiresLinearHistory: Boolean!
+
+ """
+ Are commits required to be signed.
+ """
+ requiresSignatures: Boolean!
+
+ """
+ Can the viewer push to the branch
+ """
+ viewerCanPush: Boolean!
+}
+
+"""
+Represents a 'referenced' event on a given `ReferencedSubject`.
+"""
+type ReferencedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the commit associated with the 'referenced' event.
+ """
+ commit: Commit
+
+ """
+ Identifies the repository associated with the 'referenced' event.
+ """
+ commitRepository: Repository!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Reference originated in a different repository.
+ """
+ isCrossRepository: Boolean!
+
+ """
+ Checks if the commit message itself references the subject. Can be false in the case of a commit comment reference.
+ """
+ isDirectReference: Boolean!
+
+ """
+ Object referenced by event.
+ """
+ subject: ReferencedSubject!
+}
+
+"""
+Any referencable object
+"""
+union ReferencedSubject = Issue | PullRequest
+
+"""
+Autogenerated input type of RegenerateEnterpriseIdentityProviderRecoveryCodes
+"""
+input RegenerateEnterpriseIdentityProviderRecoveryCodesInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set an identity provider.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+}
+
+"""
+Autogenerated return type of RegenerateEnterpriseIdentityProviderRecoveryCodes
+"""
+type RegenerateEnterpriseIdentityProviderRecoveryCodesPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The identity provider for the enterprise.
+ """
+ identityProvider: EnterpriseIdentityProvider
+}
+
+"""
+Autogenerated input type of RegenerateVerifiableDomainToken
+"""
+input RegenerateVerifiableDomainTokenInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the verifiable domain to regenerate the verification token of.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["VerifiableDomain"])
+}
+
+"""
+Autogenerated return type of RegenerateVerifiableDomainToken
+"""
+type RegenerateVerifiableDomainTokenPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The verification token that was generated.
+ """
+ verificationToken: String
+}
+
+"""
+A release contains the content for a release.
+"""
+type Release implements Node & UniformResourceLocatable {
+ """
+ The author of the release
+ """
+ author: User
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The description of the release.
+ """
+ description: String
+
+ """
+ The description of this release rendered to HTML.
+ """
+ descriptionHTML: HTML
+ id: ID!
+
+ """
+ Whether or not the release is a draft
+ """
+ isDraft: Boolean!
+
+ """
+ Whether or not the release is the latest releast
+ """
+ isLatest: Boolean!
+
+ """
+ Whether or not the release is a prerelease
+ """
+ isPrerelease: Boolean!
+
+ """
+ The title of the release.
+ """
+ name: String
+
+ """
+ Identifies the date and time when the release was created.
+ """
+ publishedAt: DateTime
+
+ """
+ List of releases assets which are dependent on this release.
+ """
+ releaseAssets(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ A list of names to filter the assets by.
+ """
+ name: String
+ ): ReleaseAssetConnection!
+
+ """
+ The HTTP path for this issue
+ """
+ resourcePath: URI!
+
+ """
+ A description of the release, rendered to HTML without any links in it.
+ """
+ shortDescriptionHTML(
+ """
+ How many characters to return.
+ """
+ limit: Int = 200
+ ): HTML
+
+ """
+ The Git tag the release points to
+ """
+ tag: Ref
+
+ """
+ The name of the release's Git tag
+ """
+ tagName: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this issue
+ """
+ url: URI!
+}
+
+"""
+A release asset contains the content for a release asset.
+"""
+type ReleaseAsset implements Node {
+ """
+ The asset's content-type
+ """
+ contentType: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The number of times this asset was downloaded
+ """
+ downloadCount: Int!
+
+ """
+ Identifies the URL where you can download the release asset via the browser.
+ """
+ downloadUrl: URI!
+ id: ID!
+
+ """
+ Identifies the title of the release asset.
+ """
+ name: String!
+
+ """
+ Release that the asset is associated with
+ """
+ release: Release
+
+ """
+ The size (in bytes) of the asset
+ """
+ size: Int!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The user that performed the upload
+ """
+ uploadedBy: User!
+
+ """
+ Identifies the URL of the release asset.
+ """
+ url: URI!
+}
+
+"""
+The connection type for ReleaseAsset.
+"""
+type ReleaseAssetConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReleaseAssetEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ReleaseAsset]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ReleaseAssetEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ReleaseAsset
+}
+
+"""
+The connection type for Release.
+"""
+type ReleaseConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReleaseEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Release]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ReleaseEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Release
+}
+
+"""
+Ways in which lists of releases can be ordered upon return.
+"""
+input ReleaseOrder {
+ """
+ The direction in which to order releases by the specified field.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order releases by.
+ """
+ field: ReleaseOrderField!
+}
+
+"""
+Properties by which release connections can be ordered.
+"""
+enum ReleaseOrderField {
+ """
+ Order releases by creation time
+ """
+ CREATED_AT
+
+ """
+ Order releases alphabetically by name
+ """
+ NAME
+}
+
+"""
+Autogenerated input type of RemoveAssigneesFromAssignable
+"""
+input RemoveAssigneesFromAssignableInput {
+ """
+ The id of the assignable object to remove assignees from.
+ """
+ assignableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Assignable")
+
+ """
+ The id of users to remove as assignees.
+ """
+ assigneeIds: [ID!]! @possibleTypes(concreteTypes: ["User"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated return type of RemoveAssigneesFromAssignable
+"""
+type RemoveAssigneesFromAssignablePayload {
+ """
+ The item that was unassigned.
+ """
+ assignable: Assignable
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of RemoveEnterpriseAdmin
+"""
+input RemoveEnterpriseAdminInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Enterprise ID from which to remove the administrator.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of the user to remove as an administrator.
+ """
+ login: String!
+}
+
+"""
+Autogenerated return type of RemoveEnterpriseAdmin
+"""
+type RemoveEnterpriseAdminPayload {
+ """
+ The user who was removed as an administrator.
+ """
+ admin: User
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated enterprise.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of removing an administrator.
+ """
+ message: String
+
+ """
+ The viewer performing the mutation.
+ """
+ viewer: User
+}
+
+"""
+Autogenerated input type of RemoveEnterpriseIdentityProvider
+"""
+input RemoveEnterpriseIdentityProviderInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise from which to remove the identity provider.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+}
+
+"""
+Autogenerated return type of RemoveEnterpriseIdentityProvider
+"""
+type RemoveEnterpriseIdentityProviderPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The identity provider that was removed from the enterprise.
+ """
+ identityProvider: EnterpriseIdentityProvider
+}
+
+"""
+Autogenerated input type of RemoveEnterpriseOrganization
+"""
+input RemoveEnterpriseOrganizationInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise from which the organization should be removed.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The ID of the organization to remove from the enterprise.
+ """
+ organizationId: ID! @possibleTypes(concreteTypes: ["Organization"])
+}
+
+"""
+Autogenerated return type of RemoveEnterpriseOrganization
+"""
+type RemoveEnterpriseOrganizationPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated enterprise.
+ """
+ enterprise: Enterprise
+
+ """
+ The organization that was removed from the enterprise.
+ """
+ organization: Organization
+
+ """
+ The viewer performing the mutation.
+ """
+ viewer: User
+}
+
+"""
+Autogenerated input type of RemoveEnterpriseSupportEntitlement
+"""
+input RemoveEnterpriseSupportEntitlementInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Enterprise which the admin belongs to.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of a member who will lose the support entitlement.
+ """
+ login: String!
+}
+
+"""
+Autogenerated return type of RemoveEnterpriseSupportEntitlement
+"""
+type RemoveEnterpriseSupportEntitlementPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A message confirming the result of removing the support entitlement.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of RemoveLabelsFromLabelable
+"""
+input RemoveLabelsFromLabelableInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ids of labels to remove.
+ """
+ labelIds: [ID!]! @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ The id of the Labelable to remove labels from.
+ """
+ labelableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Labelable")
+}
+
+"""
+Autogenerated return type of RemoveLabelsFromLabelable
+"""
+type RemoveLabelsFromLabelablePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Labelable the labels were removed from.
+ """
+ labelable: Labelable
+}
+
+"""
+Autogenerated input type of RemoveOutsideCollaborator
+"""
+input RemoveOutsideCollaboratorInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the organization to remove the outside collaborator from.
+ """
+ organizationId: ID! @possibleTypes(concreteTypes: ["Organization"])
+
+ """
+ The ID of the outside collaborator to remove.
+ """
+ userId: ID! @possibleTypes(concreteTypes: ["User"])
+}
+
+"""
+Autogenerated return type of RemoveOutsideCollaborator
+"""
+type RemoveOutsideCollaboratorPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The user that was removed as an outside collaborator.
+ """
+ removedUser: User
+}
+
+"""
+Autogenerated input type of RemoveReaction
+"""
+input RemoveReactionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of the emoji reaction to remove.
+ """
+ content: ReactionContent!
+
+ """
+ The Node ID of the subject to modify.
+ """
+ subjectId: ID! @possibleTypes(concreteTypes: ["CommitComment", "Issue", "IssueComment", "PullRequest", "PullRequestReview", "PullRequestReviewComment", "TeamDiscussion", "TeamDiscussionComment"], abstractType: "Reactable")
+}
+
+"""
+Autogenerated return type of RemoveReaction
+"""
+type RemoveReactionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The reaction object.
+ """
+ reaction: Reaction
+
+ """
+ The reactable subject.
+ """
+ subject: Reactable
+}
+
+"""
+Autogenerated input type of RemoveStar
+"""
+input RemoveStarInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Starrable ID to unstar.
+ """
+ starrableId: ID! @possibleTypes(concreteTypes: ["Gist", "Repository", "Topic"], abstractType: "Starrable")
+}
+
+"""
+Autogenerated return type of RemoveStar
+"""
+type RemoveStarPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The starrable.
+ """
+ starrable: Starrable
+}
+
+"""
+Represents a 'removed_from_project' event on a given issue or pull request.
+"""
+type RemovedFromProjectEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Project referenced by event.
+ """
+ project: Project @preview(toggledBy: "starfox-preview")
+
+ """
+ Column name referenced by this project event.
+ """
+ projectColumnName: String! @preview(toggledBy: "starfox-preview")
+}
+
+"""
+Represents a 'renamed' event on a given issue or pull request
+"""
+type RenamedTitleEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the current title of the issue or pull request.
+ """
+ currentTitle: String!
+ id: ID!
+
+ """
+ Identifies the previous title of the issue or pull request.
+ """
+ previousTitle: String!
+
+ """
+ Subject that was renamed.
+ """
+ subject: RenamedTitleSubject!
+}
+
+"""
+An object which has a renamable title
+"""
+union RenamedTitleSubject = Issue | PullRequest
+
+"""
+Autogenerated input type of ReopenIssue
+"""
+input ReopenIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the issue to be opened.
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+}
+
+"""
+Autogenerated return type of ReopenIssue
+"""
+type ReopenIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue that was opened.
+ """
+ issue: Issue
+}
+
+"""
+Autogenerated input type of ReopenPullRequest
+"""
+input ReopenPullRequestInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the pull request to be reopened.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of ReopenPullRequest
+"""
+type ReopenPullRequestPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request that was reopened.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Represents a 'reopened' event on any `Closable`.
+"""
+type ReopenedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Object that was reopened.
+ """
+ closable: Closable!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+}
+
+"""
+Audit log entry for a repo.access event.
+"""
+type RepoAccessAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoAccessAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoAccessAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.add_member event.
+"""
+type RepoAddMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoAddMemberAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoAddMemberAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.add_topic event.
+"""
+type RepoAddTopicAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TopicAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The name of the topic added to the repository
+ """
+ topic: Topic
+
+ """
+ The name of the topic added to the repository
+ """
+ topicName: String
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.archived event.
+"""
+type RepoArchivedAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoArchivedAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoArchivedAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.change_merge_setting event.
+"""
+type RepoChangeMergeSettingAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the change was to enable (true) or disable (false) the merge type
+ """
+ isEnabled: Boolean
+
+ """
+ The merge method affected by the change
+ """
+ mergeType: RepoChangeMergeSettingAuditEntryMergeType
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The merge options available for pull requests to this repository.
+"""
+enum RepoChangeMergeSettingAuditEntryMergeType {
+ """
+ The pull request is added to the base branch in a merge commit.
+ """
+ MERGE
+
+ """
+ Commits from the pull request are added onto the base branch individually without a merge commit.
+ """
+ REBASE
+
+ """
+ The pull request's commits are squashed into a single commit before they are merged to the base branch.
+ """
+ SQUASH
+}
+
+"""
+Audit log entry for a repo.config.disable_anonymous_git_access event.
+"""
+type RepoConfigDisableAnonymousGitAccessAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.disable_collaborators_only event.
+"""
+type RepoConfigDisableCollaboratorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.disable_contributors_only event.
+"""
+type RepoConfigDisableContributorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.disable_sockpuppet_disallowed event.
+"""
+type RepoConfigDisableSockpuppetDisallowedAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.enable_anonymous_git_access event.
+"""
+type RepoConfigEnableAnonymousGitAccessAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.enable_collaborators_only event.
+"""
+type RepoConfigEnableCollaboratorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.enable_contributors_only event.
+"""
+type RepoConfigEnableContributorsOnlyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.enable_sockpuppet_disallowed event.
+"""
+type RepoConfigEnableSockpuppetDisallowedAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.lock_anonymous_git_access event.
+"""
+type RepoConfigLockAnonymousGitAccessAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.config.unlock_anonymous_git_access event.
+"""
+type RepoConfigUnlockAnonymousGitAccessAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repo.create event.
+"""
+type RepoCreateAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The name of the parent repository for this forked repository.
+ """
+ forkParentName: String
+
+ """
+ The name of the root repository for this network.
+ """
+ forkSourceName: String
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoCreateAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoCreateAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.destroy event.
+"""
+type RepoDestroyAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoDestroyAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoDestroyAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.remove_member event.
+"""
+type RepoRemoveMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+
+ """
+ The visibility of the repository
+ """
+ visibility: RepoRemoveMemberAuditEntryVisibility
+}
+
+"""
+The privacy of a repository
+"""
+enum RepoRemoveMemberAuditEntryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repo.remove_topic event.
+"""
+type RepoRemoveTopicAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TopicAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The name of the topic added to the repository
+ """
+ topic: Topic
+
+ """
+ The name of the topic added to the repository
+ """
+ topicName: String
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The reasons a piece of content can be reported or minimized.
+"""
+enum ReportedContentClassifiers {
+ """
+ An abusive or harassing piece of content
+ """
+ ABUSE
+
+ """
+ A duplicated piece of content
+ """
+ DUPLICATE
+
+ """
+ An irrelevant piece of content
+ """
+ OFF_TOPIC
+
+ """
+ An outdated piece of content
+ """
+ OUTDATED
+
+ """
+ The content has been resolved
+ """
+ RESOLVED
+
+ """
+ A spammy piece of content
+ """
+ SPAM
+}
+
+"""
+A repository contains the content for a project.
+"""
+type Repository implements Node & PackageOwner & ProjectOwner & RepositoryInfo & Starrable & Subscribable & UniformResourceLocatable {
+ """
+ A list of users that can be assigned to issues in this repository.
+ """
+ assignableUsers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filters users with query on user name and login
+ """
+ query: String
+ ): UserConnection!
+
+ """
+ A list of branch protection rules for this repository.
+ """
+ branchProtectionRules(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): BranchProtectionRuleConnection!
+
+ """
+ Returns the code of conduct for this repository
+ """
+ codeOfConduct: CodeOfConduct
+
+ """
+ A list of collaborators associated with the repository.
+ """
+ collaborators(
+ """
+ Collaborators affiliation level with a repository.
+ """
+ affiliation: CollaboratorAffiliation
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filters users with query on user name and login
+ """
+ query: String
+ ): RepositoryCollaboratorConnection
+
+ """
+ A list of commit comments associated with the repository.
+ """
+ commitComments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitCommentConnection!
+
+ """
+ Returns a list of contact links associated to the repository
+ """
+ contactLinks: [RepositoryContactLink!]
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The Ref associated with the repository's default branch.
+ """
+ defaultBranchRef: Ref
+
+ """
+ Whether or not branches are automatically deleted when merged in this repository.
+ """
+ deleteBranchOnMerge: Boolean!
+
+ """
+ A list of dependency manifests contained in the repository
+ """
+ dependencyGraphManifests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Cursor to paginate dependencies
+ """
+ dependenciesAfter: String
+
+ """
+ Number of dependencies to fetch
+ """
+ dependenciesFirst: Int
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Flag to scope to only manifests with dependencies
+ """
+ withDependencies: Boolean
+ ): DependencyGraphManifestConnection @preview(toggledBy: "hawkgirl-preview")
+
+ """
+ A list of deploy keys that are on this repository.
+ """
+ deployKeys(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): DeployKeyConnection!
+
+ """
+ Deployments associated with the repository
+ """
+ deployments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Environments to list deployments for
+ """
+ environments: [String!]
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for deployments returned from the connection.
+ """
+ orderBy: DeploymentOrder = {field: CREATED_AT, direction: ASC}
+ ): DeploymentConnection!
+
+ """
+ The description of the repository.
+ """
+ description: String
+
+ """
+ The description of the repository rendered to HTML.
+ """
+ descriptionHTML: HTML!
+
+ """
+ The number of kilobytes this repository occupies on disk.
+ """
+ diskUsage: Int
+
+ """
+ Returns how many forks there are of this repository in the whole network.
+ """
+ forkCount: Int!
+
+ """
+ A list of direct forked repositories.
+ """
+ forks(
+ """
+ Array of viewer's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ current viewer owns.
+ """
+ affiliations: [RepositoryAffiliation]
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ Array of owner's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ organization or user being viewed owns.
+ """
+ ownerAffiliations: [RepositoryAffiliation] = [OWNER, COLLABORATOR]
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ The funding links for this repository
+ """
+ fundingLinks: [FundingLink!]!
+
+ """
+ Indicates if the repository has issues feature enabled.
+ """
+ hasIssuesEnabled: Boolean!
+
+ """
+ Indicates if the repository has the Projects feature enabled.
+ """
+ hasProjectsEnabled: Boolean!
+
+ """
+ Indicates if the repository has wiki feature enabled.
+ """
+ hasWikiEnabled: Boolean!
+
+ """
+ The repository's URL.
+ """
+ homepageUrl: URI
+ id: ID!
+
+ """
+ The interaction ability settings for this repository.
+ """
+ interactionAbility: RepositoryInteractionAbility
+
+ """
+ Indicates if the repository is unmaintained.
+ """
+ isArchived: Boolean!
+
+ """
+ Returns true if blank issue creation is allowed
+ """
+ isBlankIssuesEnabled: Boolean!
+
+ """
+ Returns whether or not this repository disabled.
+ """
+ isDisabled: Boolean!
+
+ """
+ Returns whether or not this repository is empty.
+ """
+ isEmpty: Boolean!
+
+ """
+ Identifies if the repository is a fork.
+ """
+ isFork: Boolean!
+
+ """
+ Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.
+ """
+ isInOrganization: Boolean!
+
+ """
+ Indicates if the repository has been locked or not.
+ """
+ isLocked: Boolean!
+
+ """
+ Identifies if the repository is a mirror.
+ """
+ isMirror: Boolean!
+
+ """
+ Identifies if the repository is private.
+ """
+ isPrivate: Boolean!
+
+ """
+ Returns true if this repository has a security policy
+ """
+ isSecurityPolicyEnabled: Boolean
+
+ """
+ Identifies if the repository is a template that can be used to generate new repositories.
+ """
+ isTemplate: Boolean!
+
+ """
+ Is this repository a user configuration repository?
+ """
+ isUserConfigurationRepository: Boolean!
+
+ """
+ Returns a single issue from the current repository by number.
+ """
+ issue(
+ """
+ The number for the issue to be returned.
+ """
+ number: Int!
+ ): Issue
+
+ """
+ Returns a single issue-like object from the current repository by number.
+ """
+ issueOrPullRequest(
+ """
+ The number for the issue to be returned.
+ """
+ number: Int!
+ ): IssueOrPullRequest
+
+ """
+ Returns a list of issue templates associated to the repository
+ """
+ issueTemplates: [IssueTemplate!]
+
+ """
+ A list of issues that have been opened in the repository.
+ """
+ issues(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filtering options for issues returned from the connection.
+ """
+ filterBy: IssueFilters
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issues returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the issues by.
+ """
+ states: [IssueState!]
+ ): IssueConnection!
+
+ """
+ Returns a single label by name
+ """
+ label(
+ """
+ Label name
+ """
+ name: String!
+ ): Label
+
+ """
+ A list of labels associated with the repository.
+ """
+ labels(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for labels returned from the connection.
+ """
+ orderBy: LabelOrder = {field: CREATED_AT, direction: ASC}
+
+ """
+ If provided, searches labels by name and description.
+ """
+ query: String
+ ): LabelConnection
+
+ """
+ A list containing a breakdown of the language composition of the repository.
+ """
+ languages(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: LanguageOrder
+ ): LanguageConnection
+
+ """
+ Get the latest release for the repository if one exists.
+ """
+ latestRelease: Release
+
+ """
+ The license associated with the repository
+ """
+ licenseInfo: License
+
+ """
+ The reason the repository has been locked.
+ """
+ lockReason: RepositoryLockReason
+
+ """
+ A list of Users that can be mentioned in the context of the repository.
+ """
+ mentionableUsers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filters users with query on user name and login
+ """
+ query: String
+ ): UserConnection!
+
+ """
+ Whether or not PRs are merged with a merge commit on this repository.
+ """
+ mergeCommitAllowed: Boolean!
+
+ """
+ Returns a single milestone from the current repository by number.
+ """
+ milestone(
+ """
+ The number for the milestone to be returned.
+ """
+ number: Int!
+ ): Milestone
+
+ """
+ A list of milestones associated with the repository.
+ """
+ milestones(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for milestones.
+ """
+ orderBy: MilestoneOrder
+
+ """
+ Filters milestones with a query on the title
+ """
+ query: String
+
+ """
+ Filter by the state of the milestones.
+ """
+ states: [MilestoneState!]
+ ): MilestoneConnection
+
+ """
+ The repository's original mirror URL.
+ """
+ mirrorUrl: URI
+
+ """
+ The name of the repository.
+ """
+ name: String!
+
+ """
+ The repository's name with owner.
+ """
+ nameWithOwner: String!
+
+ """
+ A Git object in the repository
+ """
+ object(
+ """
+ A Git revision expression suitable for rev-parse
+ """
+ expression: String
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID
+ ): GitObject
+
+ """
+ The image used to represent this repository in Open Graph data.
+ """
+ openGraphImageUrl: URI!
+
+ """
+ The User owner of the repository.
+ """
+ owner: RepositoryOwner!
+
+ """
+ A list of packages under the owner.
+ """
+ packages(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Find packages by their names.
+ """
+ names: [String]
+
+ """
+ Ordering of the returned packages.
+ """
+ orderBy: PackageOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ Filter registry package by type.
+ """
+ packageType: PackageType
+
+ """
+ Find packages in a repository by ID.
+ """
+ repositoryId: ID
+ ): PackageConnection!
+
+ """
+ The repository parent, if this is a fork.
+ """
+ parent: Repository
+
+ """
+ A list of pinned issues for this repository.
+ """
+ pinnedIssues(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PinnedIssueConnection @preview(toggledBy: "elektra-preview")
+
+ """
+ The primary language of the repository's code.
+ """
+ primaryLanguage: Language
+
+ """
+ Find project by number.
+ """
+ project(
+ """
+ The project number to find.
+ """
+ number: Int!
+ ): Project
+
+ """
+ A list of projects under the owner.
+ """
+ projects(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for projects returned from the connection
+ """
+ orderBy: ProjectOrder
+
+ """
+ Query to search projects by, currently only searching by name.
+ """
+ search: String
+
+ """
+ A list of states to filter the projects by.
+ """
+ states: [ProjectState!]
+ ): ProjectConnection!
+
+ """
+ The HTTP path listing the repository's projects
+ """
+ projectsResourcePath: URI!
+
+ """
+ The HTTP URL listing the repository's projects
+ """
+ projectsUrl: URI!
+
+ """
+ Returns a single pull request from the current repository by number.
+ """
+ pullRequest(
+ """
+ The number for the pull request to be returned.
+ """
+ number: Int!
+ ): PullRequest
+
+ """
+ A list of pull requests that have been opened in the repository.
+ """
+ pullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection!
+
+ """
+ Identifies when the repository was last pushed to.
+ """
+ pushedAt: DateTime
+
+ """
+ Whether or not rebase-merging is enabled on this repository.
+ """
+ rebaseMergeAllowed: Boolean!
+
+ """
+ Fetch a given ref from the repository
+ """
+ ref(
+ """
+ The ref to retrieve. Fully qualified matches are checked in order
+ (`refs/heads/master`) before falling back onto checks for short name matches (`master`).
+ """
+ qualifiedName: String!
+ ): Ref
+
+ """
+ Fetch a list of refs from the repository
+ """
+ refs(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ DEPRECATED: use orderBy. The ordering direction.
+ """
+ direction: OrderDirection
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for refs returned from the connection.
+ """
+ orderBy: RefOrder
+
+ """
+ Filters refs with query on name
+ """
+ query: String
+
+ """
+ A ref name prefix like `refs/heads/`, `refs/tags/`, etc.
+ """
+ refPrefix: String!
+ ): RefConnection
+
+ """
+ Lookup a single release given various criteria.
+ """
+ release(
+ """
+ The name of the Tag the Release was created from
+ """
+ tagName: String!
+ ): Release
+
+ """
+ List of releases which are dependent on this repository.
+ """
+ releases(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: ReleaseOrder
+ ): ReleaseConnection!
+
+ """
+ A list of applied repository-topic associations for this repository.
+ """
+ repositoryTopics(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): RepositoryTopicConnection!
+
+ """
+ The HTTP path for this repository
+ """
+ resourcePath: URI!
+
+ """
+ The security policy URL.
+ """
+ securityPolicyUrl: URI
+
+ """
+ A description of the repository, rendered to HTML without any links in it.
+ """
+ shortDescriptionHTML(
+ """
+ How many characters to return.
+ """
+ limit: Int = 200
+ ): HTML!
+
+ """
+ Whether or not squash-merging is enabled on this repository.
+ """
+ squashMergeAllowed: Boolean!
+
+ """
+ The SSH URL to clone this repository
+ """
+ sshUrl: GitSSHRemote!
+
+ """
+ Returns a count of how many stargazers there are on this object
+ """
+ stargazerCount: Int!
+
+ """
+ A list of users who have starred this starrable.
+ """
+ stargazers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: StarOrder
+ ): StargazerConnection!
+
+ """
+ Returns a list of all submodules in this repository parsed from the
+ .gitmodules file as of the default branch's HEAD commit.
+ """
+ submodules(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): SubmoduleConnection!
+
+ """
+ Temporary authentication token for cloning this repository.
+ """
+ tempCloneToken: String
+
+ """
+ The repository from which this repository was generated, if any.
+ """
+ templateRepository: Repository
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this repository
+ """
+ url: URI!
+
+ """
+ Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.
+ """
+ usesCustomOpenGraphImage: Boolean!
+
+ """
+ Indicates whether the viewer has admin permissions on this repository.
+ """
+ viewerCanAdminister: Boolean!
+
+ """
+ Can the current viewer create new projects on this owner.
+ """
+ viewerCanCreateProjects: Boolean!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Indicates whether the viewer can update the topics of this repository.
+ """
+ viewerCanUpdateTopics: Boolean!
+
+ """
+ The last commit email for the viewer.
+ """
+ viewerDefaultCommitEmail: String
+
+ """
+ The last used merge method by the viewer or the default for the repository.
+ """
+ viewerDefaultMergeMethod: PullRequestMergeMethod!
+
+ """
+ Returns a boolean indicating whether the viewing user has starred this starrable.
+ """
+ viewerHasStarred: Boolean!
+
+ """
+ The users permission level on the repository. Will return null if authenticated as an GitHub App.
+ """
+ viewerPermission: RepositoryPermission
+
+ """
+ A list of emails this viewer can commit with.
+ """
+ viewerPossibleCommitEmails: [String!]
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+
+ """
+ A list of vulnerability alerts that are on this repository.
+ """
+ vulnerabilityAlerts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): RepositoryVulnerabilityAlertConnection
+
+ """
+ A list of users watching the repository.
+ """
+ watchers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserConnection!
+}
+
+"""
+The affiliation of a user to a repository
+"""
+enum RepositoryAffiliation {
+ """
+ Repositories that the user has been added to as a collaborator.
+ """
+ COLLABORATOR
+
+ """
+ Repositories that the user has access to through being a member of an
+ organization. This includes every repository on every team that the user is on.
+ """
+ ORGANIZATION_MEMBER
+
+ """
+ Repositories that are owned by the authenticated user.
+ """
+ OWNER
+}
+
+"""
+Metadata for an audit entry with action repo.*
+"""
+interface RepositoryAuditEntryData {
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+}
+
+"""
+The connection type for User.
+"""
+type RepositoryCollaboratorConnection {
+ """
+ A list of edges.
+ """
+ edges: [RepositoryCollaboratorEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a user who is a collaborator of a repository.
+"""
+type RepositoryCollaboratorEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+ node: User!
+
+ """
+ The permission the user has on the repository.
+ """
+ permission: RepositoryPermission!
+
+ """
+ A list of sources for the user's access to the repository.
+ """
+ permissionSources: [PermissionSource!]
+}
+
+"""
+A list of repositories owned by the subject.
+"""
+type RepositoryConnection {
+ """
+ A list of edges.
+ """
+ edges: [RepositoryEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Repository]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+
+ """
+ The total size in kilobytes of all repositories in the connection.
+ """
+ totalDiskUsage: Int!
+}
+
+"""
+A repository contact link.
+"""
+type RepositoryContactLink {
+ """
+ The contact link purpose.
+ """
+ about: String!
+
+ """
+ The contact link name.
+ """
+ name: String!
+
+ """
+ The contact link URL.
+ """
+ url: URI!
+}
+
+"""
+The reason a repository is listed as 'contributed'.
+"""
+enum RepositoryContributionType {
+ """
+ Created a commit
+ """
+ COMMIT
+
+ """
+ Created an issue
+ """
+ ISSUE
+
+ """
+ Created a pull request
+ """
+ PULL_REQUEST
+
+ """
+ Reviewed a pull request
+ """
+ PULL_REQUEST_REVIEW
+
+ """
+ Created the repository
+ """
+ REPOSITORY
+}
+
+"""
+An edge in a connection.
+"""
+type RepositoryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Repository
+}
+
+"""
+A subset of repository info.
+"""
+interface RepositoryInfo {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The description of the repository.
+ """
+ description: String
+
+ """
+ The description of the repository rendered to HTML.
+ """
+ descriptionHTML: HTML!
+
+ """
+ Returns how many forks there are of this repository in the whole network.
+ """
+ forkCount: Int!
+
+ """
+ Indicates if the repository has issues feature enabled.
+ """
+ hasIssuesEnabled: Boolean!
+
+ """
+ Indicates if the repository has the Projects feature enabled.
+ """
+ hasProjectsEnabled: Boolean!
+
+ """
+ Indicates if the repository has wiki feature enabled.
+ """
+ hasWikiEnabled: Boolean!
+
+ """
+ The repository's URL.
+ """
+ homepageUrl: URI
+
+ """
+ Indicates if the repository is unmaintained.
+ """
+ isArchived: Boolean!
+
+ """
+ Identifies if the repository is a fork.
+ """
+ isFork: Boolean!
+
+ """
+ Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.
+ """
+ isInOrganization: Boolean!
+
+ """
+ Indicates if the repository has been locked or not.
+ """
+ isLocked: Boolean!
+
+ """
+ Identifies if the repository is a mirror.
+ """
+ isMirror: Boolean!
+
+ """
+ Identifies if the repository is private.
+ """
+ isPrivate: Boolean!
+
+ """
+ Identifies if the repository is a template that can be used to generate new repositories.
+ """
+ isTemplate: Boolean!
+
+ """
+ The license associated with the repository
+ """
+ licenseInfo: License
+
+ """
+ The reason the repository has been locked.
+ """
+ lockReason: RepositoryLockReason
+
+ """
+ The repository's original mirror URL.
+ """
+ mirrorUrl: URI
+
+ """
+ The name of the repository.
+ """
+ name: String!
+
+ """
+ The repository's name with owner.
+ """
+ nameWithOwner: String!
+
+ """
+ The image used to represent this repository in Open Graph data.
+ """
+ openGraphImageUrl: URI!
+
+ """
+ The User owner of the repository.
+ """
+ owner: RepositoryOwner!
+
+ """
+ Identifies when the repository was last pushed to.
+ """
+ pushedAt: DateTime
+
+ """
+ The HTTP path for this repository
+ """
+ resourcePath: URI!
+
+ """
+ A description of the repository, rendered to HTML without any links in it.
+ """
+ shortDescriptionHTML(
+ """
+ How many characters to return.
+ """
+ limit: Int = 200
+ ): HTML!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this repository
+ """
+ url: URI!
+
+ """
+ Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.
+ """
+ usesCustomOpenGraphImage: Boolean!
+}
+
+"""
+Repository interaction limit that applies to this object.
+"""
+type RepositoryInteractionAbility {
+ """
+ The time the currently active limit expires.
+ """
+ expiresAt: DateTime
+
+ """
+ The current limit that is enabled on this object.
+ """
+ limit: RepositoryInteractionLimit!
+
+ """
+ The origin of the currently active interaction limit.
+ """
+ origin: RepositoryInteractionLimitOrigin!
+}
+
+"""
+A repository interaction limit.
+"""
+enum RepositoryInteractionLimit {
+ """
+ Users that are not collaborators will not be able to interact with the repository.
+ """
+ COLLABORATORS_ONLY
+
+ """
+ Users that have not previously committed to a repository’s default branch will be unable to interact with the repository.
+ """
+ CONTRIBUTORS_ONLY
+
+ """
+ Users that have recently created their account will be unable to interact with the repository.
+ """
+ EXISTING_USERS
+
+ """
+ No interaction limits are enabled.
+ """
+ NO_LIMIT
+}
+
+"""
+The length for a repository interaction limit to be enabled for.
+"""
+enum RepositoryInteractionLimitExpiry {
+ """
+ The interaction limit will expire after 1 day.
+ """
+ ONE_DAY
+
+ """
+ The interaction limit will expire after 1 month.
+ """
+ ONE_MONTH
+
+ """
+ The interaction limit will expire after 1 week.
+ """
+ ONE_WEEK
+
+ """
+ The interaction limit will expire after 6 months.
+ """
+ SIX_MONTHS
+
+ """
+ The interaction limit will expire after 3 days.
+ """
+ THREE_DAYS
+}
+
+"""
+Indicates where an interaction limit is configured.
+"""
+enum RepositoryInteractionLimitOrigin {
+ """
+ A limit that is configured at the organization level.
+ """
+ ORGANIZATION
+
+ """
+ A limit that is configured at the repository level.
+ """
+ REPOSITORY
+
+ """
+ A limit that is configured at the user-wide level.
+ """
+ USER
+}
+
+"""
+An invitation for a user to be added to a repository.
+"""
+type RepositoryInvitation implements Node {
+ """
+ The email address that received the invitation.
+ """
+ email: String
+ id: ID!
+
+ """
+ The user who received the invitation.
+ """
+ invitee: User
+
+ """
+ The user who created the invitation.
+ """
+ inviter: User!
+
+ """
+ The permalink for this repository invitation.
+ """
+ permalink: URI!
+
+ """
+ The permission granted on this repository by this invitation.
+ """
+ permission: RepositoryPermission!
+
+ """
+ The Repository the user is invited to.
+ """
+ repository: RepositoryInfo
+}
+
+"""
+The connection type for RepositoryInvitation.
+"""
+type RepositoryInvitationConnection {
+ """
+ A list of edges.
+ """
+ edges: [RepositoryInvitationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [RepositoryInvitation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type RepositoryInvitationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: RepositoryInvitation
+}
+
+"""
+Ordering options for repository invitation connections.
+"""
+input RepositoryInvitationOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order repository invitations by.
+ """
+ field: RepositoryInvitationOrderField!
+}
+
+"""
+Properties by which repository invitation connections can be ordered.
+"""
+enum RepositoryInvitationOrderField {
+ """
+ Order repository invitations by creation time
+ """
+ CREATED_AT
+
+ """
+ Order repository invitations by invitee login
+ """
+ INVITEE_LOGIN @deprecated(reason: "`INVITEE_LOGIN` is no longer a valid field value. Repository invitations can now be associated with an email, not only an invitee. Removal on 2020-10-01 UTC.")
+}
+
+"""
+The possible reasons a given repository could be in a locked state.
+"""
+enum RepositoryLockReason {
+ """
+ The repository is locked due to a billing related reason.
+ """
+ BILLING
+
+ """
+ The repository is locked due to a migration.
+ """
+ MIGRATING
+
+ """
+ The repository is locked due to a move.
+ """
+ MOVING
+
+ """
+ The repository is locked due to a rename.
+ """
+ RENAME
+}
+
+"""
+Represents a object that belongs to a repository.
+"""
+interface RepositoryNode {
+ """
+ The repository associated with this node.
+ """
+ repository: Repository!
+}
+
+"""
+Ordering options for repository connections
+"""
+input RepositoryOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order repositories by.
+ """
+ field: RepositoryOrderField!
+}
+
+"""
+Properties by which repository connections can be ordered.
+"""
+enum RepositoryOrderField {
+ """
+ Order repositories by creation time
+ """
+ CREATED_AT
+
+ """
+ Order repositories by name
+ """
+ NAME
+
+ """
+ Order repositories by push time
+ """
+ PUSHED_AT
+
+ """
+ Order repositories by number of stargazers
+ """
+ STARGAZERS
+
+ """
+ Order repositories by update time
+ """
+ UPDATED_AT
+}
+
+"""
+Represents an owner of a Repository.
+"""
+interface RepositoryOwner {
+ """
+ A URL pointing to the owner's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+ id: ID!
+
+ """
+ The username used to login.
+ """
+ login: String!
+
+ """
+ A list of repositories that the user owns.
+ """
+ repositories(
+ """
+ Array of viewer's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ current viewer owns.
+ """
+ affiliations: [RepositoryAffiliation]
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If non-null, filters repositories according to whether they are forks of another repository
+ """
+ isFork: Boolean
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ Array of owner's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ organization or user being viewed owns.
+ """
+ ownerAffiliations: [RepositoryAffiliation] = [OWNER, COLLABORATOR]
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ Find Repository.
+ """
+ repository(
+ """
+ Name of Repository to find.
+ """
+ name: String!
+ ): Repository
+
+ """
+ The HTTP URL for the owner.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for the owner.
+ """
+ url: URI!
+}
+
+"""
+The access level to a repository
+"""
+enum RepositoryPermission {
+ """
+ Can read, clone, and push to this repository. Can also manage issues, pull
+ requests, and repository settings, including adding collaborators
+ """
+ ADMIN
+
+ """
+ Can read, clone, and push to this repository. They can also manage issues, pull requests, and some repository settings
+ """
+ MAINTAIN
+
+ """
+ Can read and clone this repository. Can also open and comment on issues and pull requests
+ """
+ READ
+
+ """
+ Can read and clone this repository. Can also manage issues and pull requests
+ """
+ TRIAGE
+
+ """
+ Can read, clone, and push to this repository. Can also manage issues and pull requests
+ """
+ WRITE
+}
+
+"""
+The privacy of a repository
+"""
+enum RepositoryPrivacy {
+ """
+ Private
+ """
+ PRIVATE
+
+ """
+ Public
+ """
+ PUBLIC
+}
+
+"""
+A repository-topic connects a repository to a topic.
+"""
+type RepositoryTopic implements Node & UniformResourceLocatable {
+ id: ID!
+
+ """
+ The HTTP path for this repository-topic.
+ """
+ resourcePath: URI!
+
+ """
+ The topic.
+ """
+ topic: Topic!
+
+ """
+ The HTTP URL for this repository-topic.
+ """
+ url: URI!
+}
+
+"""
+The connection type for RepositoryTopic.
+"""
+type RepositoryTopicConnection {
+ """
+ A list of edges.
+ """
+ edges: [RepositoryTopicEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [RepositoryTopic]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type RepositoryTopicEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: RepositoryTopic
+}
+
+"""
+The repository's visibility level.
+"""
+enum RepositoryVisibility {
+ """
+ The repository is visible only to users in the same business.
+ """
+ INTERNAL
+
+ """
+ The repository is visible only to those with explicit access.
+ """
+ PRIVATE
+
+ """
+ The repository is visible to everyone.
+ """
+ PUBLIC
+}
+
+"""
+Audit log entry for a repository_visibility_change.disable event.
+"""
+type RepositoryVisibilityChangeDisableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a repository_visibility_change.enable event.
+"""
+type RepositoryVisibilityChangeEnableAuditEntry implements AuditEntry & EnterpriseAuditEntryData & Node & OrganizationAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+
+ """
+ The HTTP path for this enterprise.
+ """
+ enterpriseResourcePath: URI
+
+ """
+ The slug of the enterprise.
+ """
+ enterpriseSlug: String
+
+ """
+ The HTTP URL for this enterprise.
+ """
+ enterpriseUrl: URI
+ id: ID!
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+A alert for a repository with an affected vulnerability.
+"""
+type RepositoryVulnerabilityAlert implements Node & RepositoryNode {
+ """
+ When was the alert created?
+ """
+ createdAt: DateTime!
+
+ """
+ The reason the alert was dismissed
+ """
+ dismissReason: String
+
+ """
+ When was the alert dismissed?
+ """
+ dismissedAt: DateTime
+
+ """
+ The user who dismissed the alert
+ """
+ dismisser: User
+ id: ID!
+
+ """
+ The associated repository
+ """
+ repository: Repository!
+
+ """
+ The associated security advisory
+ """
+ securityAdvisory: SecurityAdvisory
+
+ """
+ The associated security vulnerability
+ """
+ securityVulnerability: SecurityVulnerability
+
+ """
+ The vulnerable manifest filename
+ """
+ vulnerableManifestFilename: String!
+
+ """
+ The vulnerable manifest path
+ """
+ vulnerableManifestPath: String!
+
+ """
+ The vulnerable requirements
+ """
+ vulnerableRequirements: String
+}
+
+"""
+The connection type for RepositoryVulnerabilityAlert.
+"""
+type RepositoryVulnerabilityAlertConnection {
+ """
+ A list of edges.
+ """
+ edges: [RepositoryVulnerabilityAlertEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [RepositoryVulnerabilityAlert]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type RepositoryVulnerabilityAlertEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: RepositoryVulnerabilityAlert
+}
+
+"""
+Autogenerated input type of RequestReviews
+"""
+input RequestReviewsInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the pull request to modify.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The Node IDs of the team to request.
+ """
+ teamIds: [ID!] @possibleTypes(concreteTypes: ["Team"])
+
+ """
+ Add users to the set rather than replace.
+ """
+ union: Boolean
+
+ """
+ The Node IDs of the user to request.
+ """
+ userIds: [ID!] @possibleTypes(concreteTypes: ["User"])
+}
+
+"""
+Autogenerated return type of RequestReviews
+"""
+type RequestReviewsPayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The pull request that is getting requests.
+ """
+ pullRequest: PullRequest
+
+ """
+ The edge from the pull request to the requested reviewers.
+ """
+ requestedReviewersEdge: UserEdge
+}
+
+"""
+The possible states that can be requested when creating a check run.
+"""
+enum RequestableCheckStatusState {
+ """
+ The check suite or run has been completed.
+ """
+ COMPLETED
+
+ """
+ The check suite or run is in progress.
+ """
+ IN_PROGRESS
+
+ """
+ The check suite or run has been queued.
+ """
+ QUEUED
+
+ """
+ The check suite or run is in waiting state.
+ """
+ WAITING
+}
+
+"""
+Types that can be requested reviewers.
+"""
+union RequestedReviewer = Mannequin | Team | User
+
+"""
+Autogenerated input type of RerequestCheckSuite
+"""
+input RerequestCheckSuiteInput {
+ """
+ The Node ID of the check suite.
+ """
+ checkSuiteId: ID! @possibleTypes(concreteTypes: ["CheckSuite"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of RerequestCheckSuite
+"""
+type RerequestCheckSuitePayload {
+ """
+ The requested check suite.
+ """
+ checkSuite: CheckSuite
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of ResolveReviewThread
+"""
+input ResolveReviewThreadInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the thread to resolve
+ """
+ threadId: ID! @possibleTypes(concreteTypes: ["PullRequestReviewThread"])
+}
+
+"""
+Autogenerated return type of ResolveReviewThread
+"""
+type ResolveReviewThreadPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The thread to resolve.
+ """
+ thread: PullRequestReviewThread
+}
+
+"""
+Represents a private contribution a user made on GitHub.
+"""
+type RestrictedContribution implements Contribution {
+ """
+ Whether this contribution is associated with a record you do not have access to. For
+ example, your own 'first issue' contribution may have been made on a repository you can no
+ longer access.
+ """
+ isRestricted: Boolean!
+
+ """
+ When this contribution was made.
+ """
+ occurredAt: DateTime!
+
+ """
+ The HTTP path for this contribution.
+ """
+ resourcePath: URI!
+
+ """
+ The HTTP URL for this contribution.
+ """
+ url: URI!
+
+ """
+ The user who made this contribution.
+ """
+ user: User!
+}
+
+"""
+A team or user who has the ability to dismiss a review on a protected branch.
+"""
+type ReviewDismissalAllowance implements Node {
+ """
+ The actor that can dismiss.
+ """
+ actor: ReviewDismissalAllowanceActor
+
+ """
+ Identifies the branch protection rule associated with the allowed user or team.
+ """
+ branchProtectionRule: BranchProtectionRule
+ id: ID!
+}
+
+"""
+Types that can be an actor.
+"""
+union ReviewDismissalAllowanceActor = Team | User
+
+"""
+The connection type for ReviewDismissalAllowance.
+"""
+type ReviewDismissalAllowanceConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReviewDismissalAllowanceEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ReviewDismissalAllowance]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ReviewDismissalAllowanceEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ReviewDismissalAllowance
+}
+
+"""
+Represents a 'review_dismissed' event on a given issue or pull request.
+"""
+type ReviewDismissedEvent implements Node & UniformResourceLocatable {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ Identifies the optional message associated with the 'review_dismissed' event.
+ """
+ dismissalMessage: String
+
+ """
+ Identifies the optional message associated with the event, rendered to HTML.
+ """
+ dismissalMessageHTML: String
+ id: ID!
+
+ """
+ Identifies the previous state of the review with the 'review_dismissed' event.
+ """
+ previousReviewState: PullRequestReviewState!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the commit which caused the review to become stale.
+ """
+ pullRequestCommit: PullRequestCommit
+
+ """
+ The HTTP path for this review dismissed event.
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the review associated with the 'review_dismissed' event.
+ """
+ review: PullRequestReview
+
+ """
+ The HTTP URL for this review dismissed event.
+ """
+ url: URI!
+}
+
+"""
+A request for a user to review a pull request.
+"""
+type ReviewRequest implements Node {
+ """
+ Whether this request was created for a code owner
+ """
+ asCodeOwner: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ Identifies the pull request associated with this review request.
+ """
+ pullRequest: PullRequest!
+
+ """
+ The reviewer that is requested.
+ """
+ requestedReviewer: RequestedReviewer
+}
+
+"""
+The connection type for ReviewRequest.
+"""
+type ReviewRequestConnection {
+ """
+ A list of edges.
+ """
+ edges: [ReviewRequestEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [ReviewRequest]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type ReviewRequestEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: ReviewRequest
+}
+
+"""
+Represents an 'review_request_removed' event on a given pull request.
+"""
+type ReviewRequestRemovedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the reviewer whose review request was removed.
+ """
+ requestedReviewer: RequestedReviewer
+}
+
+"""
+Represents an 'review_requested' event on a given pull request.
+"""
+type ReviewRequestedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ PullRequest referenced by event.
+ """
+ pullRequest: PullRequest!
+
+ """
+ Identifies the reviewer whose review was requested.
+ """
+ requestedReviewer: RequestedReviewer
+}
+
+"""
+A hovercard context with a message describing the current code review state of the pull
+request.
+"""
+type ReviewStatusHovercardContext implements HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+
+ """
+ The current status of the pull request with respect to code review.
+ """
+ reviewDecision: PullRequestReviewDecision
+}
+
+"""
+The possible digest algorithms used to sign SAML requests for an identity provider.
+"""
+enum SamlDigestAlgorithm {
+ """
+ SHA1
+ """
+ SHA1
+
+ """
+ SHA256
+ """
+ SHA256
+
+ """
+ SHA384
+ """
+ SHA384
+
+ """
+ SHA512
+ """
+ SHA512
+}
+
+"""
+The possible signature algorithms used to sign SAML requests for a Identity Provider.
+"""
+enum SamlSignatureAlgorithm {
+ """
+ RSA-SHA1
+ """
+ RSA_SHA1
+
+ """
+ RSA-SHA256
+ """
+ RSA_SHA256
+
+ """
+ RSA-SHA384
+ """
+ RSA_SHA384
+
+ """
+ RSA-SHA512
+ """
+ RSA_SHA512
+}
+
+"""
+A Saved Reply is text a user can use to reply quickly.
+"""
+type SavedReply implements Node {
+ """
+ The body of the saved reply.
+ """
+ body: String!
+
+ """
+ The saved reply body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+ id: ID!
+
+ """
+ The title of the saved reply.
+ """
+ title: String!
+
+ """
+ The user that saved this reply.
+ """
+ user: Actor
+}
+
+"""
+The connection type for SavedReply.
+"""
+type SavedReplyConnection {
+ """
+ A list of edges.
+ """
+ edges: [SavedReplyEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [SavedReply]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SavedReplyEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: SavedReply
+}
+
+"""
+Ordering options for saved reply connections.
+"""
+input SavedReplyOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order saved replies by.
+ """
+ field: SavedReplyOrderField!
+}
+
+"""
+Properties by which saved reply connections can be ordered.
+"""
+enum SavedReplyOrderField {
+ """
+ Order saved reply by when they were updated.
+ """
+ UPDATED_AT
+}
+
+"""
+The results of a search.
+"""
+union SearchResultItem = App | Issue | MarketplaceListing | Organization | PullRequest | Repository | User
+
+"""
+A list of results that matched against a search query.
+"""
+type SearchResultItemConnection {
+ """
+ The number of pieces of code that matched the search query.
+ """
+ codeCount: Int!
+
+ """
+ A list of edges.
+ """
+ edges: [SearchResultItemEdge]
+
+ """
+ The number of issues that matched the search query.
+ """
+ issueCount: Int!
+
+ """
+ A list of nodes.
+ """
+ nodes: [SearchResultItem]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ The number of repositories that matched the search query.
+ """
+ repositoryCount: Int!
+
+ """
+ The number of users that matched the search query.
+ """
+ userCount: Int!
+
+ """
+ The number of wiki pages that matched the search query.
+ """
+ wikiCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SearchResultItemEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: SearchResultItem
+
+ """
+ Text matches on the result found.
+ """
+ textMatches: [TextMatch]
+}
+
+"""
+Represents the individual results of a search.
+"""
+enum SearchType {
+ """
+ Returns results matching issues in repositories.
+ """
+ ISSUE
+
+ """
+ Returns results matching repositories.
+ """
+ REPOSITORY
+
+ """
+ Returns results matching users and organizations on GitHub.
+ """
+ USER
+}
+
+"""
+A GitHub Security Advisory
+"""
+type SecurityAdvisory implements Node {
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ This is a long plaintext description of the advisory
+ """
+ description: String!
+
+ """
+ The GitHub Security Advisory ID
+ """
+ ghsaId: String!
+ id: ID!
+
+ """
+ A list of identifiers for this advisory
+ """
+ identifiers: [SecurityAdvisoryIdentifier!]!
+
+ """
+ The organization that originated the advisory
+ """
+ origin: String!
+
+ """
+ The permalink for the advisory
+ """
+ permalink: URI
+
+ """
+ When the advisory was published
+ """
+ publishedAt: DateTime!
+
+ """
+ A list of references for this advisory
+ """
+ references: [SecurityAdvisoryReference!]!
+
+ """
+ The severity of the advisory
+ """
+ severity: SecurityAdvisorySeverity!
+
+ """
+ A short plaintext summary of the advisory
+ """
+ summary: String!
+
+ """
+ When the advisory was last updated
+ """
+ updatedAt: DateTime!
+
+ """
+ Vulnerabilities associated with this Advisory
+ """
+ vulnerabilities(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ An ecosystem to filter vulnerabilities by.
+ """
+ ecosystem: SecurityAdvisoryEcosystem
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for the returned topics.
+ """
+ orderBy: SecurityVulnerabilityOrder = {field: UPDATED_AT, direction: DESC}
+
+ """
+ A package name to filter vulnerabilities by.
+ """
+ package: String
+
+ """
+ A list of severities to filter vulnerabilities by.
+ """
+ severities: [SecurityAdvisorySeverity!]
+ ): SecurityVulnerabilityConnection!
+
+ """
+ When the advisory was withdrawn, if it has been withdrawn
+ """
+ withdrawnAt: DateTime
+}
+
+"""
+The connection type for SecurityAdvisory.
+"""
+type SecurityAdvisoryConnection {
+ """
+ A list of edges.
+ """
+ edges: [SecurityAdvisoryEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [SecurityAdvisory]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+The possible ecosystems of a security vulnerability's package.
+"""
+enum SecurityAdvisoryEcosystem {
+ """
+ PHP packages hosted at packagist.org
+ """
+ COMPOSER
+
+ """
+ Java artifacts hosted at the Maven central repository
+ """
+ MAVEN
+
+ """
+ JavaScript packages hosted at npmjs.com
+ """
+ NPM
+
+ """
+ .NET packages hosted at the NuGet Gallery
+ """
+ NUGET
+
+ """
+ Python packages hosted at PyPI.org
+ """
+ PIP
+
+ """
+ Ruby gems hosted at RubyGems.org
+ """
+ RUBYGEMS
+}
+
+"""
+An edge in a connection.
+"""
+type SecurityAdvisoryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: SecurityAdvisory
+}
+
+"""
+A GitHub Security Advisory Identifier
+"""
+type SecurityAdvisoryIdentifier {
+ """
+ The identifier type, e.g. GHSA, CVE
+ """
+ type: String!
+
+ """
+ The identifier
+ """
+ value: String!
+}
+
+"""
+An advisory identifier to filter results on.
+"""
+input SecurityAdvisoryIdentifierFilter {
+ """
+ The identifier type.
+ """
+ type: SecurityAdvisoryIdentifierType!
+
+ """
+ The identifier string. Supports exact or partial matching.
+ """
+ value: String!
+}
+
+"""
+Identifier formats available for advisories.
+"""
+enum SecurityAdvisoryIdentifierType {
+ """
+ Common Vulnerabilities and Exposures Identifier.
+ """
+ CVE
+
+ """
+ GitHub Security Advisory ID.
+ """
+ GHSA
+}
+
+"""
+Ordering options for security advisory connections
+"""
+input SecurityAdvisoryOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order security advisories by.
+ """
+ field: SecurityAdvisoryOrderField!
+}
+
+"""
+Properties by which security advisory connections can be ordered.
+"""
+enum SecurityAdvisoryOrderField {
+ """
+ Order advisories by publication time
+ """
+ PUBLISHED_AT
+
+ """
+ Order advisories by update time
+ """
+ UPDATED_AT
+}
+
+"""
+An individual package
+"""
+type SecurityAdvisoryPackage {
+ """
+ The ecosystem the package belongs to, e.g. RUBYGEMS, NPM
+ """
+ ecosystem: SecurityAdvisoryEcosystem!
+
+ """
+ The package name
+ """
+ name: String!
+}
+
+"""
+An individual package version
+"""
+type SecurityAdvisoryPackageVersion {
+ """
+ The package name or version
+ """
+ identifier: String!
+}
+
+"""
+A GitHub Security Advisory Reference
+"""
+type SecurityAdvisoryReference {
+ """
+ A publicly accessible reference
+ """
+ url: URI!
+}
+
+"""
+Severity of the vulnerability.
+"""
+enum SecurityAdvisorySeverity {
+ """
+ Critical.
+ """
+ CRITICAL
+
+ """
+ High.
+ """
+ HIGH
+
+ """
+ Low.
+ """
+ LOW
+
+ """
+ Moderate.
+ """
+ MODERATE
+}
+
+"""
+An individual vulnerability within an Advisory
+"""
+type SecurityVulnerability {
+ """
+ The Advisory associated with this Vulnerability
+ """
+ advisory: SecurityAdvisory!
+
+ """
+ The first version containing a fix for the vulnerability
+ """
+ firstPatchedVersion: SecurityAdvisoryPackageVersion
+
+ """
+ A description of the vulnerable package
+ """
+ package: SecurityAdvisoryPackage!
+
+ """
+ The severity of the vulnerability within this package
+ """
+ severity: SecurityAdvisorySeverity!
+
+ """
+ When the vulnerability was last updated
+ """
+ updatedAt: DateTime!
+
+ """
+ A string that describes the vulnerable package versions.
+ This string follows a basic syntax with a few forms.
+ + `= 0.2.0` denotes a single vulnerable version.
+ + `<= 1.0.8` denotes a version range up to and including the specified version
+ + `< 0.1.11` denotes a version range up to, but excluding, the specified version
+ + `>= 4.3.0, < 4.3.5` denotes a version range with a known minimum and maximum version.
+ + `>= 0.0.1` denotes a version range with a known minimum, but no known maximum
+ """
+ vulnerableVersionRange: String!
+}
+
+"""
+The connection type for SecurityVulnerability.
+"""
+type SecurityVulnerabilityConnection {
+ """
+ A list of edges.
+ """
+ edges: [SecurityVulnerabilityEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [SecurityVulnerability]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SecurityVulnerabilityEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: SecurityVulnerability
+}
+
+"""
+Ordering options for security vulnerability connections
+"""
+input SecurityVulnerabilityOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order security vulnerabilities by.
+ """
+ field: SecurityVulnerabilityOrderField!
+}
+
+"""
+Properties by which security vulnerability connections can be ordered.
+"""
+enum SecurityVulnerabilityOrderField {
+ """
+ Order vulnerability by update time
+ """
+ UPDATED_AT
+}
+
+"""
+Autogenerated input type of SetEnterpriseIdentityProvider
+"""
+input SetEnterpriseIdentityProviderInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The digest algorithm used to sign SAML requests for the identity provider.
+ """
+ digestMethod: SamlDigestAlgorithm!
+
+ """
+ The ID of the enterprise on which to set an identity provider.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The x509 certificate used by the identity provider to sign assertions and responses.
+ """
+ idpCertificate: String!
+
+ """
+ The Issuer Entity ID for the SAML identity provider
+ """
+ issuer: String
+
+ """
+ The signature algorithm used to sign SAML requests for the identity provider.
+ """
+ signatureMethod: SamlSignatureAlgorithm!
+
+ """
+ The URL endpoint for the identity provider's SAML SSO.
+ """
+ ssoUrl: URI!
+}
+
+"""
+Autogenerated return type of SetEnterpriseIdentityProvider
+"""
+type SetEnterpriseIdentityProviderPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The identity provider for the enterprise.
+ """
+ identityProvider: EnterpriseIdentityProvider
+}
+
+"""
+Autogenerated input type of SetOrganizationInteractionLimit
+"""
+input SetOrganizationInteractionLimitInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ When this limit should expire.
+ """
+ expiry: RepositoryInteractionLimitExpiry
+
+ """
+ The limit to set.
+ """
+ limit: RepositoryInteractionLimit!
+
+ """
+ The ID of the organization to set a limit for.
+ """
+ organizationId: ID! @possibleTypes(concreteTypes: ["Organization"])
+}
+
+"""
+Autogenerated return type of SetOrganizationInteractionLimit
+"""
+type SetOrganizationInteractionLimitPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The organization that the interaction limit was set for.
+ """
+ organization: Organization
+}
+
+"""
+Autogenerated input type of SetRepositoryInteractionLimit
+"""
+input SetRepositoryInteractionLimitInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ When this limit should expire.
+ """
+ expiry: RepositoryInteractionLimitExpiry
+
+ """
+ The limit to set.
+ """
+ limit: RepositoryInteractionLimit!
+
+ """
+ The ID of the repository to set a limit for.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of SetRepositoryInteractionLimit
+"""
+type SetRepositoryInteractionLimitPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The repository that the interaction limit was set for.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of SetUserInteractionLimit
+"""
+input SetUserInteractionLimitInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ When this limit should expire.
+ """
+ expiry: RepositoryInteractionLimitExpiry
+
+ """
+ The limit to set.
+ """
+ limit: RepositoryInteractionLimit!
+
+ """
+ The ID of the user to set a limit for.
+ """
+ userId: ID! @possibleTypes(concreteTypes: ["User"])
+}
+
+"""
+Autogenerated return type of SetUserInteractionLimit
+"""
+type SetUserInteractionLimitPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The user that the interaction limit was set for.
+ """
+ user: User
+}
+
+"""
+Represents an S/MIME signature on a Commit or Tag.
+"""
+type SmimeSignature implements GitSignature {
+ """
+ Email used to sign this object.
+ """
+ email: String!
+
+ """
+ True if the signature is valid and verified by GitHub.
+ """
+ isValid: Boolean!
+
+ """
+ Payload for GPG signing object. Raw ODB object without the signature header.
+ """
+ payload: String!
+
+ """
+ ASCII-armored signature header from object.
+ """
+ signature: String!
+
+ """
+ GitHub user corresponding to the email signing this commit.
+ """
+ signer: User
+
+ """
+ The state of this signature. `VALID` if signature is valid and verified by
+ GitHub, otherwise represents reason why signature is considered invalid.
+ """
+ state: GitSignatureState!
+
+ """
+ True if the signature was made with GitHub's signing key.
+ """
+ wasSignedByGitHub: Boolean!
+}
+
+"""
+Entities that can sponsor others via GitHub Sponsors
+"""
+union Sponsor = Organization | User
+
+"""
+Entities that can be sponsored through GitHub Sponsors
+"""
+interface Sponsorable {
+ """
+ True if this user/organization has a GitHub Sponsors listing.
+ """
+ hasSponsorsListing: Boolean!
+
+ """
+ True if the viewer is sponsored by this user/organization.
+ """
+ isSponsoringViewer: Boolean!
+
+ """
+ The GitHub Sponsors listing for this user or organization.
+ """
+ sponsorsListing: SponsorsListing
+
+ """
+ This object's sponsorships as the maintainer.
+ """
+ sponsorshipsAsMaintainer(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Whether or not to include private sponsorships in the result set
+ """
+ includePrivate: Boolean = false
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ This object's sponsorships as the sponsor.
+ """
+ sponsorshipsAsSponsor(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ Whether or not the viewer is able to sponsor this user/organization.
+ """
+ viewerCanSponsor: Boolean!
+
+ """
+ True if the viewer is sponsoring this user/organization.
+ """
+ viewerIsSponsoring: Boolean!
+}
+
+"""
+A GitHub Sponsors listing.
+"""
+type SponsorsListing implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The full description of the listing.
+ """
+ fullDescription: String!
+
+ """
+ The full description of the listing rendered to HTML.
+ """
+ fullDescriptionHTML: HTML!
+ id: ID!
+
+ """
+ The listing's full name.
+ """
+ name: String!
+
+ """
+ The short description of the listing.
+ """
+ shortDescription: String!
+
+ """
+ The short name of the listing.
+ """
+ slug: String!
+
+ """
+ The published tiers for this GitHub Sponsors listing.
+ """
+ tiers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for Sponsors tiers returned from the connection.
+ """
+ orderBy: SponsorsTierOrder = {field: MONTHLY_PRICE_IN_CENTS, direction: ASC}
+ ): SponsorsTierConnection
+}
+
+"""
+A GitHub Sponsors tier associated with a GitHub Sponsors listing.
+"""
+type SponsorsTier implements Node {
+ """
+ SponsorsTier information only visible to users that can administer the associated Sponsors listing.
+ """
+ adminInfo: SponsorsTierAdminInfo
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The description of the tier.
+ """
+ description: String!
+
+ """
+ The tier description rendered to HTML
+ """
+ descriptionHTML: HTML!
+ id: ID!
+
+ """
+ How much this tier costs per month in cents.
+ """
+ monthlyPriceInCents: Int!
+
+ """
+ How much this tier costs per month in dollars.
+ """
+ monthlyPriceInDollars: Int!
+
+ """
+ The name of the tier.
+ """
+ name: String!
+
+ """
+ The sponsors listing that this tier belongs to.
+ """
+ sponsorsListing: SponsorsListing!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+SponsorsTier information only visible to users that can administer the associated Sponsors listing.
+"""
+type SponsorsTierAdminInfo {
+ """
+ The sponsorships associated with this tier.
+ """
+ sponsorships(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Whether or not to include private sponsorships in the result set
+ """
+ includePrivate: Boolean = false
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+}
+
+"""
+The connection type for SponsorsTier.
+"""
+type SponsorsTierConnection {
+ """
+ A list of edges.
+ """
+ edges: [SponsorsTierEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [SponsorsTier]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SponsorsTierEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: SponsorsTier
+}
+
+"""
+Ordering options for Sponsors tiers connections.
+"""
+input SponsorsTierOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order tiers by.
+ """
+ field: SponsorsTierOrderField!
+}
+
+"""
+Properties by which Sponsors tiers connections can be ordered.
+"""
+enum SponsorsTierOrderField {
+ """
+ Order tiers by creation time.
+ """
+ CREATED_AT
+
+ """
+ Order tiers by their monthly price in cents
+ """
+ MONTHLY_PRICE_IN_CENTS
+}
+
+"""
+A sponsorship relationship between a sponsor and a maintainer
+"""
+type Sponsorship implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The entity that is being sponsored
+ """
+ maintainer: User! @deprecated(reason: "`Sponsorship.maintainer` will be removed. Use `Sponsorship.sponsorable` instead. Removal on 2020-04-01 UTC.")
+
+ """
+ The privacy level for this sponsorship.
+ """
+ privacyLevel: SponsorshipPrivacy!
+
+ """
+ The user that is sponsoring. Returns null if the sponsorship is private or if sponsor is not a user.
+ """
+ sponsor: User @deprecated(reason: "`Sponsorship.sponsor` will be removed. Use `Sponsorship.sponsorEntity` instead. Removal on 2020-10-01 UTC.")
+
+ """
+ The user or organization that is sponsoring, if you have permission to view them.
+ """
+ sponsorEntity: Sponsor
+
+ """
+ The entity that is being sponsored
+ """
+ sponsorable: Sponsorable!
+
+ """
+ The associated sponsorship tier
+ """
+ tier: SponsorsTier
+}
+
+"""
+The connection type for Sponsorship.
+"""
+type SponsorshipConnection {
+ """
+ A list of edges.
+ """
+ edges: [SponsorshipEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Sponsorship]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SponsorshipEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Sponsorship
+}
+
+"""
+Ordering options for sponsorship connections.
+"""
+input SponsorshipOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order sponsorship by.
+ """
+ field: SponsorshipOrderField!
+}
+
+"""
+Properties by which sponsorship connections can be ordered.
+"""
+enum SponsorshipOrderField {
+ """
+ Order sponsorship by creation time.
+ """
+ CREATED_AT
+}
+
+"""
+The privacy of a sponsorship
+"""
+enum SponsorshipPrivacy {
+ """
+ Private
+ """
+ PRIVATE
+
+ """
+ Public
+ """
+ PUBLIC
+}
+
+"""
+Ways in which star connections can be ordered.
+"""
+input StarOrder {
+ """
+ The direction in which to order nodes.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order nodes by.
+ """
+ field: StarOrderField!
+}
+
+"""
+Properties by which star connections can be ordered.
+"""
+enum StarOrderField {
+ """
+ Allows ordering a list of stars by when they were created.
+ """
+ STARRED_AT
+}
+
+"""
+The connection type for User.
+"""
+type StargazerConnection {
+ """
+ A list of edges.
+ """
+ edges: [StargazerEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a user that's starred a repository.
+"""
+type StargazerEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+ node: User!
+
+ """
+ Identifies when the item was starred.
+ """
+ starredAt: DateTime!
+}
+
+"""
+Things that can be starred.
+"""
+interface Starrable {
+ id: ID!
+
+ """
+ Returns a count of how many stargazers there are on this object
+ """
+ stargazerCount: Int!
+
+ """
+ A list of users who have starred this starrable.
+ """
+ stargazers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: StarOrder
+ ): StargazerConnection!
+
+ """
+ Returns a boolean indicating whether the viewing user has starred this starrable.
+ """
+ viewerHasStarred: Boolean!
+}
+
+"""
+The connection type for Repository.
+"""
+type StarredRepositoryConnection {
+ """
+ A list of edges.
+ """
+ edges: [StarredRepositoryEdge]
+
+ """
+ Is the list of stars for this user truncated? This is true for users that have many stars.
+ """
+ isOverLimit: Boolean!
+
+ """
+ A list of nodes.
+ """
+ nodes: [Repository]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a starred repository.
+"""
+type StarredRepositoryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+ node: Repository!
+
+ """
+ Identifies when the item was starred.
+ """
+ starredAt: DateTime!
+}
+
+"""
+Represents a commit status.
+"""
+type Status implements Node {
+ """
+ A list of status contexts and check runs for this commit.
+ """
+ combinedContexts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): StatusCheckRollupContextConnection!
+
+ """
+ The commit this status is attached to.
+ """
+ commit: Commit
+
+ """
+ Looks up an individual status context by context name.
+ """
+ context(
+ """
+ The context name.
+ """
+ name: String!
+ ): StatusContext
+
+ """
+ The individual status contexts for this commit.
+ """
+ contexts: [StatusContext!]!
+ id: ID!
+
+ """
+ The combined commit status.
+ """
+ state: StatusState!
+}
+
+"""
+Represents the rollup for both the check runs and status for a commit.
+"""
+type StatusCheckRollup implements Node {
+ """
+ The commit the status and check runs are attached to.
+ """
+ commit: Commit
+
+ """
+ A list of status contexts and check runs for this commit.
+ """
+ contexts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): StatusCheckRollupContextConnection!
+ id: ID!
+
+ """
+ The combined status for the commit.
+ """
+ state: StatusState!
+}
+
+"""
+Types that can be inside a StatusCheckRollup context.
+"""
+union StatusCheckRollupContext = CheckRun | StatusContext
+
+"""
+The connection type for StatusCheckRollupContext.
+"""
+type StatusCheckRollupContextConnection {
+ """
+ A list of edges.
+ """
+ edges: [StatusCheckRollupContextEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [StatusCheckRollupContext]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type StatusCheckRollupContextEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: StatusCheckRollupContext
+}
+
+"""
+Represents an individual commit status context
+"""
+type StatusContext implements Node {
+ """
+ The avatar of the OAuth application or the user that created the status
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int = 40
+ ): URI
+
+ """
+ This commit this status context is attached to.
+ """
+ commit: Commit
+
+ """
+ The name of this status context.
+ """
+ context: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The actor who created this status context.
+ """
+ creator: Actor
+
+ """
+ The description for this status context.
+ """
+ description: String
+ id: ID!
+
+ """
+ The state of this status context.
+ """
+ state: StatusState!
+
+ """
+ The URL for this status context.
+ """
+ targetUrl: URI
+}
+
+"""
+The possible commit status states.
+"""
+enum StatusState {
+ """
+ Status is errored.
+ """
+ ERROR
+
+ """
+ Status is expected.
+ """
+ EXPECTED
+
+ """
+ Status is failing.
+ """
+ FAILURE
+
+ """
+ Status is pending.
+ """
+ PENDING
+
+ """
+ Status is successful.
+ """
+ SUCCESS
+}
+
+"""
+Autogenerated input type of SubmitPullRequestReview
+"""
+input SubmitPullRequestReviewInput {
+ """
+ The text field to set on the Pull Request Review.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The event to send to the Pull Request Review.
+ """
+ event: PullRequestReviewEvent!
+
+ """
+ The Pull Request ID to submit any pending reviews.
+ """
+ pullRequestId: ID @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The Pull Request Review ID to submit.
+ """
+ pullRequestReviewId: ID @possibleTypes(concreteTypes: ["PullRequestReview"])
+}
+
+"""
+Autogenerated return type of SubmitPullRequestReview
+"""
+type SubmitPullRequestReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The submitted pull request review.
+ """
+ pullRequestReview: PullRequestReview
+}
+
+"""
+A pointer to a repository at a specific revision embedded inside another repository.
+"""
+type Submodule {
+ """
+ The branch of the upstream submodule for tracking updates
+ """
+ branch: String
+
+ """
+ The git URL of the submodule repository
+ """
+ gitUrl: URI!
+
+ """
+ The name of the submodule in .gitmodules
+ """
+ name: String!
+
+ """
+ The path in the superproject that this submodule is located in
+ """
+ path: String!
+
+ """
+ The commit revision of the subproject repository being tracked by the submodule
+ """
+ subprojectCommitOid: GitObjectID
+}
+
+"""
+The connection type for Submodule.
+"""
+type SubmoduleConnection {
+ """
+ A list of edges.
+ """
+ edges: [SubmoduleEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Submodule]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type SubmoduleEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Submodule
+}
+
+"""
+Entities that can be subscribed to for web and email notifications.
+"""
+interface Subscribable {
+ id: ID!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+}
+
+"""
+Represents a 'subscribed' event on a given `Subscribable`.
+"""
+type SubscribedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Object referenced by event.
+ """
+ subscribable: Subscribable!
+}
+
+"""
+The possible states of a subscription.
+"""
+enum SubscriptionState {
+ """
+ The User is never notified.
+ """
+ IGNORED
+
+ """
+ The User is notified of all conversations.
+ """
+ SUBSCRIBED
+
+ """
+ The User is only notified when participating or @mentioned.
+ """
+ UNSUBSCRIBED
+}
+
+"""
+A suggestion to review a pull request based on a user's commit history and review comments.
+"""
+type SuggestedReviewer {
+ """
+ Is this suggestion based on past commits?
+ """
+ isAuthor: Boolean!
+
+ """
+ Is this suggestion based on past review comments?
+ """
+ isCommenter: Boolean!
+
+ """
+ Identifies the user suggested to review the pull request.
+ """
+ reviewer: User!
+}
+
+"""
+Represents a Git tag.
+"""
+type Tag implements GitObject & Node {
+ """
+ An abbreviated version of the Git object ID
+ """
+ abbreviatedOid: String!
+
+ """
+ The HTTP path for this Git object
+ """
+ commitResourcePath: URI!
+
+ """
+ The HTTP URL for this Git object
+ """
+ commitUrl: URI!
+ id: ID!
+
+ """
+ The Git tag message.
+ """
+ message: String
+
+ """
+ The Git tag name.
+ """
+ name: String!
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID!
+
+ """
+ The Repository the Git object belongs to
+ """
+ repository: Repository!
+
+ """
+ Details about the tag author.
+ """
+ tagger: GitActor
+
+ """
+ The Git object the tag points to.
+ """
+ target: GitObject!
+}
+
+"""
+A team of users in an organization.
+"""
+type Team implements MemberStatusable & Node & Subscribable {
+ """
+ A list of teams that are ancestors of this team.
+ """
+ ancestors(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): TeamConnection!
+
+ """
+ A URL pointing to the team's avatar.
+ """
+ avatarUrl(
+ """
+ The size in pixels of the resulting square image.
+ """
+ size: Int = 400
+ ): URI
+
+ """
+ List of child teams belonging to this team
+ """
+ childTeams(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Whether to list immediate child teams or all descendant child teams.
+ """
+ immediateOnly: Boolean = true
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: TeamOrder
+
+ """
+ User logins to filter by
+ """
+ userLogins: [String!]
+ ): TeamConnection!
+
+ """
+ The slug corresponding to the organization and team.
+ """
+ combinedSlug: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The description of the team.
+ """
+ description: String
+
+ """
+ Find a team discussion by its number.
+ """
+ discussion(
+ """
+ The sequence number of the discussion to find.
+ """
+ number: Int!
+ ): TeamDiscussion
+
+ """
+ A list of team discussions.
+ """
+ discussions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If provided, filters discussions according to whether or not they are pinned.
+ """
+ isPinned: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: TeamDiscussionOrder
+ ): TeamDiscussionConnection!
+
+ """
+ The HTTP path for team discussions
+ """
+ discussionsResourcePath: URI!
+
+ """
+ The HTTP URL for team discussions
+ """
+ discussionsUrl: URI!
+
+ """
+ The HTTP path for editing this team
+ """
+ editTeamResourcePath: URI!
+
+ """
+ The HTTP URL for editing this team
+ """
+ editTeamUrl: URI!
+ id: ID!
+
+ """
+ A list of pending invitations for users to this team
+ """
+ invitations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): OrganizationInvitationConnection
+
+ """
+ Get the status messages members of this entity have set that are either public or visible only to the organization.
+ """
+ memberStatuses(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for user statuses returned from the connection.
+ """
+ orderBy: UserStatusOrder = {field: UPDATED_AT, direction: DESC}
+ ): UserStatusConnection!
+
+ """
+ A list of users who are members of this team.
+ """
+ members(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter by membership type
+ """
+ membership: TeamMembershipType = ALL
+
+ """
+ Order for the connection.
+ """
+ orderBy: TeamMemberOrder
+
+ """
+ The search string to look for.
+ """
+ query: String
+
+ """
+ Filter by team member role
+ """
+ role: TeamMemberRole
+ ): TeamMemberConnection!
+
+ """
+ The HTTP path for the team' members
+ """
+ membersResourcePath: URI!
+
+ """
+ The HTTP URL for the team' members
+ """
+ membersUrl: URI!
+
+ """
+ The name of the team.
+ """
+ name: String!
+
+ """
+ The HTTP path creating a new team
+ """
+ newTeamResourcePath: URI!
+
+ """
+ The HTTP URL creating a new team
+ """
+ newTeamUrl: URI!
+
+ """
+ The organization that owns this team.
+ """
+ organization: Organization!
+
+ """
+ The parent team of the team.
+ """
+ parentTeam: Team
+
+ """
+ The level of privacy the team has.
+ """
+ privacy: TeamPrivacy!
+
+ """
+ A list of repositories this team has access to.
+ """
+ repositories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for the connection.
+ """
+ orderBy: TeamRepositoryOrder
+
+ """
+ The search string to look for.
+ """
+ query: String
+ ): TeamRepositoryConnection!
+
+ """
+ The HTTP path for this team's repositories
+ """
+ repositoriesResourcePath: URI!
+
+ """
+ The HTTP URL for this team's repositories
+ """
+ repositoriesUrl: URI!
+
+ """
+ The HTTP path for this team
+ """
+ resourcePath: URI!
+
+ """
+ What algorithm is used for review assignment for this team
+ """
+ reviewRequestDelegationAlgorithm: TeamReviewAssignmentAlgorithm @preview(toggledBy: "stone-crop-preview")
+
+ """
+ True if review assignment is enabled for this team
+ """
+ reviewRequestDelegationEnabled: Boolean! @preview(toggledBy: "stone-crop-preview")
+
+ """
+ How many team members are required for review assignment for this team
+ """
+ reviewRequestDelegationMemberCount: Int @preview(toggledBy: "stone-crop-preview")
+
+ """
+ When assigning team members via delegation, whether the entire team should be notified as well.
+ """
+ reviewRequestDelegationNotifyTeam: Boolean! @preview(toggledBy: "stone-crop-preview")
+
+ """
+ The slug corresponding to the team.
+ """
+ slug: String!
+
+ """
+ The HTTP path for this team's teams
+ """
+ teamsResourcePath: URI!
+
+ """
+ The HTTP URL for this team's teams
+ """
+ teamsUrl: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this team
+ """
+ url: URI!
+
+ """
+ Team is adminable by the viewer.
+ """
+ viewerCanAdminister: Boolean!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+}
+
+"""
+Audit log entry for a team.add_member event.
+"""
+type TeamAddMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & TeamAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the team was mapped to an LDAP Group.
+ """
+ isLdapMapped: Boolean
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a team.add_repository event.
+"""
+type TeamAddRepositoryAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TeamAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the team was mapped to an LDAP Group.
+ """
+ isLdapMapped: Boolean
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Metadata for an audit entry with action team.*
+"""
+interface TeamAuditEntryData {
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+}
+
+"""
+Audit log entry for a team.change_parent_team event.
+"""
+type TeamChangeParentTeamAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & TeamAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the team was mapped to an LDAP Group.
+ """
+ isLdapMapped: Boolean
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The new parent team.
+ """
+ parentTeam: Team
+
+ """
+ The name of the new parent team
+ """
+ parentTeamName: String
+
+ """
+ The name of the former parent team
+ """
+ parentTeamNameWas: String
+
+ """
+ The HTTP path for the parent team
+ """
+ parentTeamResourcePath: URI
+
+ """
+ The HTTP URL for the parent team
+ """
+ parentTeamUrl: URI
+
+ """
+ The former parent team.
+ """
+ parentTeamWas: Team
+
+ """
+ The HTTP path for the previous parent team
+ """
+ parentTeamWasResourcePath: URI
+
+ """
+ The HTTP URL for the previous parent team
+ """
+ parentTeamWasUrl: URI
+
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The connection type for Team.
+"""
+type TeamConnection {
+ """
+ A list of edges.
+ """
+ edges: [TeamEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Team]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+A team discussion.
+"""
+type TeamDiscussion implements Comment & Deletable & Node & Reactable & Subscribable & UniformResourceLocatable & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the discussion's team.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ The body as Markdown.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ Identifies the discussion body hash.
+ """
+ bodyVersion: String!
+
+ """
+ A list of comments on this discussion.
+ """
+ comments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ When provided, filters the connection such that results begin with the comment with this number.
+ """
+ fromComment: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: TeamDiscussionCommentOrder
+ ): TeamDiscussionCommentConnection!
+
+ """
+ The HTTP path for discussion comments
+ """
+ commentsResourcePath: URI!
+
+ """
+ The HTTP URL for discussion comments
+ """
+ commentsUrl: URI!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ Whether or not the discussion is pinned.
+ """
+ isPinned: Boolean!
+
+ """
+ Whether or not the discussion is only visible to team members and org admins.
+ """
+ isPrivate: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Identifies the discussion within its team.
+ """
+ number: Int!
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The HTTP path for this discussion
+ """
+ resourcePath: URI!
+
+ """
+ The team that defines the context of this discussion.
+ """
+ team: Team!
+
+ """
+ The title of the discussion
+ """
+ title: String!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this discussion
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Whether or not the current viewer can pin this discussion.
+ """
+ viewerCanPin: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the viewer is able to change their subscription status for the repository.
+ """
+ viewerCanSubscribe: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+
+ """
+ Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+ """
+ viewerSubscription: SubscriptionState
+}
+
+"""
+A comment on a team discussion.
+"""
+type TeamDiscussionComment implements Comment & Deletable & Node & Reactable & UniformResourceLocatable & Updatable & UpdatableComment {
+ """
+ The actor who authored the comment.
+ """
+ author: Actor
+
+ """
+ Author's association with the comment's team.
+ """
+ authorAssociation: CommentAuthorAssociation!
+
+ """
+ The body as Markdown.
+ """
+ body: String!
+
+ """
+ The body rendered to HTML.
+ """
+ bodyHTML: HTML!
+
+ """
+ The body rendered to text.
+ """
+ bodyText: String!
+
+ """
+ The current version of the body content.
+ """
+ bodyVersion: String!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Check if this comment was created via an email reply.
+ """
+ createdViaEmail: Boolean!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The discussion this comment is about.
+ """
+ discussion: TeamDiscussion!
+
+ """
+ The actor who edited the comment.
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Check if this comment was edited and includes an edit with the creation data
+ """
+ includesCreatedEdit: Boolean!
+
+ """
+ The moment the editor made the last edit
+ """
+ lastEditedAt: DateTime
+
+ """
+ Identifies the comment number.
+ """
+ number: Int!
+
+ """
+ Identifies when the comment was published at.
+ """
+ publishedAt: DateTime
+
+ """
+ A list of reactions grouped by content left on the subject.
+ """
+ reactionGroups: [ReactionGroup!]
+
+ """
+ A list of Reactions left on the Issue.
+ """
+ reactions(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Allows filtering Reactions by emoji.
+ """
+ content: ReactionContent
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Allows specifying the order in which reactions are returned.
+ """
+ orderBy: ReactionOrder
+ ): ReactionConnection!
+
+ """
+ The HTTP path for this comment
+ """
+ resourcePath: URI!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this comment
+ """
+ url: URI!
+
+ """
+ A list of edits to this content.
+ """
+ userContentEdits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): UserContentEditConnection
+
+ """
+ Check if the current viewer can delete this object.
+ """
+ viewerCanDelete: Boolean!
+
+ """
+ Can user react to this subject
+ """
+ viewerCanReact: Boolean!
+
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+ """
+ Did the viewer author this comment.
+ """
+ viewerDidAuthor: Boolean!
+}
+
+"""
+The connection type for TeamDiscussionComment.
+"""
+type TeamDiscussionCommentConnection {
+ """
+ A list of edges.
+ """
+ edges: [TeamDiscussionCommentEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [TeamDiscussionComment]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type TeamDiscussionCommentEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: TeamDiscussionComment
+}
+
+"""
+Ways in which team discussion comment connections can be ordered.
+"""
+input TeamDiscussionCommentOrder {
+ """
+ The direction in which to order nodes.
+ """
+ direction: OrderDirection!
+
+ """
+ The field by which to order nodes.
+ """
+ field: TeamDiscussionCommentOrderField!
+}
+
+"""
+Properties by which team discussion comment connections can be ordered.
+"""
+enum TeamDiscussionCommentOrderField {
+ """
+ Allows sequential ordering of team discussion comments (which is equivalent to chronological ordering).
+ """
+ NUMBER
+}
+
+"""
+The connection type for TeamDiscussion.
+"""
+type TeamDiscussionConnection {
+ """
+ A list of edges.
+ """
+ edges: [TeamDiscussionEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [TeamDiscussion]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type TeamDiscussionEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: TeamDiscussion
+}
+
+"""
+Ways in which team discussion connections can be ordered.
+"""
+input TeamDiscussionOrder {
+ """
+ The direction in which to order nodes.
+ """
+ direction: OrderDirection!
+
+ """
+ The field by which to order nodes.
+ """
+ field: TeamDiscussionOrderField!
+}
+
+"""
+Properties by which team discussion connections can be ordered.
+"""
+enum TeamDiscussionOrderField {
+ """
+ Allows chronological ordering of team discussions.
+ """
+ CREATED_AT
+}
+
+"""
+An edge in a connection.
+"""
+type TeamEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Team
+}
+
+"""
+The connection type for User.
+"""
+type TeamMemberConnection {
+ """
+ A list of edges.
+ """
+ edges: [TeamMemberEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a user who is a member of a team.
+"""
+type TeamMemberEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The HTTP path to the organization's member access page.
+ """
+ memberAccessResourcePath: URI!
+
+ """
+ The HTTP URL to the organization's member access page.
+ """
+ memberAccessUrl: URI!
+ node: User!
+
+ """
+ The role the member has on the team.
+ """
+ role: TeamMemberRole!
+}
+
+"""
+Ordering options for team member connections
+"""
+input TeamMemberOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order team members by.
+ """
+ field: TeamMemberOrderField!
+}
+
+"""
+Properties by which team member connections can be ordered.
+"""
+enum TeamMemberOrderField {
+ """
+ Order team members by creation time
+ """
+ CREATED_AT
+
+ """
+ Order team members by login
+ """
+ LOGIN
+}
+
+"""
+The possible team member roles; either 'maintainer' or 'member'.
+"""
+enum TeamMemberRole {
+ """
+ A team maintainer has permission to add and remove team members.
+ """
+ MAINTAINER
+
+ """
+ A team member has no administrative permissions on the team.
+ """
+ MEMBER
+}
+
+"""
+Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL.
+"""
+enum TeamMembershipType {
+ """
+ Includes immediate and child team members for the team.
+ """
+ ALL
+
+ """
+ Includes only child team members for the team.
+ """
+ CHILD_TEAM
+
+ """
+ Includes only immediate members of the team.
+ """
+ IMMEDIATE
+}
+
+"""
+Ways in which team connections can be ordered.
+"""
+input TeamOrder {
+ """
+ The direction in which to order nodes.
+ """
+ direction: OrderDirection!
+
+ """
+ The field in which to order nodes by.
+ """
+ field: TeamOrderField!
+}
+
+"""
+Properties by which team connections can be ordered.
+"""
+enum TeamOrderField {
+ """
+ Allows ordering a list of teams by name.
+ """
+ NAME
+}
+
+"""
+The possible team privacy values.
+"""
+enum TeamPrivacy {
+ """
+ A secret team can only be seen by its members.
+ """
+ SECRET
+
+ """
+ A visible team can be seen and @mentioned by every member of the organization.
+ """
+ VISIBLE
+}
+
+"""
+Audit log entry for a team.remove_member event.
+"""
+type TeamRemoveMemberAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & TeamAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the team was mapped to an LDAP Group.
+ """
+ isLdapMapped: Boolean
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+Audit log entry for a team.remove_repository event.
+"""
+type TeamRemoveRepositoryAuditEntry implements AuditEntry & Node & OrganizationAuditEntryData & RepositoryAuditEntryData & TeamAuditEntryData {
+ """
+ The action name
+ """
+ action: String!
+
+ """
+ The user who initiated the action
+ """
+ actor: AuditEntryActor
+
+ """
+ The IP address of the actor
+ """
+ actorIp: String
+
+ """
+ A readable representation of the actor's location
+ """
+ actorLocation: ActorLocation
+
+ """
+ The username of the user who initiated the action
+ """
+ actorLogin: String
+
+ """
+ The HTTP path for the actor.
+ """
+ actorResourcePath: URI
+
+ """
+ The HTTP URL for the actor.
+ """
+ actorUrl: URI
+
+ """
+ The time the action was initiated
+ """
+ createdAt: PreciseDateTime!
+ id: ID!
+
+ """
+ Whether the team was mapped to an LDAP Group.
+ """
+ isLdapMapped: Boolean
+
+ """
+ The corresponding operation type for the action
+ """
+ operationType: OperationType
+
+ """
+ The Organization associated with the Audit Entry.
+ """
+ organization: Organization
+
+ """
+ The name of the Organization.
+ """
+ organizationName: String
+
+ """
+ The HTTP path for the organization
+ """
+ organizationResourcePath: URI
+
+ """
+ The HTTP URL for the organization
+ """
+ organizationUrl: URI
+
+ """
+ The repository associated with the action
+ """
+ repository: Repository
+
+ """
+ The name of the repository
+ """
+ repositoryName: String
+
+ """
+ The HTTP path for the repository
+ """
+ repositoryResourcePath: URI
+
+ """
+ The HTTP URL for the repository
+ """
+ repositoryUrl: URI
+
+ """
+ The team associated with the action
+ """
+ team: Team
+
+ """
+ The name of the team
+ """
+ teamName: String
+
+ """
+ The HTTP path for this team
+ """
+ teamResourcePath: URI
+
+ """
+ The HTTP URL for this team
+ """
+ teamUrl: URI
+
+ """
+ The user affected by the action
+ """
+ user: User
+
+ """
+ For actions involving two users, the actor is the initiator and the user is the affected user.
+ """
+ userLogin: String
+
+ """
+ The HTTP path for the user.
+ """
+ userResourcePath: URI
+
+ """
+ The HTTP URL for the user.
+ """
+ userUrl: URI
+}
+
+"""
+The connection type for Repository.
+"""
+type TeamRepositoryConnection {
+ """
+ A list of edges.
+ """
+ edges: [TeamRepositoryEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Repository]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+Represents a team repository.
+"""
+type TeamRepositoryEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+ node: Repository!
+
+ """
+ The permission level the team has on the repository
+ """
+ permission: RepositoryPermission!
+}
+
+"""
+Ordering options for team repository connections
+"""
+input TeamRepositoryOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order repositories by.
+ """
+ field: TeamRepositoryOrderField!
+}
+
+"""
+Properties by which team repository connections can be ordered.
+"""
+enum TeamRepositoryOrderField {
+ """
+ Order repositories by creation time
+ """
+ CREATED_AT
+
+ """
+ Order repositories by name
+ """
+ NAME
+
+ """
+ Order repositories by permission
+ """
+ PERMISSION
+
+ """
+ Order repositories by push time
+ """
+ PUSHED_AT
+
+ """
+ Order repositories by number of stargazers
+ """
+ STARGAZERS
+
+ """
+ Order repositories by update time
+ """
+ UPDATED_AT
+}
+
+"""
+The possible team review assignment algorithms
+"""
+enum TeamReviewAssignmentAlgorithm @preview(toggledBy: "stone-crop-preview") {
+ """
+ Balance review load across the entire team
+ """
+ LOAD_BALANCE
+
+ """
+ Alternate reviews between each team member
+ """
+ ROUND_ROBIN
+}
+
+"""
+The role of a user on a team.
+"""
+enum TeamRole {
+ """
+ User has admin rights on the team.
+ """
+ ADMIN
+
+ """
+ User is a member of the team.
+ """
+ MEMBER
+}
+
+"""
+A text match within a search result.
+"""
+type TextMatch {
+ """
+ The specific text fragment within the property matched on.
+ """
+ fragment: String!
+
+ """
+ Highlights within the matched fragment.
+ """
+ highlights: [TextMatchHighlight!]!
+
+ """
+ The property matched on.
+ """
+ property: String!
+}
+
+"""
+Represents a single highlight in a search result match.
+"""
+type TextMatchHighlight {
+ """
+ The indice in the fragment where the matched text begins.
+ """
+ beginIndice: Int!
+
+ """
+ The indice in the fragment where the matched text ends.
+ """
+ endIndice: Int!
+
+ """
+ The text matched.
+ """
+ text: String!
+}
+
+"""
+A topic aggregates entities that are related to a subject.
+"""
+type Topic implements Node & Starrable {
+ id: ID!
+
+ """
+ The topic's name.
+ """
+ name: String!
+
+ """
+ A list of related topics, including aliases of this topic, sorted with the most relevant
+ first. Returns up to 10 Topics.
+ """
+ relatedTopics(
+ """
+ How many topics to return.
+ """
+ first: Int = 3
+ ): [Topic!]!
+
+ """
+ Returns a count of how many stargazers there are on this object
+ """
+ stargazerCount: Int!
+
+ """
+ A list of users who have starred this starrable.
+ """
+ stargazers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: StarOrder
+ ): StargazerConnection!
+
+ """
+ Returns a boolean indicating whether the viewing user has starred this starrable.
+ """
+ viewerHasStarred: Boolean!
+}
+
+"""
+Metadata for an audit entry with a topic.
+"""
+interface TopicAuditEntryData {
+ """
+ The name of the topic added to the repository
+ """
+ topic: Topic
+
+ """
+ The name of the topic added to the repository
+ """
+ topicName: String
+}
+
+"""
+Reason that the suggested topic is declined.
+"""
+enum TopicSuggestionDeclineReason {
+ """
+ The suggested topic is not relevant to the repository.
+ """
+ NOT_RELEVANT
+
+ """
+ The viewer does not like the suggested topic.
+ """
+ PERSONAL_PREFERENCE
+
+ """
+ The suggested topic is too general for the repository.
+ """
+ TOO_GENERAL
+
+ """
+ The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1).
+ """
+ TOO_SPECIFIC
+}
+
+"""
+Autogenerated input type of TransferIssue
+"""
+input TransferIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the issue to be transferred
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+
+ """
+ The Node ID of the repository the issue should be transferred to
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of TransferIssue
+"""
+type TransferIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue that was transferred
+ """
+ issue: Issue
+}
+
+"""
+Represents a 'transferred' event on a given issue or pull request.
+"""
+type TransferredEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The repository this came from
+ """
+ fromRepository: Repository
+ id: ID!
+
+ """
+ Identifies the issue associated with the event.
+ """
+ issue: Issue!
+}
+
+"""
+Represents a Git tree.
+"""
+type Tree implements GitObject & Node {
+ """
+ An abbreviated version of the Git object ID
+ """
+ abbreviatedOid: String!
+
+ """
+ The HTTP path for this Git object
+ """
+ commitResourcePath: URI!
+
+ """
+ The HTTP URL for this Git object
+ """
+ commitUrl: URI!
+
+ """
+ A list of tree entries.
+ """
+ entries: [TreeEntry!]
+ id: ID!
+
+ """
+ The Git object ID
+ """
+ oid: GitObjectID!
+
+ """
+ The Repository the Git object belongs to
+ """
+ repository: Repository!
+}
+
+"""
+Represents a Git tree entry.
+"""
+type TreeEntry {
+ """
+ The extension of the file
+ """
+ extension: String
+
+ """
+ Whether or not this tree entry is generated
+ """
+ isGenerated: Boolean!
+
+ """
+ Entry file mode.
+ """
+ mode: Int!
+
+ """
+ Entry file name.
+ """
+ name: String!
+
+ """
+ Entry file object.
+ """
+ object: GitObject
+
+ """
+ Entry file Git object ID.
+ """
+ oid: GitObjectID!
+
+ """
+ The full path of the file.
+ """
+ path: String
+
+ """
+ The Repository the tree entry belongs to
+ """
+ repository: Repository!
+
+ """
+ If the TreeEntry is for a directory occupied by a submodule project, this returns the corresponding submodule
+ """
+ submodule: Submodule
+
+ """
+ Entry file type.
+ """
+ type: String!
+}
+
+"""
+An RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI string.
+"""
+scalar URI
+
+"""
+Autogenerated input type of UnarchiveRepository
+"""
+input UnarchiveRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the repository to unarchive.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of UnarchiveRepository
+"""
+type UnarchiveRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The repository that was unarchived.
+ """
+ repository: Repository
+}
+
+"""
+Represents an 'unassigned' event on any assignable object.
+"""
+type UnassignedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the assignable associated with the event.
+ """
+ assignable: Assignable!
+
+ """
+ Identifies the user or mannequin that was unassigned.
+ """
+ assignee: Assignee
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the subject (user) who was unassigned.
+ """
+ user: User @deprecated(reason: "Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC.")
+}
+
+"""
+Autogenerated input type of UnfollowUser
+"""
+input UnfollowUserInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the user to unfollow.
+ """
+ userId: ID! @possibleTypes(concreteTypes: ["User"])
+}
+
+"""
+Autogenerated return type of UnfollowUser
+"""
+type UnfollowUserPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The user that was unfollowed.
+ """
+ user: User
+}
+
+"""
+Represents a type that can be retrieved by a URL.
+"""
+interface UniformResourceLocatable {
+ """
+ The HTML path to this resource.
+ """
+ resourcePath: URI!
+
+ """
+ The URL to this resource.
+ """
+ url: URI!
+}
+
+"""
+Represents an unknown signature on a Commit or Tag.
+"""
+type UnknownSignature implements GitSignature {
+ """
+ Email used to sign this object.
+ """
+ email: String!
+
+ """
+ True if the signature is valid and verified by GitHub.
+ """
+ isValid: Boolean!
+
+ """
+ Payload for GPG signing object. Raw ODB object without the signature header.
+ """
+ payload: String!
+
+ """
+ ASCII-armored signature header from object.
+ """
+ signature: String!
+
+ """
+ GitHub user corresponding to the email signing this commit.
+ """
+ signer: User
+
+ """
+ The state of this signature. `VALID` if signature is valid and verified by
+ GitHub, otherwise represents reason why signature is considered invalid.
+ """
+ state: GitSignatureState!
+
+ """
+ True if the signature was made with GitHub's signing key.
+ """
+ wasSignedByGitHub: Boolean!
+}
+
+"""
+Represents an 'unlabeled' event on a given issue or pull request.
+"""
+type UnlabeledEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the label associated with the 'unlabeled' event.
+ """
+ label: Label!
+
+ """
+ Identifies the `Labelable` associated with the event.
+ """
+ labelable: Labelable!
+}
+
+"""
+Autogenerated input type of UnlinkRepositoryFromProject
+"""
+input UnlinkRepositoryFromProjectInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Project linked to the Repository.
+ """
+ projectId: ID! @possibleTypes(concreteTypes: ["Project"])
+
+ """
+ The ID of the Repository linked to the Project.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of UnlinkRepositoryFromProject
+"""
+type UnlinkRepositoryFromProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The linked Project.
+ """
+ project: Project
+
+ """
+ The linked Repository.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of UnlockLockable
+"""
+input UnlockLockableInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the item to be unlocked.
+ """
+ lockableId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "Lockable")
+}
+
+"""
+Autogenerated return type of UnlockLockable
+"""
+type UnlockLockablePayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The item that was unlocked.
+ """
+ unlockedRecord: Lockable
+}
+
+"""
+Represents an 'unlocked' event on a given issue or pull request.
+"""
+type UnlockedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Object that was unlocked.
+ """
+ lockable: Lockable!
+}
+
+"""
+Autogenerated input type of UnmarkFileAsViewed
+"""
+input UnmarkFileAsViewedInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The path of the file to mark as unviewed
+ """
+ path: String!
+
+ """
+ The Node ID of the pull request.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+}
+
+"""
+Autogenerated return type of UnmarkFileAsViewed
+"""
+type UnmarkFileAsViewedPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated pull request.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Autogenerated input type of UnmarkIssueAsDuplicate
+"""
+input UnmarkIssueAsDuplicateInput {
+ """
+ ID of the issue or pull request currently considered canonical/authoritative/original.
+ """
+ canonicalId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "IssueOrPullRequest")
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the issue or pull request currently marked as a duplicate.
+ """
+ duplicateId: ID! @possibleTypes(concreteTypes: ["Issue", "PullRequest"], abstractType: "IssueOrPullRequest")
+}
+
+"""
+Autogenerated return type of UnmarkIssueAsDuplicate
+"""
+type UnmarkIssueAsDuplicatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue or pull request that was marked as a duplicate.
+ """
+ duplicate: IssueOrPullRequest
+}
+
+"""
+Represents an 'unmarked_as_duplicate' event on a given issue or pull request.
+"""
+type UnmarkedAsDuplicateEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ The authoritative issue or pull request which has been duplicated by another.
+ """
+ canonical: IssueOrPullRequest
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ The issue or pull request which has been marked as a duplicate of another.
+ """
+ duplicate: IssueOrPullRequest
+ id: ID!
+
+ """
+ Canonical and duplicate belong to different repositories.
+ """
+ isCrossRepository: Boolean!
+}
+
+"""
+Autogenerated input type of UnminimizeComment
+"""
+input UnminimizeCommentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the subject to modify.
+ """
+ subjectId: ID! @possibleTypes(concreteTypes: ["CommitComment", "GistComment", "IssueComment", "PullRequestReviewComment"], abstractType: "Minimizable")
+}
+
+"""
+Autogenerated return type of UnminimizeComment
+"""
+type UnminimizeCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The comment that was unminimized.
+ """
+ unminimizedComment: Minimizable
+}
+
+"""
+Autogenerated input type of UnpinIssue
+"""
+input UnpinIssueInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the issue to be unpinned
+ """
+ issueId: ID! @possibleTypes(concreteTypes: ["Issue"])
+}
+
+"""
+Autogenerated return type of UnpinIssue
+"""
+type UnpinIssuePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue that was unpinned
+ """
+ issue: Issue
+}
+
+"""
+Represents an 'unpinned' event on a given issue or pull request.
+"""
+type UnpinnedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Identifies the issue associated with the event.
+ """
+ issue: Issue!
+}
+
+"""
+Autogenerated input type of UnresolveReviewThread
+"""
+input UnresolveReviewThreadInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the thread to unresolve
+ """
+ threadId: ID! @possibleTypes(concreteTypes: ["PullRequestReviewThread"])
+}
+
+"""
+Autogenerated return type of UnresolveReviewThread
+"""
+type UnresolveReviewThreadPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The thread to resolve.
+ """
+ thread: PullRequestReviewThread
+}
+
+"""
+Represents an 'unsubscribed' event on a given `Subscribable`.
+"""
+type UnsubscribedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ Object referenced by event.
+ """
+ subscribable: Subscribable!
+}
+
+"""
+Entities that can be updated.
+"""
+interface Updatable {
+ """
+ Check if the current viewer can update this object.
+ """
+ viewerCanUpdate: Boolean!
+}
+
+"""
+Comments that can be updated.
+"""
+interface UpdatableComment {
+ """
+ Reasons why the current viewer can not update this comment.
+ """
+ viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+}
+
+"""
+Autogenerated input type of UpdateBranchProtectionRule
+"""
+input UpdateBranchProtectionRuleInput {
+ """
+ Can this branch be deleted.
+ """
+ allowsDeletions: Boolean
+
+ """
+ Are force pushes allowed on this branch.
+ """
+ allowsForcePushes: Boolean
+
+ """
+ The global relay id of the branch protection rule to be updated.
+ """
+ branchProtectionRuleId: ID! @possibleTypes(concreteTypes: ["BranchProtectionRule"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Will new commits pushed to matching branches dismiss pull request review approvals.
+ """
+ dismissesStaleReviews: Boolean
+
+ """
+ Can admins overwrite branch protection.
+ """
+ isAdminEnforced: Boolean
+
+ """
+ The glob-like pattern used to determine matching branches.
+ """
+ pattern: String
+
+ """
+ A list of User, Team or App IDs allowed to push to matching branches.
+ """
+ pushActorIds: [ID!]
+
+ """
+ Number of approving reviews required to update matching branches.
+ """
+ requiredApprovingReviewCount: Int
+
+ """
+ List of required status check contexts that must pass for commits to be accepted to matching branches.
+ """
+ requiredStatusCheckContexts: [String!]
+
+ """
+ Are approving reviews required to update matching branches.
+ """
+ requiresApprovingReviews: Boolean
+
+ """
+ Are reviews from code owners required to update matching branches.
+ """
+ requiresCodeOwnerReviews: Boolean
+
+ """
+ Are commits required to be signed.
+ """
+ requiresCommitSignatures: Boolean
+
+ """
+ Are merge commits prohibited from being pushed to this branch.
+ """
+ requiresLinearHistory: Boolean
+
+ """
+ Are status checks required to update matching branches.
+ """
+ requiresStatusChecks: Boolean
+
+ """
+ Are branches required to be up to date before merging.
+ """
+ requiresStrictStatusChecks: Boolean
+
+ """
+ Is pushing to matching branches restricted.
+ """
+ restrictsPushes: Boolean
+
+ """
+ Is dismissal of pull request reviews restricted.
+ """
+ restrictsReviewDismissals: Boolean
+
+ """
+ A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.
+ """
+ reviewDismissalActorIds: [ID!]
+}
+
+"""
+Autogenerated return type of UpdateBranchProtectionRule
+"""
+type UpdateBranchProtectionRulePayload {
+ """
+ The newly created BranchProtectionRule.
+ """
+ branchProtectionRule: BranchProtectionRule
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of UpdateCheckRun
+"""
+input UpdateCheckRunInput {
+ """
+ Possible further actions the integrator can perform, which a user may trigger.
+ """
+ actions: [CheckRunAction!]
+
+ """
+ The node of the check.
+ """
+ checkRunId: ID! @possibleTypes(concreteTypes: ["CheckRun"])
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The time that the check run finished.
+ """
+ completedAt: DateTime
+
+ """
+ The final conclusion of the check.
+ """
+ conclusion: CheckConclusionState
+
+ """
+ The URL of the integrator's site that has the full details of the check.
+ """
+ detailsUrl: URI
+
+ """
+ A reference for the run on the integrator's system.
+ """
+ externalId: String
+
+ """
+ The name of the check.
+ """
+ name: String
+
+ """
+ Descriptive details about the run.
+ """
+ output: CheckRunOutput
+
+ """
+ The node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ The time that the check run began.
+ """
+ startedAt: DateTime
+
+ """
+ The current status.
+ """
+ status: RequestableCheckStatusState
+}
+
+"""
+Autogenerated return type of UpdateCheckRun
+"""
+type UpdateCheckRunPayload {
+ """
+ The updated check run.
+ """
+ checkRun: CheckRun
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of UpdateCheckSuitePreferences
+"""
+input UpdateCheckSuitePreferencesInput {
+ """
+ The check suite preferences to modify.
+ """
+ autoTriggerPreferences: [CheckSuiteAutoTriggerPreference!]!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of UpdateCheckSuitePreferences
+"""
+type UpdateCheckSuitePreferencesPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated repository.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseAdministratorRole
+"""
+input UpdateEnterpriseAdministratorRoleInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Enterprise which the admin belongs to.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The login of a administrator whose role is being changed.
+ """
+ login: String!
+
+ """
+ The new role for the Enterprise administrator.
+ """
+ role: EnterpriseAdministratorRole!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseAdministratorRole
+"""
+type UpdateEnterpriseAdministratorRolePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A message confirming the result of changing the administrator's role.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting
+"""
+input UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the allow private repository forking setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the allow private repository forking setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting
+"""
+type UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated allow private repository forking setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the allow private repository forking setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseDefaultRepositoryPermissionSetting
+"""
+input UpdateEnterpriseDefaultRepositoryPermissionSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the default repository permission setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the default repository permission setting on the enterprise.
+ """
+ settingValue: EnterpriseDefaultRepositoryPermissionSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseDefaultRepositoryPermissionSetting
+"""
+type UpdateEnterpriseDefaultRepositoryPermissionSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated default repository permission setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the default repository permission setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting
+"""
+input UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can change repository visibility setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can change repository visibility setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting
+"""
+type UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can change repository visibility setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can change repository visibility setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanCreateRepositoriesSetting
+"""
+input UpdateEnterpriseMembersCanCreateRepositoriesSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can create repositories setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ Allow members to create internal repositories. Defaults to current value.
+ """
+ membersCanCreateInternalRepositories: Boolean
+
+ """
+ Allow members to create private repositories. Defaults to current value.
+ """
+ membersCanCreatePrivateRepositories: Boolean
+
+ """
+ Allow members to create public repositories. Defaults to current value.
+ """
+ membersCanCreatePublicRepositories: Boolean
+
+ """
+ When false, allow member organizations to set their own repository creation member privileges.
+ """
+ membersCanCreateRepositoriesPolicyEnabled: Boolean
+
+ """
+ Value for the members can create repositories setting on the enterprise. This
+ or the granular public/private/internal allowed fields (but not both) must be provided.
+ """
+ settingValue: EnterpriseMembersCanCreateRepositoriesSettingValue
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanCreateRepositoriesSetting
+"""
+type UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can create repositories setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can create repositories setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanDeleteIssuesSetting
+"""
+input UpdateEnterpriseMembersCanDeleteIssuesSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can delete issues setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can delete issues setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanDeleteIssuesSetting
+"""
+type UpdateEnterpriseMembersCanDeleteIssuesSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can delete issues setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can delete issues setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting
+"""
+input UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can delete repositories setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can delete repositories setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting
+"""
+type UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can delete repositories setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can delete repositories setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting
+"""
+input UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can invite collaborators setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can invite collaborators setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting
+"""
+type UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can invite collaborators setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can invite collaborators setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanMakePurchasesSetting
+"""
+input UpdateEnterpriseMembersCanMakePurchasesSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can make purchases setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can make purchases setting on the enterprise.
+ """
+ settingValue: EnterpriseMembersCanMakePurchasesSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanMakePurchasesSetting
+"""
+type UpdateEnterpriseMembersCanMakePurchasesSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can make purchases setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can make purchases setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting
+"""
+input UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can update protected branches setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can update protected branches setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting
+"""
+type UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can update protected branches setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can update protected branches setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting
+"""
+input UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the members can view dependency insights setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the members can view dependency insights setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting
+"""
+type UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated members can view dependency insights setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the members can view dependency insights setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseOrganizationProjectsSetting
+"""
+input UpdateEnterpriseOrganizationProjectsSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the organization projects setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the organization projects setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseOrganizationProjectsSetting
+"""
+type UpdateEnterpriseOrganizationProjectsSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated organization projects setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the organization projects setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseProfile
+"""
+input UpdateEnterpriseProfileInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The description of the enterprise.
+ """
+ description: String
+
+ """
+ The Enterprise ID to update.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The location of the enterprise.
+ """
+ location: String
+
+ """
+ The name of the enterprise.
+ """
+ name: String
+
+ """
+ The URL of the enterprise's website.
+ """
+ websiteUrl: String
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseProfile
+"""
+type UpdateEnterpriseProfilePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated enterprise.
+ """
+ enterprise: Enterprise
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseRepositoryProjectsSetting
+"""
+input UpdateEnterpriseRepositoryProjectsSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the repository projects setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the repository projects setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseRepositoryProjectsSetting
+"""
+type UpdateEnterpriseRepositoryProjectsSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated repository projects setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the repository projects setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseTeamDiscussionsSetting
+"""
+input UpdateEnterpriseTeamDiscussionsSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the team discussions setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the team discussions setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledDisabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseTeamDiscussionsSetting
+"""
+type UpdateEnterpriseTeamDiscussionsSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated team discussions setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the team discussions setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting
+"""
+input UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the enterprise on which to set the two factor authentication required setting.
+ """
+ enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"])
+
+ """
+ The value for the two factor authentication required setting on the enterprise.
+ """
+ settingValue: EnterpriseEnabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting
+"""
+type UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The enterprise with the updated two factor authentication required setting.
+ """
+ enterprise: Enterprise
+
+ """
+ A message confirming the result of updating the two factor authentication required setting.
+ """
+ message: String
+}
+
+"""
+Autogenerated input type of UpdateIpAllowListEnabledSetting
+"""
+input UpdateIpAllowListEnabledSettingInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the owner on which to set the IP allow list enabled setting.
+ """
+ ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner")
+
+ """
+ The value for the IP allow list enabled setting.
+ """
+ settingValue: IpAllowListEnabledSettingValue!
+}
+
+"""
+Autogenerated return type of UpdateIpAllowListEnabledSetting
+"""
+type UpdateIpAllowListEnabledSettingPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The IP allow list owner on which the setting was updated.
+ """
+ owner: IpAllowListOwner
+}
+
+"""
+Autogenerated input type of UpdateIpAllowListEntry
+"""
+input UpdateIpAllowListEntryInput {
+ """
+ An IP address or range of addresses in CIDR notation.
+ """
+ allowListValue: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the IP allow list entry to update.
+ """
+ ipAllowListEntryId: ID! @possibleTypes(concreteTypes: ["IpAllowListEntry"])
+
+ """
+ Whether the IP allow list entry is active when an IP allow list is enabled.
+ """
+ isActive: Boolean!
+
+ """
+ An optional name for the IP allow list entry.
+ """
+ name: String
+}
+
+"""
+Autogenerated return type of UpdateIpAllowListEntry
+"""
+type UpdateIpAllowListEntryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The IP allow list entry that was updated.
+ """
+ ipAllowListEntry: IpAllowListEntry
+}
+
+"""
+Autogenerated input type of UpdateIssueComment
+"""
+input UpdateIssueCommentInput {
+ """
+ The updated text of the comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the IssueComment to modify.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["IssueComment"])
+}
+
+"""
+Autogenerated return type of UpdateIssueComment
+"""
+type UpdateIssueCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated comment.
+ """
+ issueComment: IssueComment
+}
+
+"""
+Autogenerated input type of UpdateIssue
+"""
+input UpdateIssueInput {
+ """
+ An array of Node IDs of users for this issue.
+ """
+ assigneeIds: [ID!] @possibleTypes(concreteTypes: ["User"])
+
+ """
+ The body for the issue description.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the Issue to modify.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["Issue"])
+
+ """
+ An array of Node IDs of labels for this issue.
+ """
+ labelIds: [ID!] @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ The Node ID of the milestone for this issue.
+ """
+ milestoneId: ID @possibleTypes(concreteTypes: ["Milestone"])
+
+ """
+ An array of Node IDs for projects associated with this issue.
+ """
+ projectIds: [ID!]
+
+ """
+ The desired issue state.
+ """
+ state: IssueState
+
+ """
+ The title for the issue.
+ """
+ title: String
+}
+
+"""
+Autogenerated return type of UpdateIssue
+"""
+type UpdateIssuePayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The issue.
+ """
+ issue: Issue
+}
+
+"""
+Autogenerated input type of UpdateLabel
+"""
+input UpdateLabelInput @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A 6 character hex code, without the leading #, identifying the updated color of the label.
+ """
+ color: String
+
+ """
+ A brief description of the label, such as its purpose.
+ """
+ description: String
+
+ """
+ The Node ID of the label to be updated.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ The updated name of the label.
+ """
+ name: String
+}
+
+"""
+Autogenerated return type of UpdateLabel
+"""
+type UpdateLabelPayload @preview(toggledBy: "bane-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated label.
+ """
+ label: Label
+}
+
+"""
+Autogenerated input type of UpdateProjectCard
+"""
+input UpdateProjectCardInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Whether or not the ProjectCard should be archived
+ """
+ isArchived: Boolean
+
+ """
+ The note of ProjectCard.
+ """
+ note: String
+
+ """
+ The ProjectCard ID to update.
+ """
+ projectCardId: ID! @possibleTypes(concreteTypes: ["ProjectCard"])
+}
+
+"""
+Autogenerated return type of UpdateProjectCard
+"""
+type UpdateProjectCardPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated ProjectCard.
+ """
+ projectCard: ProjectCard
+}
+
+"""
+Autogenerated input type of UpdateProjectColumn
+"""
+input UpdateProjectColumnInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of project column.
+ """
+ name: String!
+
+ """
+ The ProjectColumn ID to update.
+ """
+ projectColumnId: ID! @possibleTypes(concreteTypes: ["ProjectColumn"])
+}
+
+"""
+Autogenerated return type of UpdateProjectColumn
+"""
+type UpdateProjectColumnPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated project column.
+ """
+ projectColumn: ProjectColumn
+}
+
+"""
+Autogenerated input type of UpdateProject
+"""
+input UpdateProjectInput {
+ """
+ The description of project.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The name of project.
+ """
+ name: String
+
+ """
+ The Project ID to update.
+ """
+ projectId: ID! @possibleTypes(concreteTypes: ["Project"])
+
+ """
+ Whether the project is public or not.
+ """
+ public: Boolean
+
+ """
+ Whether the project is open or closed.
+ """
+ state: ProjectState
+}
+
+"""
+Autogenerated return type of UpdateProject
+"""
+type UpdateProjectPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated project.
+ """
+ project: Project
+}
+
+"""
+Autogenerated input type of UpdatePullRequest
+"""
+input UpdatePullRequestInput {
+ """
+ An array of Node IDs of users for this pull request.
+ """
+ assigneeIds: [ID!] @possibleTypes(concreteTypes: ["User"])
+
+ """
+ The name of the branch you want your changes pulled into. This should be an existing branch
+ on the current repository.
+ """
+ baseRefName: String
+
+ """
+ The contents of the pull request.
+ """
+ body: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ An array of Node IDs of labels for this pull request.
+ """
+ labelIds: [ID!] @possibleTypes(concreteTypes: ["Label"])
+
+ """
+ Indicates whether maintainers can modify the pull request.
+ """
+ maintainerCanModify: Boolean
+
+ """
+ The Node ID of the milestone for this pull request.
+ """
+ milestoneId: ID @possibleTypes(concreteTypes: ["Milestone"])
+
+ """
+ An array of Node IDs for projects associated with this pull request.
+ """
+ projectIds: [ID!]
+
+ """
+ The Node ID of the pull request.
+ """
+ pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"])
+
+ """
+ The target state of the pull request.
+ """
+ state: PullRequestUpdateState
+
+ """
+ The title of the pull request.
+ """
+ title: String
+}
+
+"""
+Autogenerated return type of UpdatePullRequest
+"""
+type UpdatePullRequestPayload {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated pull request.
+ """
+ pullRequest: PullRequest
+}
+
+"""
+Autogenerated input type of UpdatePullRequestReviewComment
+"""
+input UpdatePullRequestReviewCommentInput {
+ """
+ The text of the comment.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the comment to modify.
+ """
+ pullRequestReviewCommentId: ID! @possibleTypes(concreteTypes: ["PullRequestReviewComment"])
+}
+
+"""
+Autogenerated return type of UpdatePullRequestReviewComment
+"""
+type UpdatePullRequestReviewCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated comment.
+ """
+ pullRequestReviewComment: PullRequestReviewComment
+}
+
+"""
+Autogenerated input type of UpdatePullRequestReview
+"""
+input UpdatePullRequestReviewInput {
+ """
+ The contents of the pull request review body.
+ """
+ body: String!
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the pull request review to modify.
+ """
+ pullRequestReviewId: ID! @possibleTypes(concreteTypes: ["PullRequestReview"])
+}
+
+"""
+Autogenerated return type of UpdatePullRequestReview
+"""
+type UpdatePullRequestReviewPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated pull request review.
+ """
+ pullRequestReview: PullRequestReview
+}
+
+"""
+Autogenerated input type of UpdateRef
+"""
+input UpdateRefInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Permit updates of branch Refs that are not fast-forwards?
+ """
+ force: Boolean = false
+
+ """
+ The GitObjectID that the Ref shall be updated to target.
+ """
+ oid: GitObjectID!
+
+ """
+ The Node ID of the Ref to be updated.
+ """
+ refId: ID! @possibleTypes(concreteTypes: ["Ref"])
+}
+
+"""
+Autogenerated return type of UpdateRef
+"""
+type UpdateRefPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated Ref.
+ """
+ ref: Ref
+}
+
+"""
+Autogenerated input type of UpdateRefs
+"""
+input UpdateRefsInput @preview(toggledBy: "update-refs-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A list of ref updates.
+ """
+ refUpdates: [RefUpdate!]!
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+}
+
+"""
+Autogenerated return type of UpdateRefs
+"""
+type UpdateRefsPayload @preview(toggledBy: "update-refs-preview") {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+}
+
+"""
+Autogenerated input type of UpdateRepository
+"""
+input UpdateRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ A new description for the repository. Pass an empty string to erase the existing description.
+ """
+ description: String
+
+ """
+ Indicates if the repository should have the issues feature enabled.
+ """
+ hasIssuesEnabled: Boolean
+
+ """
+ Indicates if the repository should have the project boards feature enabled.
+ """
+ hasProjectsEnabled: Boolean
+
+ """
+ Indicates if the repository should have the wiki feature enabled.
+ """
+ hasWikiEnabled: Boolean
+
+ """
+ The URL for a web page about this repository. Pass an empty string to erase the existing URL.
+ """
+ homepageUrl: URI
+
+ """
+ The new name of the repository.
+ """
+ name: String
+
+ """
+ The ID of the repository to update.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ Whether this repository should be marked as a template such that anyone who
+ can access it can create new repositories with the same files and directory structure.
+ """
+ template: Boolean
+}
+
+"""
+Autogenerated return type of UpdateRepository
+"""
+type UpdateRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated repository.
+ """
+ repository: Repository
+}
+
+"""
+Autogenerated input type of UpdateSubscription
+"""
+input UpdateSubscriptionInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The new state of the subscription.
+ """
+ state: SubscriptionState!
+
+ """
+ The Node ID of the subscribable object to modify.
+ """
+ subscribableId: ID! @possibleTypes(concreteTypes: ["Commit", "Issue", "PullRequest", "Repository", "Team", "TeamDiscussion"], abstractType: "Subscribable")
+}
+
+"""
+Autogenerated return type of UpdateSubscription
+"""
+type UpdateSubscriptionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The input subscribable entity.
+ """
+ subscribable: Subscribable
+}
+
+"""
+Autogenerated input type of UpdateTeamDiscussionComment
+"""
+input UpdateTeamDiscussionCommentInput {
+ """
+ The updated text of the comment.
+ """
+ body: String!
+
+ """
+ The current version of the body content.
+ """
+ bodyVersion: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the comment to modify.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["TeamDiscussionComment"])
+}
+
+"""
+Autogenerated return type of UpdateTeamDiscussionComment
+"""
+type UpdateTeamDiscussionCommentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated comment.
+ """
+ teamDiscussionComment: TeamDiscussionComment
+}
+
+"""
+Autogenerated input type of UpdateTeamDiscussion
+"""
+input UpdateTeamDiscussionInput {
+ """
+ The updated text of the discussion.
+ """
+ body: String
+
+ """
+ The current version of the body content. If provided, this update operation
+ will be rejected if the given version does not match the latest version on the server.
+ """
+ bodyVersion: String
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the discussion to modify.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["TeamDiscussion"])
+
+ """
+ If provided, sets the pinned state of the updated discussion.
+ """
+ pinned: Boolean
+
+ """
+ The updated title of the discussion.
+ """
+ title: String
+}
+
+"""
+Autogenerated return type of UpdateTeamDiscussion
+"""
+type UpdateTeamDiscussionPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The updated discussion.
+ """
+ teamDiscussion: TeamDiscussion
+}
+
+"""
+Autogenerated input type of UpdateTeamReviewAssignment
+"""
+input UpdateTeamReviewAssignmentInput @preview(toggledBy: "stone-crop-preview") {
+ """
+ The algorithm to use for review assignment
+ """
+ algorithm: TeamReviewAssignmentAlgorithm = ROUND_ROBIN
+
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Turn on or off review assignment
+ """
+ enabled: Boolean!
+
+ """
+ An array of team member IDs to exclude
+ """
+ excludedTeamMemberIds: [ID!] @possibleTypes(concreteTypes: ["User"])
+
+ """
+ The Node ID of the team to update review assignments of
+ """
+ id: ID! @possibleTypes(concreteTypes: ["Team"])
+
+ """
+ Notify the entire team of the PR if it is delegated
+ """
+ notifyTeam: Boolean = true
+
+ """
+ The number of team members to assign
+ """
+ teamMemberCount: Int = 1
+}
+
+"""
+Autogenerated return type of UpdateTeamReviewAssignment
+"""
+type UpdateTeamReviewAssignmentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The team that was modified
+ """
+ team: Team
+}
+
+"""
+Autogenerated input type of UpdateTopics
+"""
+input UpdateTopicsInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The Node ID of the repository.
+ """
+ repositoryId: ID! @possibleTypes(concreteTypes: ["Repository"])
+
+ """
+ An array of topic names.
+ """
+ topicNames: [String!]!
+}
+
+"""
+Autogenerated return type of UpdateTopics
+"""
+type UpdateTopicsPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Names of the provided topics that are not valid.
+ """
+ invalidTopicNames: [String!]
+
+ """
+ The updated repository.
+ """
+ repository: Repository
+}
+
+"""
+A user is an individual's account on GitHub that owns repositories and can make new content.
+"""
+type User implements Actor & Node & PackageOwner & ProfileOwner & ProjectOwner & RepositoryOwner & Sponsorable & UniformResourceLocatable {
+ """
+ Determine if this repository owner has any items that can be pinned to their profile.
+ """
+ anyPinnableItems(
+ """
+ Filter to only a particular kind of pinnable item.
+ """
+ type: PinnableItemType
+ ): Boolean!
+
+ """
+ A URL pointing to the user's public avatar.
+ """
+ avatarUrl(
+ """
+ The size of the resulting square image.
+ """
+ size: Int
+ ): URI!
+
+ """
+ The user's public profile bio.
+ """
+ bio: String
+
+ """
+ The user's public profile bio as HTML.
+ """
+ bioHTML: HTML!
+
+ """
+ A list of commit comments made by this user.
+ """
+ commitComments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitCommentConnection!
+
+ """
+ The user's public profile company.
+ """
+ company: String
+
+ """
+ The user's public profile company as HTML.
+ """
+ companyHTML: HTML!
+
+ """
+ The collection of contributions this user has made to different repositories.
+ """
+ contributionsCollection(
+ """
+ Only contributions made at this time or later will be counted. If omitted, defaults to a year ago.
+ """
+ from: DateTime
+
+ """
+ The ID of the organization used to filter contributions.
+ """
+ organizationID: ID
+
+ """
+ Only contributions made before and up to and including this time will be
+ counted. If omitted, defaults to the current time.
+ """
+ to: DateTime
+ ): ContributionsCollection!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The user's publicly visible profile email.
+ """
+ email: String!
+
+ """
+ A list of users the given user is followed by.
+ """
+ followers(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): FollowerConnection!
+
+ """
+ A list of users the given user is following.
+ """
+ following(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): FollowingConnection!
+
+ """
+ Find gist by repo name.
+ """
+ gist(
+ """
+ The gist name to find.
+ """
+ name: String!
+ ): Gist
+
+ """
+ A list of gist comments made by this user.
+ """
+ gistComments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): GistCommentConnection!
+
+ """
+ A list of the Gists the user has created.
+ """
+ gists(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for gists returned from the connection
+ """
+ orderBy: GistOrder
+
+ """
+ Filters Gists according to privacy.
+ """
+ privacy: GistPrivacy
+ ): GistConnection!
+
+ """
+ True if this user/organization has a GitHub Sponsors listing.
+ """
+ hasSponsorsListing: Boolean!
+
+ """
+ The hovercard information for this user in a given context
+ """
+ hovercard(
+ """
+ The ID of the subject to get the hovercard in the context of
+ """
+ primarySubjectId: ID
+ ): Hovercard!
+ id: ID!
+
+ """
+ The interaction ability settings for this user.
+ """
+ interactionAbility: RepositoryInteractionAbility
+
+ """
+ Whether or not this user is a participant in the GitHub Security Bug Bounty.
+ """
+ isBountyHunter: Boolean!
+
+ """
+ Whether or not this user is a participant in the GitHub Campus Experts Program.
+ """
+ isCampusExpert: Boolean!
+
+ """
+ Whether or not this user is a GitHub Developer Program member.
+ """
+ isDeveloperProgramMember: Boolean!
+
+ """
+ Whether or not this user is a GitHub employee.
+ """
+ isEmployee: Boolean!
+
+ """
+ Whether or not the user has marked themselves as for hire.
+ """
+ isHireable: Boolean!
+
+ """
+ Whether or not this user is a site administrator.
+ """
+ isSiteAdmin: Boolean!
+
+ """
+ True if the viewer is sponsored by this user/organization.
+ """
+ isSponsoringViewer: Boolean!
+
+ """
+ Whether or not this user is the viewing user.
+ """
+ isViewer: Boolean!
+
+ """
+ A list of issue comments made by this user.
+ """
+ issueComments(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issue comments returned from the connection.
+ """
+ orderBy: IssueCommentOrder
+ ): IssueCommentConnection!
+
+ """
+ A list of issues associated with this user.
+ """
+ issues(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Filtering options for issues returned from the connection.
+ """
+ filterBy: IssueFilters
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for issues returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the issues by.
+ """
+ states: [IssueState!]
+ ): IssueConnection!
+
+ """
+ Showcases a selection of repositories and gists that the profile owner has
+ either curated or that have been selected automatically based on popularity.
+ """
+ itemShowcase: ProfileItemShowcase!
+
+ """
+ The user's public profile location.
+ """
+ location: String
+
+ """
+ The username used to login.
+ """
+ login: String!
+
+ """
+ The user's public profile name.
+ """
+ name: String
+
+ """
+ Find an organization by its login that the user belongs to.
+ """
+ organization(
+ """
+ The login of the organization to find.
+ """
+ login: String!
+ ): Organization
+
+ """
+ Verified email addresses that match verified domains for a specified organization the user is a member of.
+ """
+ organizationVerifiedDomainEmails(
+ """
+ The login of the organization to match verified domains from.
+ """
+ login: String!
+ ): [String!]!
+
+ """
+ A list of organizations the user belongs to.
+ """
+ organizations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): OrganizationConnection!
+
+ """
+ A list of packages under the owner.
+ """
+ packages(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Find packages by their names.
+ """
+ names: [String]
+
+ """
+ Ordering of the returned packages.
+ """
+ orderBy: PackageOrder = {field: CREATED_AT, direction: DESC}
+
+ """
+ Filter registry package by type.
+ """
+ packageType: PackageType
+
+ """
+ Find packages in a repository by ID.
+ """
+ repositoryId: ID
+ ): PackageConnection!
+
+ """
+ A list of repositories and gists this profile owner can pin to their profile.
+ """
+ pinnableItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinnable items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ A list of repositories and gists this profile owner has pinned to their profile
+ """
+ pinnedItems(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Filter the types of pinned items that are returned.
+ """
+ types: [PinnableItemType!]
+ ): PinnableItemConnection!
+
+ """
+ Returns how many more items this profile owner can pin to their profile.
+ """
+ pinnedItemsRemaining: Int!
+
+ """
+ Find project by number.
+ """
+ project(
+ """
+ The project number to find.
+ """
+ number: Int!
+ ): Project
+
+ """
+ A list of projects under the owner.
+ """
+ projects(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for projects returned from the connection
+ """
+ orderBy: ProjectOrder
+
+ """
+ Query to search projects by, currently only searching by name.
+ """
+ search: String
+
+ """
+ A list of states to filter the projects by.
+ """
+ states: [ProjectState!]
+ ): ProjectConnection!
+
+ """
+ The HTTP path listing user's projects
+ """
+ projectsResourcePath: URI!
+
+ """
+ The HTTP URL listing user's projects
+ """
+ projectsUrl: URI!
+
+ """
+ A list of public keys associated with this user.
+ """
+ publicKeys(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): PublicKeyConnection!
+
+ """
+ A list of pull requests associated with this user.
+ """
+ pullRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ The base ref name to filter the pull requests by.
+ """
+ baseRefName: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ The head ref name to filter the pull requests by.
+ """
+ headRefName: String
+
+ """
+ A list of label names to filter the pull requests by.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for pull requests returned from the connection.
+ """
+ orderBy: IssueOrder
+
+ """
+ A list of states to filter the pull requests by.
+ """
+ states: [PullRequestState!]
+ ): PullRequestConnection!
+
+ """
+ A list of repositories that the user owns.
+ """
+ repositories(
+ """
+ Array of viewer's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ current viewer owns.
+ """
+ affiliations: [RepositoryAffiliation]
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If non-null, filters repositories according to whether they are forks of another repository
+ """
+ isFork: Boolean
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ Array of owner's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ organization or user being viewed owns.
+ """
+ ownerAffiliations: [RepositoryAffiliation] = [OWNER, COLLABORATOR]
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ A list of repositories that the user recently contributed to.
+ """
+ repositoriesContributedTo(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ If non-null, include only the specified types of contributions. The
+ GitHub.com UI uses [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]
+ """
+ contributionTypes: [RepositoryContributionType]
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If true, include user repositories
+ """
+ includeUserRepositories: Boolean
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ Find Repository.
+ """
+ repository(
+ """
+ Name of Repository to find.
+ """
+ name: String!
+ ): Repository
+
+ """
+ The HTTP path for this user
+ """
+ resourcePath: URI!
+
+ """
+ Replies this user has saved
+ """
+ savedReplies(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ The field to order saved replies by.
+ """
+ orderBy: SavedReplyOrder = {field: UPDATED_AT, direction: DESC}
+ ): SavedReplyConnection
+
+ """
+ The GitHub Sponsors listing for this user or organization.
+ """
+ sponsorsListing: SponsorsListing
+
+ """
+ This object's sponsorships as the maintainer.
+ """
+ sponsorshipsAsMaintainer(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Whether or not to include private sponsorships in the result set
+ """
+ includePrivate: Boolean = false
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ This object's sponsorships as the sponsor.
+ """
+ sponsorshipsAsSponsor(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for sponsorships returned from this connection. If left
+ blank, the sponsorships will be ordered based on relevancy to the viewer.
+ """
+ orderBy: SponsorshipOrder
+ ): SponsorshipConnection!
+
+ """
+ Repositories the user has starred.
+ """
+ starredRepositories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Order for connection
+ """
+ orderBy: StarOrder
+
+ """
+ Filters starred repositories to only return repositories owned by the viewer.
+ """
+ ownedByViewer: Boolean
+ ): StarredRepositoryConnection!
+
+ """
+ The user's description of what they're currently doing.
+ """
+ status: UserStatus
+
+ """
+ Repositories the user has contributed to, ordered by contribution rank, plus repositories the user has created
+ """
+ topRepositories(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder!
+
+ """
+ How far back in time to fetch contributed repositories
+ """
+ since: DateTime
+ ): RepositoryConnection!
+
+ """
+ The user's Twitter username.
+ """
+ twitterUsername: String
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The HTTP URL for this user
+ """
+ url: URI!
+
+ """
+ Can the viewer pin repositories and gists to the profile?
+ """
+ viewerCanChangePinnedItems: Boolean!
+
+ """
+ Can the current viewer create new projects on this owner.
+ """
+ viewerCanCreateProjects: Boolean!
+
+ """
+ Whether or not the viewer is able to follow the user.
+ """
+ viewerCanFollow: Boolean!
+
+ """
+ Whether or not the viewer is able to sponsor this user/organization.
+ """
+ viewerCanSponsor: Boolean!
+
+ """
+ Whether or not this user is followed by the viewer.
+ """
+ viewerIsFollowing: Boolean!
+
+ """
+ True if the viewer is sponsoring this user/organization.
+ """
+ viewerIsSponsoring: Boolean!
+
+ """
+ A list of repositories the given user is watching.
+ """
+ watching(
+ """
+ Affiliation options for repositories returned from the connection. If none
+ specified, the results will include repositories for which the current
+ viewer is an owner or collaborator, or member.
+ """
+ affiliations: [RepositoryAffiliation]
+
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ If non-null, filters repositories according to whether they have been locked
+ """
+ isLocked: Boolean
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Ordering options for repositories returned from the connection
+ """
+ orderBy: RepositoryOrder
+
+ """
+ Array of owner's affiliation options for repositories returned from the
+ connection. For example, OWNER will include only repositories that the
+ organization or user being viewed owns.
+ """
+ ownerAffiliations: [RepositoryAffiliation] = [OWNER, COLLABORATOR]
+
+ """
+ If non-null, filters repositories according to privacy
+ """
+ privacy: RepositoryPrivacy
+ ): RepositoryConnection!
+
+ """
+ A URL pointing to the user's public website/blog.
+ """
+ websiteUrl: URI
+}
+
+"""
+The possible durations that a user can be blocked for.
+"""
+enum UserBlockDuration {
+ """
+ The user was blocked for 1 day
+ """
+ ONE_DAY
+
+ """
+ The user was blocked for 30 days
+ """
+ ONE_MONTH
+
+ """
+ The user was blocked for 7 days
+ """
+ ONE_WEEK
+
+ """
+ The user was blocked permanently
+ """
+ PERMANENT
+
+ """
+ The user was blocked for 3 days
+ """
+ THREE_DAYS
+}
+
+"""
+Represents a 'user_blocked' event on a given user.
+"""
+type UserBlockedEvent implements Node {
+ """
+ Identifies the actor who performed the event.
+ """
+ actor: Actor
+
+ """
+ Number of days that the user was blocked for.
+ """
+ blockDuration: UserBlockDuration!
+
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+ id: ID!
+
+ """
+ The user who was blocked.
+ """
+ subject: User
+}
+
+"""
+The connection type for User.
+"""
+type UserConnection {
+ """
+ A list of edges.
+ """
+ edges: [UserEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [User]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edit on user content
+"""
+type UserContentEdit implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ Identifies the date and time when the object was deleted.
+ """
+ deletedAt: DateTime
+
+ """
+ The actor who deleted this content
+ """
+ deletedBy: Actor
+
+ """
+ A summary of the changes for this edit
+ """
+ diff: String
+
+ """
+ When this content was edited
+ """
+ editedAt: DateTime!
+
+ """
+ The actor who edited this content
+ """
+ editor: Actor
+ id: ID!
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+}
+
+"""
+A list of edits to content.
+"""
+type UserContentEditConnection {
+ """
+ A list of edges.
+ """
+ edges: [UserContentEditEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [UserContentEdit]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type UserContentEditEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: UserContentEdit
+}
+
+"""
+Represents a user.
+"""
+type UserEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: User
+}
+
+"""
+Email attributes from External Identity
+"""
+type UserEmailMetadata {
+ """
+ Boolean to identify primary emails
+ """
+ primary: Boolean
+
+ """
+ Type of email
+ """
+ type: String
+
+ """
+ Email id
+ """
+ value: String!
+}
+
+"""
+The user's description of what they're currently doing.
+"""
+type UserStatus implements Node {
+ """
+ Identifies the date and time when the object was created.
+ """
+ createdAt: DateTime!
+
+ """
+ An emoji summarizing the user's status.
+ """
+ emoji: String
+
+ """
+ The status emoji as HTML.
+ """
+ emojiHTML: HTML
+
+ """
+ If set, the status will not be shown after this date.
+ """
+ expiresAt: DateTime
+
+ """
+ ID of the object.
+ """
+ id: ID!
+
+ """
+ Whether this status indicates the user is not fully available on GitHub.
+ """
+ indicatesLimitedAvailability: Boolean!
+
+ """
+ A brief message describing what the user is doing.
+ """
+ message: String
+
+ """
+ The organization whose members can see this status. If null, this status is publicly visible.
+ """
+ organization: Organization
+
+ """
+ Identifies the date and time when the object was last updated.
+ """
+ updatedAt: DateTime!
+
+ """
+ The user who has this status.
+ """
+ user: User!
+}
+
+"""
+The connection type for UserStatus.
+"""
+type UserStatusConnection {
+ """
+ A list of edges.
+ """
+ edges: [UserStatusEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [UserStatus]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type UserStatusEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: UserStatus
+}
+
+"""
+Ordering options for user status connections.
+"""
+input UserStatusOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order user statuses by.
+ """
+ field: UserStatusOrderField!
+}
+
+"""
+Properties by which user status connections can be ordered.
+"""
+enum UserStatusOrderField {
+ """
+ Order user statuses by when they were updated.
+ """
+ UPDATED_AT
+}
+
+"""
+A domain that can be verified for an organization or an enterprise.
+"""
+type VerifiableDomain implements Node {
+ """
+ Identifies the primary key from the database.
+ """
+ databaseId: Int
+
+ """
+ The DNS host name that should be used for verification.
+ """
+ dnsHostName: URI
+
+ """
+ The unicode encoded domain.
+ """
+ domain: URI!
+
+ """
+ Whether a TXT record for verification with the expected host name was found.
+ """
+ hasFoundHostName: Boolean!
+
+ """
+ Whether a TXT record for verification with the expected verification token was found.
+ """
+ hasFoundVerificationToken: Boolean!
+ id: ID!
+
+ """
+ Whether this domain is required to exist for an organization policy to be enforced.
+ """
+ isRequiredForPolicyEnforcement: Boolean!
+
+ """
+ Whether or not the domain is verified.
+ """
+ isVerified: Boolean!
+
+ """
+ The owner of the domain.
+ """
+ owner: VerifiableDomainOwner!
+
+ """
+ The punycode encoded domain.
+ """
+ punycodeEncodedDomain: URI!
+
+ """
+ The time that the current verification token will expire.
+ """
+ tokenExpirationTime: DateTime
+
+ """
+ The current verification token for the domain.
+ """
+ verificationToken: String
+}
+
+"""
+The connection type for VerifiableDomain.
+"""
+type VerifiableDomainConnection {
+ """
+ A list of edges.
+ """
+ edges: [VerifiableDomainEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [VerifiableDomain]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+
+ """
+ Identifies the total count of items in the connection.
+ """
+ totalCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type VerifiableDomainEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: VerifiableDomain
+}
+
+"""
+Ordering options for verifiable domain connections.
+"""
+input VerifiableDomainOrder {
+ """
+ The ordering direction.
+ """
+ direction: OrderDirection!
+
+ """
+ The field to order verifiable domains by.
+ """
+ field: VerifiableDomainOrderField!
+}
+
+"""
+Properties by which verifiable domain connections can be ordered.
+"""
+enum VerifiableDomainOrderField {
+ """
+ Order verifiable domains by the domain name.
+ """
+ DOMAIN
+}
+
+"""
+Types that can own a verifiable domain.
+"""
+union VerifiableDomainOwner = Enterprise | Organization
+
+"""
+Autogenerated input type of VerifyVerifiableDomain
+"""
+input VerifyVerifiableDomainInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The ID of the verifiable domain to verify.
+ """
+ id: ID! @possibleTypes(concreteTypes: ["VerifiableDomain"])
+}
+
+"""
+Autogenerated return type of VerifyVerifiableDomain
+"""
+type VerifyVerifiableDomainPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The verifiable domain that was verified.
+ """
+ domain: VerifiableDomain
+}
+
+"""
+A hovercard context with a message describing how the viewer is related.
+"""
+type ViewerHovercardContext implements HovercardContext {
+ """
+ A string describing this context
+ """
+ message: String!
+
+ """
+ An octicon to accompany this context
+ """
+ octicon: String!
+
+ """
+ Identifies the user who is related to this context.
+ """
+ viewer: User!
+}
+
+"""
+A valid x509 certificate string
+"""
+scalar X509Certificate
diff --git a/cla-backend-go/github/protected_branch.go b/cla-backend-go/github/protected_branch.go
index 97d0fce65..1f449d14a 100644
--- a/cla-backend-go/github/protected_branch.go
+++ b/cla-backend-go/github/protected_branch.go
@@ -9,13 +9,16 @@ import (
"fmt"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/swag"
+ "github.com/sirupsen/logrus"
"github.com/jinzhu/copier"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
githubpkg "github.com/google/go-github/v33/github"
+ "github.com/shurcooL/githubv4"
"go.uber.org/ratelimit"
"golang.org/x/time/rate"
)
@@ -267,6 +270,189 @@ func (bp *BranchProtectionRepository) EnableBranchProtection(ctx context.Context
return err
}
+// GetRepositoryIDFromName when provided the organization and repository name, returns the repository ID
+func GetRepositoryIDFromName(ctx context.Context, installationID int64, repositoryOwner, repositoryName string) (string, error) {
+ f := logrus.Fields{
+ "functionName": "github.GetRepositoryIDFromName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "installationID": installationID,
+ "repositoryOwner": repositoryOwner,
+ "repositoryName": repositoryName,
+ }
+
+ log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
+ client, clientErr := NewGithubV4AppClient(installationID)
+ if clientErr != nil {
+ log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
+ return "", clientErr
+ }
+ log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
+
+ // Define the graphql query
+ //"query": "query{repository(name: \"test1\", owner: \"deal-test-org\") {id}}"
+ var query struct {
+ Viewer struct {
+ Login githubv4.String
+ }
+ Repository struct {
+ ID string
+ } `graphql:"repository(owner:$repositoryOwner, name:$repositoryName)"`
+ }
+
+ // Define the variables for the query
+ variables := map[string]interface{}{
+ "repositoryOwner": githubv4.String(repositoryOwner),
+ "repositoryName": githubv4.String(repositoryName),
+ }
+
+ log.WithFields(f).Debug("executing the query...")
+ err := client.Query(ctx, &query, variables)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem executing GitHub v4 query using: %+v with variables: %+v",
+ query, variables)
+ return "", err
+ }
+
+ log.WithFields(f).Debugf("User %s looked up repository ID: %s wth installation ID: %d using repository name: %s",
+ query.Viewer.Login, query.Repository.ID, installationID, repositoryName)
+ return query.Repository.ID, nil
+}
+
+// GetRepositoryBranchProtection when provided the organization and repository name, returns the repository branch protection rules/info
+func GetRepositoryBranchProtection(ctx context.Context, installationID int64, repositoryOwner, repositoryName string) error {
+ f := logrus.Fields{
+ "functionName": "github.GetRepositoryBranchProtection",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "installationID": installationID,
+ "repositoryOwner": repositoryOwner,
+ "repositoryName": repositoryName,
+ }
+
+ // NOTE: This function is not complete - does not return the values as we are still evaluating/testing this API
+
+ log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
+ client, clientErr := NewGithubV4AppClient(installationID)
+ if clientErr != nil {
+ log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
+ return clientErr
+ }
+ log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
+
+ // Define the graphql query
+ /*
+ query {
+ repository(owner: "lee-dohm", name: "test-repo") {
+ branchProtectionRules(first: 10) {
+ nodes {
+ pattern
+ }
+ }
+ }
+ }
+ */
+ var query struct {
+ Viewer struct {
+ Login githubv4.String
+ }
+ //Repository struct {
+ // BranchProtectionRepositoryOption struct {
+ // }
+ //} `graphql:"repository(owner:$repositoryOwner, name:$repositoryName)"`
+ }
+
+ // Define the variables for the query
+ variables := map[string]interface{}{
+ "repositoryOwner": githubv4.String(repositoryOwner),
+ "repositoryName": githubv4.String(repositoryName),
+ }
+
+ log.WithFields(f).Debug("executing the query...")
+ err := client.Query(ctx, &query, variables)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem executing GitHub v4 query using: %+v with variables: %+v",
+ query, variables)
+ return err
+ }
+
+ // NOTE: still need to implement logic above
+ return nil
+}
+
+// EnableBranchProtectionForAll sets the branch protection for all branches for the specified repository
+func EnableBranchProtectionForAll(ctx context.Context, installationID int64, repositoryOwner, repositoryName string, enforceAdmin bool, enableStatusChecks, disableStatusChecks []string) error {
+ f := logrus.Fields{
+ "functionName": "github.EnableBranchProtectionForAll",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "installationID": installationID,
+ "repositoryOwner": repositoryOwner,
+ "repositoryName": repositoryName,
+ "enforceAdmin": enforceAdmin,
+ "enableStatusChecks": strings.Join(enableStatusChecks, ","),
+ "disableStatusChecks": strings.Join(disableStatusChecks, ","),
+ }
+
+ log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
+ client, clientErr := NewGithubV4AppClient(installationID)
+ if clientErr != nil {
+ log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
+ return clientErr
+ }
+ log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
+
+ // Define the graphql mutation/update
+ // This is a sample, not implemented yet
+ var mutation struct {
+ AddReaction struct {
+ Reaction struct {
+ Content githubv4.ReactionContent
+ }
+ Subject struct {
+ ID githubv4.ID
+ }
+ } `graphql:"addReaction(input: $input)"`
+ Repository struct {
+ ID string
+ } `graphql:"repository(repositoryOwner:$repositoryOwner, name:$repositoryName)"`
+ }
+
+ // Lookup the unique repository ID from the organization and repository name
+ repositoryID, lookupErr := GetRepositoryIDFromName(ctx, installationID, repositoryOwner, repositoryName)
+ if lookupErr != nil {
+ log.WithFields(f).WithError(lookupErr).Warnf("problem loading repository ID from repository owner and repository name values using installation ID: %d", installationID)
+ return lookupErr
+ }
+
+ input := githubv4.CreateBranchProtectionRuleInput{
+ RepositoryID: repositoryID,
+ Pattern: "**/**",
+ RequiresApprovingReviews: nil,
+ RequiredApprovingReviewCount: nil,
+ RequiresCommitSignatures: nil,
+ RequiresLinearHistory: nil,
+ AllowsForcePushes: githubv4.NewBoolean(false),
+ AllowsDeletions: nil,
+ IsAdminEnforced: githubv4.NewBoolean(githubv4.Boolean(enforceAdmin)),
+ RequiresStatusChecks: githubv4.NewBoolean(len(enableStatusChecks) > 0),
+ RequiresStrictStatusChecks: nil,
+ RequiresCodeOwnerReviews: nil,
+ DismissesStaleReviews: nil,
+ RestrictsReviewDismissals: nil,
+ ReviewDismissalActorIDs: nil,
+ RestrictsPushes: nil,
+ PushActorIDs: nil,
+ RequiredStatusCheckContexts: nil,
+ ClientMutationID: nil,
+ }
+
+ // Define the variables for the query
+ variables := map[string]interface{}{
+ "repositoryOwner": githubv4.String(repositoryOwner),
+ "repositoryName": githubv4.String(repositoryName),
+ }
+
+ return client.Mutate(ctx, &mutation, input, variables)
+}
+
// createBranchProtectionRequest creates a branch protection request from existing protection
func createBranchProtectionRequest(protection *githubpkg.Protection, enableStatusChecks, disableStatusChecks []string, enforceAdmin bool) (*githubpkg.ProtectionRequest, error) {
var currentChecks *githubpkg.RequiredStatusChecks
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index 4621ac135..ae1fb4901 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -47,6 +47,8 @@ require (
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
+ github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
+ github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a
github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.3.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 4807b6946..a751a0aa4 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -427,6 +427,10 @@ github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wk
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429/go.mod h1:fK0DIsn9VGLYVur3nQ54Yz4LSLLCyDil0gzq5Y8Yzls=
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
+github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
+github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=
+github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
diff --git a/cla-backend-go/init/init.go b/cla-backend-go/init/init.go
index d0b96041d..f0f4e5635 100644
--- a/cla-backend-go/init/init.go
+++ b/cla-backend-go/init/init.go
@@ -63,7 +63,7 @@ func GetStage() string {
return stage
}
-// GetConfig returns the configration SSM based on stage, e.g. dev, test, stage or prod
+// GetConfig returns the configuration SSM based on stage, e.g. dev, test, stage or prod
func GetConfig() config.Config {
return configVars
}
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index 641f6be7c..4c7639699 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -20,7 +20,7 @@ import (
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
)
-// Service contains functions of Github Repository service
+// Service contains functions of GitHub Repository service
type Service interface {
AddGithubRepository(ctx context.Context, externalProjectID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
EnableRepository(ctx context.Context, repositoryID string) error
diff --git a/cla-backend-go/swagger/cla.v1.yaml b/cla-backend-go/swagger/cla.v1.yaml
index 0a5b85134..cff74f9bb 100644
--- a/cla-backend-go/swagger/cla.v1.yaml
+++ b/cla-backend-go/swagger/cla.v1.yaml
@@ -169,6 +169,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- users
delete:
@@ -200,6 +202,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- users
@@ -365,6 +369,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -396,6 +402,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -429,6 +437,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -460,6 +470,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -492,6 +504,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -564,6 +578,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
delete:
@@ -598,6 +614,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
post:
@@ -632,6 +650,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- signatures
@@ -1198,6 +1218,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- company
@@ -1261,6 +1283,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- company
@@ -1291,6 +1315,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- company
@@ -1321,6 +1347,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- company
@@ -1351,6 +1379,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- company
@@ -1589,6 +1619,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
tags:
@@ -1625,6 +1657,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
tags:
@@ -1694,6 +1728,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
tags:
@@ -1795,6 +1831,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
tags:
@@ -2093,6 +2131,8 @@ paths:
$ref: '#/responses/unauthorized'
403:
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- github-repositories
get:
@@ -2119,6 +2159,8 @@ paths:
$ref: '#/responses/unauthorized'
403:
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- github-repositories
/project/{projectSFID}/github/repositories/{repositoryID}:
@@ -2259,10 +2301,10 @@ paths:
$ref: '#/definitions/gerrit-repo-list'
'400':
$ref: '#/responses/invalid-request'
- '404':
- $ref: '#/responses/not-found'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'500':
$ref: '#/responses/internal-server-error'
tags:
@@ -2293,6 +2335,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
tags:
- gerrits
@@ -2327,6 +2371,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
tags:
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 3207f8f7e..8498d73c2 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -2899,6 +2899,75 @@ paths:
tags:
- gerrits
+ /company/{companyID}:
+ get:
+ summary: Get Company By Internal ID
+ description: Returns the company by internal ID
+ operationId: getCompanyByInternalID
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-companyID"
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/company'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ tags:
+ - company
+
+ /company/external/{companySFID}:
+ get:
+ summary: Get Company by External SFID
+ description: Returns the company by external ID
+ operationId: getCompanyByExternalID
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: companySFID
+ in: path
+ type: string
+ required: true
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/company'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ tags:
+ - company
+
/company/name/{companyName}:
get:
summary: Gets the company by name
@@ -2936,8 +3005,8 @@ paths:
/company/entityname/{signingEntityName}:
get:
- summary: Gets the company by name
- description: Returns the matching company by name
+ summary: Gets the company by signing entity namename
+ description: Returns the matching company by signing entity name
operationId: getCompanyBySigningEntityName
parameters:
- $ref: "#/parameters/x-request-id"
diff --git a/cla-backend-go/tests/github_v4_test.go b/cla-backend-go/tests/github_v4_test.go
new file mode 100644
index 000000000..5a5555da4
--- /dev/null
+++ b/cla-backend-go/tests/github_v4_test.go
@@ -0,0 +1,45 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "fmt"
+ "strconv"
+ "testing"
+
+ ini "github.com/communitybridge/easycla/cla-backend-go/init"
+ "github.com/spf13/viper"
+
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetRepositoryIDFromName(t *testing.T) {
+
+ ctx := utils.NewContext()
+
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ viper.Set("STAGE", "dev")
+ viper.Set("DYNAMODB_AWS_REGION", "us-east-1")
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+ github.Init(config.GitHub.AppID, config.GitHub.AppPrivateKey, config.GitHub.AccessToken)
+ installationID, int64Err := strconv.ParseInt(config.GitHub.TestOrganizationInstallationID, 10, 64)
+ if int64Err != nil {
+ assert.Fail(t, fmt.Sprintf("unable to convert installation ID to string: %s", config.GitHub.TestOrganizationInstallationID), int64Err)
+ }
+
+ expectedValue := config.GitHub.TestRepositoryID
+ actualValue, err := github.GetRepositoryIDFromName(ctx, installationID, config.GitHub.TestOrganization, config.GitHub.TestRepository)
+ if err != nil {
+ assert.Fail(t, fmt.Sprintf("unable to create GitHub v4 client from installation ID: %d", installationID), err)
+ }
+ assert.Equal(t, expectedValue, actualValue, "Repository ID Lookup")
+}
diff --git a/cla-backend-go/users/handlers.go b/cla-backend-go/users/handlers.go
index 9205285b7..fb97b3bb1 100644
--- a/cla-backend-go/users/handlers.go
+++ b/cla-backend-go/users/handlers.go
@@ -74,7 +74,7 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
}
// Update supports two scenarios:
// 1) user has LF login and their record has the LF login as part of their existing User record - should find and match - OK, otherwise permission denied
- // 2) user has new LF login and their record does not have the LF login as part of their existing User record - need to lookup by other means, such as Github Username
+ // 2) user has new LF login and their record does not have the LF login as part of their existing User record - need to lookup by other means, such as GitHub Username
// option 2 can happen when GH user gets a user record auto-created and later they need a login for v2 (create company, etc.)
// option 2 will be called after they create their login to update their user record with the new login details
// option 2 we will search by github username to find the old record - but we can't compare LF login with the existing record because it won't be set yet
diff --git a/cla-backend-go/utils/conversion.go b/cla-backend-go/utils/conversion.go
index 10f56725b..e2a0f3d32 100644
--- a/cla-backend-go/utils/conversion.go
+++ b/cla-backend-go/utils/conversion.go
@@ -31,3 +31,8 @@ func BoolValue(input *bool) bool {
}
return *input
}
+
+// Bool function convert boolean to boolean pointer
+func Bool(input bool) *bool {
+ return &input
+}
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index 04fa0e205..de860c7a8 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -3,7 +3,10 @@
package utils
-import "fmt"
+import (
+ "fmt"
+ "strings"
+)
// ConversionError is an error model for representing conversion errors
type ConversionError struct {
@@ -153,37 +156,6 @@ func (e *ProjectCLAGroupMappingNotFound) Unwrap() error {
return e.Err
}
-// CompanyDoesNotExist is an error model for company does not exist errors
-type CompanyDoesNotExist struct {
- CompanyName string
- CompanyID string
- CompanySFID string
- Err error
-}
-
-// Error is an error string function for company does not exist errs
-func (e *CompanyDoesNotExist) Error() string {
- var errMsg = "company does not exist"
- if e.CompanyName == "" {
- errMsg = fmt.Sprintf("%s company name: %s", errMsg, e.CompanyName)
- }
- if e.CompanyID == "" {
- errMsg = fmt.Sprintf("%s company id: %s", errMsg, e.CompanyID)
- }
- if e.CompanySFID == "" {
- errMsg = fmt.Sprintf("%s company sfid: %s", errMsg, e.CompanySFID)
- }
- if e.Err != nil {
- errMsg = fmt.Sprintf("%s error: %+v", errMsg, e.Err)
- }
- return errMsg
-}
-
-// Unwrap method returns its contained error
-func (e *CompanyDoesNotExist) Unwrap() error {
- return e.Err
-}
-
// GitHubOrgNotFound is an error model for GitHub Organization not found errors
type GitHubOrgNotFound struct {
ProjectSFID string
@@ -219,3 +191,43 @@ func (e *CompanyAdminNotFound) Error() string {
func (e *CompanyAdminNotFound) Unwrap() error {
return e.Err
}
+
+// CompanyNotFound is an error model for company not found errors
+type CompanyNotFound struct {
+ Message string
+ CompanyID string
+ CompanySFID string
+ CompanyName string
+ CompanySigningEntityName string
+ Err error
+}
+
+// Error is an error string function for Salesforce Project not found errors
+func (e *CompanyNotFound) Error() string {
+ msg := "company does not exist "
+ if e.Message != "" {
+ msg = e.Message
+ }
+ if e.CompanyName != "" {
+ msg = fmt.Sprintf("%s - company name: %s ", msg, e.CompanyName)
+ }
+ if e.CompanySigningEntityName != "" {
+ msg = fmt.Sprintf("%s - company sigining entity name: %s ", msg, e.CompanySigningEntityName)
+ }
+ if e.CompanyID != "" {
+ msg = fmt.Sprintf("%s - company ID: %s ", msg, e.CompanyID)
+ }
+ if e.CompanySFID != "" {
+ msg = fmt.Sprintf("%s - company SFID: %s ", msg, e.CompanySFID)
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *CompanyNotFound) Unwrap() error {
+ return e.Err
+}
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 696e308d0..3e3a4e142 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -310,7 +310,8 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
"contactAdmin": params.Body.ContactAdmin,
"userFullName": utils.StringValue(params.Body.FullName),
"userEmail": params.Body.UserEmail.String(),
- "authUser": *params.XUSERNAME,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Lookup the company by internal ID
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 48fb2fddf..fd4d822b3 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -16,7 +16,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/LF-Engineering/lfx-kit/auth"
- v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/company"
@@ -28,6 +27,94 @@ import (
// Configure sets up the middleware handlers
func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo projects_cla_groups.Repository, LFXPortalURL, v1CorporateConsole string) { // nolint
+ api.CompanyGetCompanyByInternalIDHandler = company.GetCompanyByInternalIDHandlerFunc(
+ func(params company.GetCompanyByInternalIDParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "company.handlers.CompanyGetCompanyByInternalIDHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": params.CompanyID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
+ }
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by internal ID...")
+ v2CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ if _, ok := err.(*utils.CompanyNotFound); ok {
+ return company.NewGetCompanyByInternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ return company.NewGetCompanyByInternalIDBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ if v2CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyByInternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ log.WithFields(f).Debug("checking permissions")
+ if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to CompanyGetCompanyByInternalIDHandler with Organization scope of %s",
+ authUser.UserName, v2CompanyModel.CompanyExternalID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyByInternalIDForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ return company.NewGetCompanyByInternalIDOK().WithXRequestID(reqID).WithPayload(v2CompanyModel)
+ })
+
+ api.CompanyGetCompanyByExternalIDHandler = company.GetCompanyByExternalIDHandlerFunc(
+ func(params company.GetCompanyByExternalIDParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "company.handlers.CompanyGetCompanyByExternalIDHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companySFID": params.CompanySFID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
+ }
+
+ // Lookup the company by internal ID
+ log.WithFields(f).Debugf("looking up company by SFID...")
+ v2CompanyModel, err := service.GetCompanyBySFID(ctx, params.CompanySFID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to lookup company by SFID: %s", params.CompanySFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ if _, ok := err.(*utils.CompanyNotFound); ok {
+ return company.NewGetCompanyByExternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ if _, ok := err.(*organizations.GetOrgNotFound); ok {
+ return company.NewGetCompanyByExternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ log.WithFields(f).Debugf("error type is: %T", err)
+ return company.NewGetCompanyByExternalIDBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ if v2CompanyModel == nil {
+ msg := fmt.Sprintf("unable to lookup company by SFID: %s", params.CompanySFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyByExternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ log.WithFields(f).Debug("checking permissions")
+ if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to CompanyGetCompanyByExternalIDHandler with Organization scope of %s",
+ authUser.UserName, v2CompanyModel.CompanyExternalID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyByExternalIDForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ return company.NewGetCompanyByExternalIDOK().WithXRequestID(reqID).WithPayload(v2CompanyModel)
+ })
+
api.CompanyGetCompanyProjectClaManagersHandler = company.GetCompanyProjectClaManagersHandlerFunc(
func(params company.GetCompanyProjectClaManagersParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
@@ -38,26 +125,28 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Lookup the company by internal ID
log.WithFields(f).Debugf("looking up company by internal ID...")
- v1CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
- if err != nil || v1CompanyModel == nil {
+ v2CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil || v2CompanyModel == nil {
msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
return company.NewGetCompanyProjectClaManagersBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectClaManagers with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
+ authUser.UserName, params.ProjectSFID, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
return company.NewGetCompanyProjectClaManagersForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.GetCompanyProjectCLAManagers(ctx, v1CompanyModel, params.ProjectSFID)
+ result, err := service.GetCompanyProjectCLAManagers(ctx, v2CompanyModel, params.ProjectSFID)
if err != nil {
msg := "unable to load company project CLA managers"
log.WithFields(f).WithError(err).Warn(msg)
@@ -84,7 +173,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
if err != nil {
msg := "problem loading company CLA group managers"
log.WithFields(f).WithError(err).Warn(msg)
- if err == v1Company.ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return company.NewGetCompanyCLAGroupManagersNotFound().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
@@ -105,27 +194,29 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Lookup the company by internal ID
log.WithFields(f).Debugf("looking up company by internal ID...")
- v1CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
- if err != nil || v1CompanyModel == nil {
+ v2CompanyModel, err := service.GetCompanyByID(ctx, params.CompanyID)
+ if err != nil || v2CompanyModel == nil {
msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectActiveCla with Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
+ authUser.UserName, params.ProjectSFID, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
return company.NewGetCompanyProjectActiveClaForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
log.WithFields(f).Debug("getting company project active CLAs...")
- result, err := service.GetCompanyProjectActiveCLAs(ctx, v1CompanyModel.CompanyID, params.ProjectSFID)
+ result, err := service.GetCompanyProjectActiveCLAs(ctx, v2CompanyModel.CompanyID, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("CLA Group not found with given project SFID: %s", params.ProjectSFID)
@@ -133,7 +224,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbiddenWithError(reqID, msg, err))
}
- msg := fmt.Sprintf("error looking up active project CLAs by internal company ID: %s and project SFID: %s", v1CompanyModel.CompanyID, params.ProjectSFID)
+ msg := fmt.Sprintf("error looking up active project CLAs by internal company ID: %s and project SFID: %s", v2CompanyModel.CompanyID, params.ProjectSFID)
log.WithFields(f).Warn(msg)
return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
@@ -151,6 +242,8 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Lookup the company by internal ID
@@ -159,6 +252,9 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
if err != nil || v1CompanyModel == nil {
msg := fmt.Sprintf("unable to lookup company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
+ if _, ok := err.(*utils.CompanyNotFound); ok {
+ return company.NewGetCompanyProjectActiveClaNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
@@ -167,16 +263,16 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
// CLA Manager - check if authorized by project|organization scope - allow if CLA Manager (for example) has project ID + org DI scope that matches
log.WithFields(f).Debug("checking permissions")
if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, projectClaGroupRepo) {
+ msg := fmt.Sprintf("user %s does not have access to get contributors with Project scope of %s or Project|Organization scope of %s | %s",
+ authUser.UserName, params.ProjectSFID, params.ProjectSFID, params.CompanyID)
+ log.WithFields(f).Warn(msg)
return company.NewGetCompanyProjectContributorsForbidden().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseForbidden(
- reqID,
- fmt.Sprintf("user %s does not have access to get contributors with Project scope of %s or Project|Organization scope of %s | %s",
- authUser.UserName, params.ProjectSFID, params.ProjectSFID, params.CompanyID)))
+ utils.ErrorResponseForbidden(reqID, msg))
}
result, err := service.GetCompanyProjectContributors(ctx, params.ProjectSFID, params.CompanyID, utils.StringValue(params.SearchTerm))
if err != nil {
- if err == v1Company.ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID)
}
return company.NewGetCompanyProjectContributorsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
@@ -194,6 +290,8 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companySFID": params.CompanySFID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
log.WithFields(f).Debug("checking permissions")
@@ -208,7 +306,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
if err != nil {
msg := "unable to load project company CLAs"
log.WithFields(f).WithError(err).Warn(msg)
- if err == v1Company.ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return company.NewGetCompanyProjectClaNotFound().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
@@ -329,6 +427,8 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
"functionName": "company.handlers.CompanyDeleteCompanyByIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": params.CompanyID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Attempt to locate the company by ID
@@ -381,6 +481,8 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
"functionName": "company.handlers.CompanyDeleteCompanyBySFIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
+ "authUserName": utils.StringValue(params.XUSERNAME),
+ "authUserEmail": utils.StringValue(params.XEMAIL),
}
// Attempt to locate the company by external SFID
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 5a7f09f6f..19dd56f21 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -487,14 +487,37 @@ func (s *service) GetCompanyBySigningEntityName(ctx context.Context, signingEnti
"signingEntityName": signingEntityName,
}
+ log.WithFields(f).Warn("looking up company record by signing entity name...")
companyModel, err := s.companyRepo.GetCompanyBySigningEntityName(ctx, signingEntityName)
if err != nil {
- return nil, err
+ if _, ok := err.(*utils.CompanyNotFound); ok { // nolint
+ // As a backup, in case the signing entity name was not set on the old records, lookup the company by it's normal name
+ log.WithFields(f).Debugf("signing entity name not found. as a backup, searching company by name using signing entity name value: %s", signingEntityName)
+ companyModel, err = s.companyRepo.GetCompanyByName(ctx, signingEntityName)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to lookup company name by attempting to use the signing entity name")
+ return nil, err
+ }
+ } else {
+ log.WithFields(f).WithError(err).Warn("unable to lookup company by signing entity name")
+ return nil, err
+ }
}
if companyModel == nil {
log.WithFields(f).Debugf("search by company signing entity name: %s didn't locate the record", signingEntityName)
- return nil, nil
+ // As a backup, in case the signing entity name was not set on the old records, lookup the company by it's normal name
+ log.WithFields(f).Debugf("as a backup, searching company by name using signing entity name value: %s", signingEntityName)
+ companyModel, err = s.companyRepo.GetCompanyByName(ctx, signingEntityName)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to lookup company name by attempting to use the signing entity name")
+ return nil, err
+ }
+
+ if companyModel == nil {
+ log.WithFields(f).Debugf("search by company name: %s didn't locate the record", signingEntityName)
+ return nil, nil
+ }
}
// Convert from v1 to v2 model - use helper: Copy(toValue interface{}, fromValue interface{})
@@ -726,7 +749,7 @@ func (s *service) GetCompanyBySFID(ctx context.Context, companySFID string) (*mo
if err != nil {
// If we were unable to find the company/org in our local database, try to auto-create based
// on the existing SF record
- if err == company.ErrCompanyDoesNotExist {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
log.WithFields(f).Debug("company not found in EasyCLA database - attempting to auto-create from platform organization service record")
newCompanyModel, createCompanyErr := s.autoCreateCompany(ctx, companySFID)
if createCompanyErr != nil {
@@ -736,7 +759,10 @@ func (s *service) GetCompanyBySFID(ctx context.Context, companySFID string) (*mo
}
if newCompanyModel == nil {
log.WithFields(f).Warnf("problem creating company from SF records - created model is nil")
- return nil, company.ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "unable to auto-create company",
+ CompanySFID: companySFID,
+ }
}
// Success, fall through and continue processing
companyModel = newCompanyModel
@@ -798,21 +824,24 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
if companyErr != nil {
// If we were unable to find the company/org in our local database, try to auto-create based
// on the existing SF record
- if companyErr == company.ErrCompanyDoesNotExist {
- log.WithFields(f).Debug("company not found in EasyCLA database - attempting to auto-create from platform organization service record")
+ if _, ok := companyErr.(*utils.CompanyNotFound); ok { // nolint
+ log.WithFields(f).WithError(companyErr).Debug("company not found in EasyCLA database - attempting to auto-create from platform organization service record")
var createCompanyErr error
companyModel, createCompanyErr = s.autoCreateCompany(ctx, companySFID)
if createCompanyErr != nil {
- log.WithFields(f).Warnf("problem creating company from platform organization SF record, error: %+v",
- createCompanyErr)
+ log.WithFields(f).WithError(createCompanyErr).Warn("problem creating company from platform organization SF record")
return nil, createCompanyErr
}
if companyModel == nil {
log.WithFields(f).Warnf("problem creating company from SF records - created model is nil")
- return nil, company.ErrCompanyDoesNotExist
+ return nil, &utils.CompanyNotFound{
+ Message: "unable to auto-create company",
+ CompanySFID: companySFID,
+ }
}
// Success, fall through and continue processing
} else {
+ log.WithFields(f).WithError(companyErr).Warnf("problem fetching company by SFID")
return nil, companyErr
}
}
@@ -1401,8 +1430,12 @@ func (s service) autoCreateCompany(ctx context.Context, companySFID string) (*v1
// If we were unable to lookup the company record in SF - we tried our best - return not exist error
if sfOrgModel == nil {
- log.WithFields(f).Warn("unable to locate platform organization record by SF ID - record not found")
- return nil, company.ErrCompanyDoesNotExist
+ msg := "unable to locate platform organization record by SF ID - record not found"
+ log.WithFields(f).Warn(msg)
+ return nil, &utils.CompanyNotFound{
+ Message: msg,
+ CompanySFID: companySFID,
+ }
}
log.WithFields(f).Debug("found platform organization record in SF")
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index cf7ca2f16..dcf6f6bf5 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -54,7 +54,7 @@ func NewAutoEnableService(repositoryService repositories.Service,
}
}
-// autoEnableServiceProvider is an abstraction helping with managing autoEnabled flag for Github Organization
+// autoEnableServiceProvider is an abstraction helping with managing autoEnabled flag for GitHub Organization
// having it separated in its own struct makes testing easier.
type autoEnableServiceProvider struct {
repositoryService repositories.Service
diff --git a/cla-backend-go/v2/dynamo_events/github_organization.go b/cla-backend-go/v2/dynamo_events/github_organization.go
index bba17a0e1..5b99176be 100644
--- a/cla-backend-go/v2/dynamo_events/github_organization.go
+++ b/cla-backend-go/v2/dynamo_events/github_organization.go
@@ -17,11 +17,13 @@ import (
// GitHubOrgAddedEvent github repository added event
func (s *service) GitHubOrgAddedEvent(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
f := logrus.Fields{
- "functionName": "dynamodb_events.GitHubOrgAddedEvent",
- "eventName": event.EventName,
- "eventSource": event.EventSource,
- "eventID": event.EventID,
+ "functionName": "dynamodb_events.github_organization.GitHubOrgAddedEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ "eventID": event.EventID,
}
log.WithFields(f).Debug("processing event")
@@ -35,7 +37,7 @@ func (s *service) GitHubOrgAddedEvent(event events.DynamoDBEventRecord) error {
// If the branch protection value was updated from false to true....
if newGitHubOrg.BranchProtectionEnabled {
log.WithFields(f).Debug("branchProtectionEnabled - processing...")
- return s.enableBranchProtectionForGithubOrg(f, newGitHubOrg)
+ return s.enableBranchProtectionForGithubOrg(ctx, newGitHubOrg)
}
if newGitHubOrg.AutoEnabled {
@@ -49,11 +51,13 @@ func (s *service) GitHubOrgAddedEvent(event events.DynamoDBEventRecord) error {
// GitHubOrgUpdatedEvent github repository updated event
func (s *service) GitHubOrgUpdatedEvent(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
f := logrus.Fields{
- "functionName": "dynamodb_events.GitHubOrgUpdatedEvent",
- "eventName": event.EventName,
- "eventSource": event.EventSource,
- "eventID": event.EventID,
+ "functionName": "dynamodb_events.github_organization.GitHubOrgUpdatedEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ "eventID": event.EventID,
}
log.WithFields(f).Debug("processing event")
@@ -72,7 +76,7 @@ func (s *service) GitHubOrgUpdatedEvent(event events.DynamoDBEventRecord) error
// If the branch protection value was updated from false to true....
if !oldGitHubOrg.BranchProtectionEnabled && newGitHubOrg.BranchProtectionEnabled {
log.WithFields(f).Debug("transition of branchProtectionEnabled false => true - processing...")
- return s.enableBranchProtectionForGithubOrg(f, newGitHubOrg)
+ return s.enableBranchProtectionForGithubOrg(ctx, newGitHubOrg)
}
if !oldGitHubOrg.AutoEnabled && newGitHubOrg.AutoEnabled {
@@ -87,11 +91,11 @@ func (s *service) GitHubOrgUpdatedEvent(event events.DynamoDBEventRecord) error
func (s *service) GitHubOrgDeletedEvent(event events.DynamoDBEventRecord) error {
ctx := utils.NewContext()
f := logrus.Fields{
- "functionName": "dynamodb_events.GitHubOrgDeletedEvent",
+ "functionName": "dynamodb_events.github_organization.GitHubOrgDeletedEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"eventName": event.EventName,
"eventSource": event.EventSource,
"eventID": event.EventID,
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
log.WithFields(f).Debug("processing event")
@@ -127,7 +131,19 @@ func (s *service) GitHubOrgDeletedEvent(event events.DynamoDBEventRecord) error
return nil
}
-func (s *service) enableBranchProtectionForGithubOrg(f logrus.Fields, newGitHubOrg github_organizations.GithubOrganization) error {
+func (s *service) enableBranchProtectionForGithubOrg(ctx context.Context, newGitHubOrg github_organizations.GithubOrganization) error {
+ f := logrus.Fields{
+ "functionName": "dynamo_events.github_organization.enableBranchProtectionForGithubOrg",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": newGitHubOrg.ProjectSFID,
+ "organizationName": newGitHubOrg.OrganizationName,
+ "organizationSFID": newGitHubOrg.OrganizationSFID,
+ "organizationInstallationID": newGitHubOrg.OrganizationInstallationID,
+ "autoEnabled": newGitHubOrg.AutoEnabled,
+ "branchProtectionEnabled": newGitHubOrg.BranchProtectionEnabled,
+ "autoEnabledCLAGroupID": newGitHubOrg.AutoEnabledClaGroupID,
+ }
+
// Locate the repositories already saved under this organization
log.WithFields(f).Debugf("loading repositories under the organization : %s", newGitHubOrg.OrganizationName)
repos, err := s.repositoryService.GetRepositoriesByOrganizationName(context.Background(), newGitHubOrg.OrganizationName)
@@ -136,10 +152,10 @@ func (s *service) enableBranchProtectionForGithubOrg(f logrus.Fields, newGitHubO
return err
}
- ctx := context.Background()
log.WithFields(f).Debugf("creating a new GitHub client object for org: %s...", newGitHubOrg.OrganizationName)
gitHubClient, clientErr := githubutils.NewGithubAppClient(newGitHubOrg.OrganizationInstallationID)
if clientErr != nil {
+ log.WithFields(f).WithError(clientErr).Warnf("unable to create a new GitHub app client using the installation ID: %d", newGitHubOrg.OrganizationInstallationID)
return clientErr
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index cadc50f80..fc97349d8 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -29,7 +29,7 @@ import (
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
)
-// Service contains functions of Github Repositories service
+// Service contains functions of GitHub Repositories service
type Service interface {
AddGithubRepository(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) (*v1Models.GithubRepository, error)
EnableRepository(ctx context.Context, repositoryID string) error
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 54548d646..b2861b0e1 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -106,7 +106,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
@@ -449,7 +449,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewGetProjectCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
@@ -502,7 +502,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
@@ -589,7 +589,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewGetCompanySignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
@@ -745,7 +745,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
@@ -871,7 +871,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", *params.CompanyID, err)
log.Warn(msg)
- if errors.Is(err, company.ErrCompanyDoesNotExist) {
+ if _, ok := err.(*utils.CompanyNotFound); ok {
return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
Code: "404",
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index d67b7a219..6cb023f1a 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -123,7 +123,7 @@ func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGro
return nil, errors.New("not Found")
}
- b.WriteString(`Github ID,LF_ID,Name,Email,Date Signed`)
+ b.WriteString(`GitHub ID,LF_ID,Name,Email,Date Signed`)
for _, sig := range result.List {
b.WriteString(eclaSigCsvLine(sig))
}
@@ -136,7 +136,7 @@ func (s service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID str
if err != nil {
return nil, err
}
- b.WriteString(`Github ID,LF_ID,Name,Email,Date Signed`)
+ b.WriteString(`GitHub ID,LF_ID,Name,Email,Date Signed`)
for _, sig := range result.List {
b.WriteString(iclaSigCsvLine(sig))
}
From f0dbfc4861799c7b77152de4ac6f4e3b40b1dcd6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 28 Jan 2021 16:55:59 -0800
Subject: [PATCH 0021/1276] Updated Allow List Domain Validation - Allow
Wildcards
- Updated ValidDomain utility method to accept allowWildcards flag - determins if wildcards are allowed or not
- Updated unit tests for domain validation
Signed-off-by: David Deal
---
cla-backend-go/tests/utils_test.go | 36 ++++++++++++++++++++--
cla-backend-go/utils/utils.go | 30 +++++++++++++-----
cla-backend-go/v2/signatures/validators.go | 4 +--
3 files changed, 58 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/tests/utils_test.go b/cla-backend-go/tests/utils_test.go
index 8ed9fb5f0..7b62da95f 100644
--- a/cla-backend-go/tests/utils_test.go
+++ b/cla-backend-go/tests/utils_test.go
@@ -242,20 +242,52 @@ func TestValidDomain(t *testing.T) {
"slack.com",
"slack-domain-with-dash.com",
}
+
+ validWildcardDomains := []string{
+ "linuxfoundation.org",
+ "wikipedia.org",
+ "google.com",
+ "slack.com",
+ "slack-domain-with-dash.com",
+ "*.google.com",
+ "*.us.google.com",
+ }
+
inValidDomains := []string{
+ "*.google.com", // test case with allowWildcards = false
+ "linuxfoundation_org",
+ "*.linuxfoundation_org", // test case with allowWildcards = false
+ "/linuxfoundation.org",
+ "linuxfoundation+fun.org",
+ "user_linuxfoundation.org",
+ }
+
+ inWildcardValidDomains := []string{
"linuxfoundation_org",
"/linuxfoundation.org",
"linuxfoundation+fun.org",
+ "*.linuxfoundation+fun.org",
"user_linuxfoundation.org",
+ "*.user_linuxfoundation.org",
}
for _, domain := range validDomains {
- msg, valid := utils.ValidDomain(domain)
+ msg, valid := utils.ValidDomain(domain, false)
+ assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
+ }
+
+ for _, domain := range validWildcardDomains {
+ msg, valid := utils.ValidDomain(domain, true)
assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
}
for _, domain := range inValidDomains {
- msg, valid := utils.ValidDomain(domain)
+ msg, valid := utils.ValidDomain(domain, false)
+ assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
+ }
+
+ for _, domain := range inWildcardValidDomains {
+ msg, valid := utils.ValidDomain(domain, true)
assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
}
}
diff --git a/cla-backend-go/utils/utils.go b/cla-backend-go/utils/utils.go
index fbfb18feb..eb7a98be0 100644
--- a/cla-backend-go/utils/utils.go
+++ b/cla-backend-go/utils/utils.go
@@ -186,7 +186,7 @@ func ValidEmail(email string) bool {
}
// ValidDomain tests the specified domain string, returns true if domain is valid, returns false otherwise
-func ValidDomain(domain string) (string, bool) {
+func ValidDomain(domain string, allowWildcard bool) (string, bool) { // nolint
domain = strings.TrimSpace(domain)
switch {
@@ -213,14 +213,28 @@ func ValidDomain(domain string) (string, bool) {
l = i + 1
continue
}
- // test label character validity, note: tests are ordered by decreasing validity frequency
- if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b >= 'A' && b <= 'Z') {
- // show the printable unicode character starting at byte offset i
- c, _ := utf8.DecodeRuneInString(domain[i:])
- if c == utf8.RuneError {
- return fmt.Sprintf("invalid character at offset %d", i), false
+
+ // If wildcard domains are allowed, e.g. *.linuxfoundation.org
+ if allowWildcard {
+ // test label character validity, note: tests are ordered by decreasing validity frequency
+ if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '*' || b >= 'A' && b <= 'Z') {
+ // show the printable unicode character starting at byte offset i
+ c, _ := utf8.DecodeRuneInString(domain[i:])
+ if c == utf8.RuneError {
+ return fmt.Sprintf("invalid character at offset %d", i), false
+ }
+ return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
+ }
+ } else {
+ // test label character validity, note: tests are ordered by decreasing validity frequency
+ if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b >= 'A' && b <= 'Z') {
+ // show the printable unicode character starting at byte offset i
+ c, _ := utf8.DecodeRuneInString(domain[i:])
+ if c == utf8.RuneError {
+ return fmt.Sprintf("invalid character at offset %d", i), false
+ }
+ return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
}
- return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
}
}
diff --git a/cla-backend-go/v2/signatures/validators.go b/cla-backend-go/v2/signatures/validators.go
index c34d7bad3..47ba14750 100644
--- a/cla-backend-go/v2/signatures/validators.go
+++ b/cla-backend-go/v2/signatures/validators.go
@@ -58,14 +58,14 @@ func entriesAreValid(params signatures.UpdateApprovalListParams) (string, bool)
// Ensure the domains are valid
for _, domain := range params.Body.AddDomainApprovalList {
- msg, valid := utils.ValidDomain(domain)
+ msg, valid := utils.ValidDomain(domain, true)
if !valid {
isValid = false
listOfErrors = append(listOfErrors, fmt.Sprintf("invalid add approval list domain %s - %s", domain, msg))
}
}
for _, domain := range params.Body.RemoveDomainApprovalList {
- msg, valid := utils.ValidDomain(domain)
+ msg, valid := utils.ValidDomain(domain, true)
if !valid {
isValid = false
listOfErrors = append(listOfErrors, fmt.Sprintf("invalid remove approval list domain %s - %s", domain, msg))
From 7ddb5df3349036906b74e6a7e8f235fe59dbd88d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 29 Jan 2021 17:38:35 -0500
Subject: [PATCH 0022/1276] Added Auto-Create Signing Entity Name on Query
(#2545)
Signed-off-by: David Deal
---
cla-backend-go/company/service.go | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/cla-backend-go/company/service.go b/cla-backend-go/company/service.go
index 7fc942d04..bc3490840 100644
--- a/cla-backend-go/company/service.go
+++ b/cla-backend-go/company/service.go
@@ -710,6 +710,14 @@ func (s service) SearchOrganizationByName(ctx context.Context, orgName string, w
var signingEntityNames []string
if len(org.SigningEntityName) > 0 {
signingEntityNames = utils.TrimSpaceFromItems(org.SigningEntityName)
+ // Auto-create on-demand from SF
+ for _, signingEntityName := range signingEntityNames {
+ // By looking up the signing entity name in our own DB, we auto-create the record if it doesn't exist
+ _, lookupErr := s.GetCompanyBySigningEntityName(ctx, signingEntityName, org.ID)
+ if lookupErr != nil {
+ log.WithFields(f).WithError(lookupErr).Warnf("problem locating company record using signing entity name: %s with SFID: %s", signingEntityName, org.ID)
+ }
+ }
}
result.List = append(result.List, &models.Org{
OrganizationID: org.ID,
From 0bc5fffa22c93e73093381b1ea9367863992419e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 29 Jan 2021 18:50:11 -0500
Subject: [PATCH 0023/1276] Added Check Prepare Sigs Logic to Support Signing
Entity Name (#2546)
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 85 ++++++++++++++++++++++-
1 file changed, 82 insertions(+), 3 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index c712a7dd5..3006c6fbc 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -416,9 +416,88 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
project_id=project_id
)
if len(ccla_signatures) < 1:
- cla.log.warning(f'{fn} - project {project.get_project_name()} and '
- f'company {company.get_company_name()} does not have CCLA for: {request_info}')
- return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+ # Save our message
+ msg = (f'{fn} - project {project.get_project_name()} and '
+ f'company {company.get_company_name()} does not have CCLA for: {request_info}')
+
+ # Ok - long story here, we could have the tricky situation where now that we've added a concept of Signing
+ # Entity Names we have, basically, a set of 'child' companies all under a common external_id (SFID). This
+ # would have been so much simpler if SF supported Parent/Child company relationships to model things like
+ # Subsidiary and Patten holding companies.
+ #
+ # Scenario:
+ #
+ # Deal Company (SFID: 123, CompanyID: AAA)
+ # Deal Company Subsidiary 1 - (SFID: 123, CompanyID: BBB)
+ # Deal Company Subsidiary 2 - (SFID: 123, CompanyID: CCC) - SIGNED!
+ # Deal Company Subsidiary 3 - (SFID: 123, CompanyID: DDD)
+ # Deal Company Subsidiary 4 - (SFID: 123, CompanyID: EEE)
+ #
+ # Now - the check-prepare-employee signature request could have come from any of the above companies with
+ # different a company_id - the contributor may have selected the correct option (CCC), the one that was
+ # signed and executed by a Signatory...or maybe none have been signed...or perhaps another one was signed
+ # such as companyID BBB.
+ #
+ # Originally, we designed the system to keep track of all these sub-companies separately - different CLA
+ # managers, different approval lists, etc.
+ #
+ # Later, the stakeholders wanted to group these all together as one but keep track of the signing entity
+ # name for each project | company. They wanted to allow the users to select one for each (project |
+ # organization) pair.
+ #
+ # So, we could have CLA signatories/managers wanting:
+ #
+ # - Project OpenCue + Deal Company Subsidiary 2
+ # - Project OpenVDB + Deal Company Subsidiary 4
+ # - Project OpenTelemetry + Deal Company
+ #
+ # As a result, we need to query the entire company family under the same external_id for a signed CCLA.
+ # Currently, we only allow 1 of these to be signed for each Project | Company pair. Later, we may change
+ # this behavior (it's been debated).
+ #
+ # Let's see if they signed the CCLA for another of the Company/Signed Entity Names for this
+ # project - if so, let's return that one, if not, return the error
+
+ # First, grab the current company's external ID/SFID
+ company_external_id = company.get_company_external_id()
+ # if missing, not much we can do...
+ if company_external_id is None:
+ cla.log.warning(f'{fn} - project {project.get_project_name()} and '
+ f'company {company.get_company_name()} - company missing external id - '
+ f'{request_info}')
+ cla.log.warning(msg)
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+ # Lookup the other companies by external id...will have 1 or more (current record plus possibly others)...
+ company_list = company.get_company_by_external_id(company_external_id)
+ # This shouldn't happen, let's trap for it anyway
+ if len(company_list) == 0:
+ cla.log.warning(f'{fn} - project {project.get_project_name()} and '
+ f'company {company.get_company_name()} - unable to lookup companies by external id: '
+ f'{company_external_id} - {request_info}')
+ cla.log.warning(msg)
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+
+ # As we loop, let's use a flag to keep track if we find a CCLA
+ found_ccla = False
+ for other_company in company_list:
+ cla.log.debug(f'{fn} - loading CCLA signatures by cla group: {project.get_project_name()} '
+ f'and company id: {other_company.get_company_id()}...')
+ ccla_signatures = Signature().get_ccla_signatures_by_company_project(
+ company_id=other_company.get_company_id(),
+ project_id=project_id
+ )
+
+ # Do we have a signed CCLA for this project|company ? If so, we found it - use it! Should NOT have
+ # more than one of the companies with Signed CCLAs
+ if len(ccla_signatures) > 0:
+ found_ccla = True
+ break
+
+ # if we didn't fine a signed CCLA under any of the other companies...
+ if not found_ccla:
+ # Give up
+ cla.log.warning(msg)
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
# Add a note in the log if we have more than 1 signed and approved CCLA signature
if len(ccla_signatures) > 1:
From 921feafcf2948ef01432d88c92afd9dd303299d2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 29 Jan 2021 19:29:31 -0500
Subject: [PATCH 0024/1276] Resolved Unit Test Issue in Staging/Prod (#2547)
Signed-off-by: David Deal
---
.circleci/config.yml | 16 ++++++++++++----
cla-backend-go/tests/github_v4_test.go | 14 ++++++++++++--
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index b13471042..e62dbbb10 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -261,13 +261,21 @@ jobs:
- run:
name: Test
command: |
- cd cla-backend-go
- make test
+ if [[ "${STAGE}" == "dev" ]]; then
+ cd cla-backend-go
+ make test
+ else
+ echo "Skipping test - only runs in dev stage."
+ fi
- run:
name: Lint
command: |
- cd cla-backend-go
- make lint
+ if [[ "${STAGE}" == "dev" ]]; then
+ cd cla-backend-go
+ make lint
+ else
+ echo "Skipping lint - only runs in dev stage."
+ fi
- run:
name: Move Binary
command: |
diff --git a/cla-backend-go/tests/github_v4_test.go b/cla-backend-go/tests/github_v4_test.go
index 5a5555da4..1465288f6 100644
--- a/cla-backend-go/tests/github_v4_test.go
+++ b/cla-backend-go/tests/github_v4_test.go
@@ -5,6 +5,7 @@ package tests
import (
"fmt"
+ "os"
"strconv"
"testing"
@@ -21,8 +22,17 @@ func TestGetRepositoryIDFromName(t *testing.T) {
ctx := utils.NewContext()
// Need to initialize the system to load the configuration which contains a number of SSM parameters
- viper.Set("STAGE", "dev")
- viper.Set("DYNAMODB_AWS_REGION", "us-east-1")
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
ini.Init()
_, err := ini.GetAWSSession()
if err != nil {
From f267a36388da8dd4d700582ca00063c3af027ba9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 29 Jan 2021 19:53:41 -0500
Subject: [PATCH 0025/1276] Fixed CI/CD Tests - Updated python check-prepare
load company logic (#2548)
Signed-off-by: David Deal
---
.circleci/config.yml | 3 +++
cla-backend/cla/models/docusign_models.py | 16 ++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e62dbbb10..9dd32a7e2 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -300,6 +300,7 @@ jobs:
AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_DEV
AWS_PROFILE: lf-cla
AWS_REGION: us-east-1
+ DYNAMODB_AWS_REGION: us-east-1
buildGoBackendStaging:
<<: *buildGoBackendAnchor
@@ -309,6 +310,7 @@ jobs:
AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_STAGING
AWS_PROFILE: lf-cla
AWS_REGION: us-east-1
+ DYNAMODB_AWS_REGION: us-east-1
buildGoBackendProd:
<<: *buildGoBackendAnchor
@@ -318,6 +320,7 @@ jobs:
AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_PROD
AWS_PROFILE: lf-cla
AWS_REGION: us-east-1
+ DYNAMODB_AWS_REGION: us-east-1
# Deploys
deployBackend: &deployBackendAnchor
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 3006c6fbc..166beb296 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -491,6 +491,22 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
# more than one of the companies with Signed CCLAs
if len(ccla_signatures) > 0:
found_ccla = True
+ # Need to load the correct company record
+ try:
+ company_id = ccla_signatures[0].get_signature_reference_id()
+ cla.log.debug(f'{fn} - loading correct signed CCLA company by id: '
+ f'{ccla_signatures[0].get_signature_reference_id()} '
+ f'with signed entity name: {ccla_signatures[0].get_signing_entity_name()} ...')
+ company.load(ccla_signatures[0].get_signature_reference_id())
+ cla.log.debug(f'{fn} - loaded company {company.get_company_name()} '
+ f'with signing entity name: {company.get_signing_entity_name()} '
+ f'for {request_info}.')
+ except DoesNotExist:
+ cla.log.warning(f'{fn} - company does NOT exist '
+ f'using company_id: {ccla_signatures[0].get_signature_reference_id()} '
+ f'for: {request_info}')
+ return {'errors': {'company_id': f'Company ({ccla_signatures[0].get_signature_reference_id()}) '
+ 'does not exist.'}}
break
# if we didn't fine a signed CCLA under any of the other companies...
From a3d36dfea99771b946797faf2f55bbbbb1a3111e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Feb 2021 01:17:11 -0500
Subject: [PATCH 0026/1276] Updated check_and_prepare_employee_signature logic
(#2549)
- Updated to set/reset the company_id based on if the signing entity
name was correctly specified by the employee
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 27 +++++++++++++++--------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 166beb296..b4dcc91b0 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -370,7 +370,10 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
# Returns an error if any of the above is false.
fn = 'docusign_models.check_and_prepare_employee_signature'
- request_info = f'project: {project_id}, company: {company_id}, user: {user_id}'
+ # Keep a variable with the actual company_id - may swap the original selected company id to use another
+ # company id if another signing entity name (another related company) is already signed
+ actual_company_id = company_id
+ request_info = f'project: {project_id}, company: {actual_company_id}, user: {user_id}'
cla.log.info(f'{fn} - check and prepare employee signature for {request_info}')
# Ensure the project exists
@@ -386,12 +389,12 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
# Ensure the company exists
company = Company()
try:
- cla.log.debug(f'{fn} - loading company by id: {company_id}...')
- company.load(str(company_id))
+ cla.log.debug(f'{fn} - loading company by id: {actual_company_id}...')
+ company.load(str(actual_company_id))
cla.log.debug(f'{fn} - company {company.get_company_name()} exists for: {request_info}')
except DoesNotExist:
cla.log.warning(f'{fn} - company does NOT exist for: {request_info}')
- return {'errors': {'company_id': f'Company ({company_id}) does not exist.'}}
+ return {'errors': {'company_id': f'Company ({actual_company_id}) does not exist.'}}
# Ensure the user exists
user = User()
@@ -493,7 +496,11 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
found_ccla = True
# Need to load the correct company record
try:
- company_id = ccla_signatures[0].get_signature_reference_id()
+ # Reset the actual company id value since we found a CCLA under a related signing entity name
+ # company
+ actual_company_id = ccla_signatures[0].get_signature_reference_id()
+ # Reset the request_info string with the updated company_id, will use it for debug/warning below
+ request_info = f'project: {project_id}, company: {actual_company_id}, user: {user_id}'
cla.log.debug(f'{fn} - loading correct signed CCLA company by id: '
f'{ccla_signatures[0].get_signature_reference_id()} '
f'with signed entity name: {ccla_signatures[0].get_signing_entity_name()} ...')
@@ -535,10 +542,12 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
cla.log.info(f'{fn} - user is approved for this CCLA: {request_info}')
- # Assume this company is the user's employer.
+ # Assume this company is the user's employer. Associated the company with the user in the EasyCLA user record
+ # For v2, we make the association with the platform via the platform project service via a separate API
+ # call from the UI
# TODO: DAD - we should check to see if they already have a company id assigned
- if user.get_user_company_id() != company_id:
- user.set_user_company_id(str(company_id))
+ if user.get_user_company_id() != actual_company_id:
+ user.set_user_company_id(str(actual_company_id))
event_data = (f'The user {user.get_user_name()} with GitHub username '
f'{user.get_github_username()} ('
f'{user.get_user_github_id()}) and user ID '
@@ -551,7 +560,7 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
f'project {project.get_project_name()}.')
Event.create_event(
event_type=EventType.UserAssociatedWithCompany,
- event_company_id=company_id,
+ event_company_id=actual_company_id,
event_company_name=company.get_company_name(),
event_project_id=project_id,
event_project_name=project.get_project_name(),
From b85931e3f67b2b08c05361cea4fa493f0f1e25ff Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Mon, 1 Feb 2021 09:17:48 +0300
Subject: [PATCH 0027/1276] [#2544] Bug/Update Email approval (#2550)
- Changed email content for approval list emails for contributor
Signed-off-by: wanyaland
---
cla-backend-go/signatures/service.go | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index fa13c9160..757253e6d 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -611,19 +611,23 @@ func (s service) getRemoveGitHubContributors(approvalList *models.ApprovalList)
func (s service) sendRequestAccessEmailToContributors(authUser *auth.User, companyModel *models.Company, claGroupModel *models.ClaGroup, approvalList *models.ApprovalList) {
addEmailUsers := s.getAddEmailContributors(approvalList)
for _, user := range addEmailUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to", "you are authorized to contribute to")
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to",
+ fmt.Sprintf("you are authorized to contribute to %s on behalf of %s", claGroupModel.ProjectName, companyModel.CompanyName))
}
removeEmailUsers := s.getRemoveEmailContributors(approvalList)
for _, user := range removeEmailUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from", "you are no longer authorized to contribute to")
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from",
+ fmt.Sprintf("you are no longer authorized to contribute to %s on behalf of %s ", claGroupModel.ProjectName, companyModel.CompanyName))
}
addGitHubUsers := s.getAddGitHubContributors(approvalList)
for _, user := range addGitHubUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to", "you are authorized to contribute to")
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to",
+ fmt.Sprintf("you are authorized to contribute to %s on behalf of %s", claGroupModel.ProjectName, companyModel.CompanyName))
}
removeGitHubUsers := s.getRemoveGitHubContributors(approvalList)
for _, user := range removeGitHubUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from", "you are no longer authorized to contribute to")
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from",
+ fmt.Sprintf("you are no longer authorized to contribute to %s on behalf of %s ", claGroupModel.ProjectName, companyModel.CompanyName))
}
}
@@ -817,12 +821,12 @@ func sendRequestAccessEmailToContributorRecipient(authUser *auth.User, companyMo
Hello %s,
This is a notification email from EasyCLA regarding the project %s.
You have been %s %s the Approval List of %s for %s by CLA Manager %s. This means that %s on behalf of %s.
-
If you had previously submitted one or more pull requests to %s that had failed, you should
-close and re-open the pull request to force a recheck by the EasyCLA system.
+
If you had previously submitted a pull request to EasyCLA Test Group that had failed,
+you can now go back to it and follow the link to verify with your organization.
%s
%s`,
recipientName, projectName, addRemove, toFrom,
- companyName, projectName, authUser.UserName, authorizedString, projectName, projectName,
+ companyName, projectName, authUser.UserName, authorizedString, projectName,
utils.GetEmailHelpContent(claGroupModel.Version == utils.V2), utils.GetEmailSignOffContent())
err := utils.SendEmail(subject, body, recipients)
From 261c5eee7f327f6d9ff2e449137d1ce35e084631 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Mon, 1 Feb 2021 16:18:15 +0200
Subject: [PATCH 0028/1276] check when 409 is returned for role assignment on
company creation
Signed-off-by: makkalot
---
cla-backend-go/v2/organization-service/client.go | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/organization-service/client.go b/cla-backend-go/v2/organization-service/client.go
index a0276e7c8..8ffcb077a 100644
--- a/cla-backend-go/v2/organization-service/client.go
+++ b/cla-backend-go/v2/organization-service/client.go
@@ -91,7 +91,11 @@ func (osc *Client) CreateOrgUserRoleOrgScope(ctx context.Context, emailID string
result, err := osc.cl.Organizations.CreateOrgUsrRoleScopes(params, clientAuth)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to assign user to organization")
- return err
+ _, ok := err.(*organizations.CreateOrgUsrRoleScopesConflict)
+ if !ok {
+ return err
+ }
+ log.WithFields(f).Warn("the role already assigned for the user skipping")
}
log.WithFields(f).Debugf("Successfully assigned user to organization, result: %#v", result)
From fec31e976bfbffc1f7b03334536efdd5ce8c0fe2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Feb 2021 13:11:19 -0500
Subject: [PATCH 0029/1276] Resolved empty list check for python (#2553)
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index b4dcc91b0..73fff4597 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -473,7 +473,7 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
# Lookup the other companies by external id...will have 1 or more (current record plus possibly others)...
company_list = company.get_company_by_external_id(company_external_id)
# This shouldn't happen, let's trap for it anyway
- if len(company_list) == 0:
+ if not company_list:
cla.log.warning(f'{fn} - project {project.get_project_name()} and '
f'company {company.get_company_name()} - unable to lookup companies by external id: '
f'{company_external_id} - {request_info}')
From a698e004cb544312f2bbb564a90396c3feb6a47e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Feb 2021 13:56:12 -0500
Subject: [PATCH 0030/1276] Updatd Get External ID to return list (#2554)
- Updated error response model with more details
- Updated API to return list for get external company id
- Updated test scenario logic
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 8 +++++++-
cla-backend/cla/models/dynamo_models.py | 5 +++--
cla-backend/helpers/create_signatures.py | 5 ++++-
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 73fff4597..d8781fffb 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -538,7 +538,13 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
if not user.is_approved(ccla_signature):
# TODO: DAD - update this warning message
cla.log.warning(f'{fn} - user is not authorized for this CCLA: {request_info}')
- return {'errors': {'ccla_approval_list': 'user not authorized for this ccla'}}
+ return {'errors': {'ccla_approval_list': 'user not authorized for this ccla',
+ 'company_id': actual_company_id,
+ 'company_name': company.get_company_name(),
+ 'signing_entity_name': company.get_signing_entity_name(),
+ 'company_external_id': company.get_company_external_id(),
+ }
+ }
cla.log.info(f'{fn} - user is approved for this CCLA: {request_info}')
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index a433712ba..b7aa12a16 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -3346,11 +3346,12 @@ def get_company_by_id(self, company_id: str):
def get_company_by_external_id(self, company_external_id: str):
company_generator = self.model.company_external_id_index.query(company_external_id)
+ companies = []
for company_model in company_generator:
company = Company()
company.model = company_model
- return company
- return None
+ companies.append(company)
+ return companies
def all(self, ids: List[str] = None):
if ids is None:
diff --git a/cla-backend/helpers/create_signatures.py b/cla-backend/helpers/create_signatures.py
index 5967d1e73..347b4222b 100644
--- a/cla-backend/helpers/create_signatures.py
+++ b/cla-backend/helpers/create_signatures.py
@@ -20,7 +20,10 @@
user = get_user_instance().get_user_by_github_id(USER_GITHUB_ID)
project1 = get_project_instance().get_projects_by_external_id(PROJECT_EXTERNAL_ID1)[0]
project2 = get_project_instance().get_projects_by_external_id(PROJECT_EXTERNAL_ID2)[0]
-company = get_company_instance().get_company_by_external_id(COMPANY_EXTERNAL_ID)
+company_list = get_company_instance().get_company_by_external_id(COMPANY_EXTERNAL_ID)
+company = None
+if company_list:
+ company = company_list[0]
# Test ICLA Agreement.
sig_id = str(uuid.uuid4())
From 00b92b0f1a58ffc8922b33e956e8a72bd294d071 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Feb 2021 16:54:33 -0500
Subject: [PATCH 0031/1276] Updated check_and_prepare_employee_signature error
response models (#2555)
- Response models now include additional informaiton on the company id, name, signing entity name, and external id
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 25 ++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index d8781fffb..96b586f10 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -469,7 +469,14 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
f'company {company.get_company_name()} - company missing external id - '
f'{request_info}')
cla.log.warning(msg)
- return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project.',
+ 'company_id': actual_company_id,
+ 'company_name': company.get_company_name(),
+ 'signing_entity_name': company.get_signing_entity_name(),
+ 'company_external_id': company.get_company_external_id(),
+ }
+ }
+
# Lookup the other companies by external id...will have 1 or more (current record plus possibly others)...
company_list = company.get_company_by_external_id(company_external_id)
# This shouldn't happen, let's trap for it anyway
@@ -478,7 +485,13 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
f'company {company.get_company_name()} - unable to lookup companies by external id: '
f'{company_external_id} - {request_info}')
cla.log.warning(msg)
- return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project.',
+ 'company_id': actual_company_id,
+ 'company_name': company.get_company_name(),
+ 'signing_entity_name': company.get_signing_entity_name(),
+ 'company_external_id': company.get_company_external_id(),
+ }
+ }
# As we loop, let's use a flag to keep track if we find a CCLA
found_ccla = False
@@ -520,7 +533,13 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
if not found_ccla:
# Give up
cla.log.warning(msg)
- return {'errors': {'missing_ccla': 'Company does not have CCLA with this project'}}
+ return {'errors': {'missing_ccla': 'Company does not have CCLA with this project.',
+ 'company_id': actual_company_id,
+ 'company_name': company.get_company_name(),
+ 'signing_entity_name': company.get_signing_entity_name(),
+ 'company_external_id': company.get_company_external_id(),
+ }
+ }
# Add a note in the log if we have more than 1 signed and approved CCLA signature
if len(ccla_signatures) > 1:
From bdf2de850492c44a06aa75a48aae95436773f782 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 2 Feb 2021 01:21:57 +0300
Subject: [PATCH 0032/1276] [#2539] Bug/ Project Multiple GH orgs (#2556)
- Resolved issue caused by return of TLF GH orgs
Signed-off-by: wanyaland
---
cla-backend-go/github_organizations/repository.go | 2 +-
cla-backend-go/v2/github_organizations/service.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 6340e69c3..a6ef3adc4 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -238,7 +238,7 @@ func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID s
func (repo repository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganizations",
+ "functionName": "GetGithubOrganizationsByParent",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
}
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index c469257e0..51a0b11ec 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,7 +85,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if projectServiceRecord.Parent == "" || projectServiceRecord.Parent == utils.TheLinuxFoundation {
+ if projectServiceRecord.Foundation != nil && projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
From 55be850c9c489e7d9b6ed951d7e32919ba55986b Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Tue, 2 Feb 2021 13:03:51 +0300
Subject: [PATCH 0033/1276] [#2427,#2558] Bug/Invite Email Cla Manager
- Updated email sent to org admin with appropriate v2 link (corporate console)
- Updated email content sent to user with no LFID in the invite workflow
Signed-off-by: wanyaland
---
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index f62cba994..64020c4c6 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -270,7 +270,7 @@ func server(localMode bool) http.Handler {
v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
v2ClaManagerService := v2ClaManager.NewService(v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
- v1ApprovalListService := approval_list.NewService(approvalListRepo, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleURL, http.DefaultClient)
+ v1ApprovalListService := approval_list.NewService(approvalListRepo, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 580bf3cb8..a1ecc83ba 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -1377,12 +1377,12 @@ func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, requesterUsername, r
This email will guide you to completing the CLA Manager role assignment
1. Accept Invite link below will take you SSO login page where you can login with your LF Login or create a LF Login and then login.
2. After logging in SSO screen should direct you to CLA Corporate Console page where you will see the project you a re associated with.
-
3. Click on workflow steps to complete the signup process. Please follow this documentation to help you guide through the process - https://docs.linuxfoundation.org/lfx/easycla/ccla-managers-and-ccla-signatories
+
3. Click on workflow steps to complete the signup process. Please follow this documentation to help you guide through the process - https://docs.linuxfoundation.org/lfx/v/v2/easycla/corporate-cla-manager-designee-or-initial-cla-manager/sign-corporate-cla-for-a-company
4. Once you have completed CLA Manager workflow you will be able to manage the approved list of contributors
This is a notification email from EasyCLA regarding the project %s.
-
%s (%s) has requested to be added to the Approved List as an authorized contributor from
-%s to the project %s. You are receiving this message as a CLA Manager from %s for
-%s.
-%s
-
If you want to add them to the Approved List, please
-log into the EasyCLA Corporate
-Console, where you can approve this user's request by selecting the 'Manage Approved List' and adding the
-contributor's email, the contributor's entire email domain, their GitHub ID or the entire GitHub Organization for the
-repository. This will permit them to begin contributing to %s on behalf of %s.
-
If you are not certain whether to add them to the Approved List, please reach out to them directly to discuss.
"
-
+ emailCLAManagerParams := []emails.ClaManagerInfoParams{}
// Build a fancy text string with CLA Manager name as an HTML unordered list
for _, manager := range signature.SignatureACL {
@@ -318,29 +312,32 @@ func (s service) sendRequestRejectedEmailToRecipient(companyModel *models.Compan
if whichEmail == "" {
log.Warnf("unable to send email to manager: %+v - no email on file...", manager)
} else {
- claManagerText += fmt.Sprintf("
"
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: Approval List Request Denied for Project %s", projectName)
recipients := []string{recipientAddress}
- body := fmt.Sprintf(`
-
Hello %s,
-
This is a notification email from EasyCLA regarding the project %s.
-
Your request to get added to the approval list from %s for %s was denied by one of the existing CLA Managers.
-If you have further questions about this denial, please contact one of the existing CLA Managers from
-%s for %s:
This is a notification email from EasyCLA regarding the project %s.
-
You are currently listed as a CLA Manager from %s for the project %s. This means that you are able to maintain the
-list of employees allowed to contribute to %s on behalf of your company, as well as view and manage the list of
-your company’s CLA Managers for %s.
-
%s (%s) has requested to be added as another CLA Manager from %s for %s. This would permit them to maintain the
-lists of approved contributors and CLA Managers as well.
-
If you want to permit this, please log into the EasyCLA Corporate Console,
-select your company, then select the %s project. From the CLA Manager requests, you can approve this user as an
-additional CLA Manager.
This is a notification email from EasyCLA regarding the project %s.
-
The following user has been approved as a CLA Manager from %s for the project %s. This means that they can now
-maintain the list of employees allowed to contribute to %s on behalf of your company, as well as view and manage the
-list of company’s CLA Managers for %s.
This is a notification email from EasyCLA regarding the project %s.
-
You have now been approved as a CLA Manager from %s for the project %s. This means that you can now maintain the
-list of employees allowed to contribute to %s on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for %s.
-
To get started, please log into the EasyCLA Corporate Console, and select your
-company and then the project %s. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the project %s.
-
The following user has been denied as a CLA Manager from %s for the project %s. This means that they will not
-be able to maintain the list of employees allowed to contribute to %s on behalf of your company.
This is a notification email from EasyCLA regarding the project %s.
-
You have been denied as a CLA Manager from %s for the project %s. This means that you can not maintain the
-list of employees allowed to contribute to %s on behalf of your company.
This is a notification email from EasyCLA regarding the project %s.
-
You have been added as a CLA Manager from %s for the project %s. This means that you can now maintain the
-list of employees allowed to contribute to %s on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for %s.
-
To get started, please log into the EasyCLA Corporate Console, and select your
-company and then the project %s. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the project %s.
-
The following user has been added as a CLA Manager from %s for the project %s. This means that they can now
-maintain the list of employees allowed to contribute to %s on behalf of your company, as well as view and manage the
-list of company’s CLA Managers for %s.
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
Your request to get added to the approval list from {{.CompanyName}} for {{.ProjectName}} was denied by one of the existing CLA Managers.
+If you have further questions about this denial, please contact one of the existing CLA Managers from
+{{.CompanyName}} for {{.CompanyName}}:
+
+ {{range .CLAManagers}}
+
{{.LfUsername}} {{.Email}}
+ {{end}}
+
+`
+)
+
+// RequestToAuthorizeTemplateParams is email params for RequestToAuthorizeTemplate
+type RequestToAuthorizeTemplateParams struct {
+ CLAManagerTemplateParams
+ ContributorName string
+ ContributorEmail string
+ OptionalMessage string
+ CorporateConsoleURL string
+ CompanyID string
+}
+
+const (
+ // RequestToAuthorizeTemplateName is email template name for RequestToAuthorizeTemplate
+ RequestToAuthorizeTemplateName = "RequestToAuthorizeTemplate"
+ // RequestToAuthorizeTemplate is email template for
+ RequestToAuthorizeTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}}.
+
{{.ContributorName}} ({{.ContributorEmail}}) has requested to be added to the Approved List as an authorized contributor from
+{{.CompanyName}} to the project {{.ProjectName}}. You are receiving this message as a CLA Manager from {{.CompanyName}} for
+{{.ProjectName}}.
+{{if .OptionalMessage}}
+
{{.ContributorName}} included the following message in the request:
+
{{.OptionalMessage}}
+{{end}}
+
If you want to add them to the Approved List, please
+log into the EasyCLA Corporate
+Console, where you can approve this user's request by selecting the 'Manage Approved List' and adding the
+contributor's email, the contributor's entire email domain, their GitHub ID or the entire GitHub Organization for the
+repository. This will permit them to begin contributing to {{.ProjectName}} on behalf of {{.CompanyName}}.
+
If you are not certain whether to add them to the Approved List, please reach out to them directly to discuss.
+`
+)
+
+// RequestApprovedToCLAManagersTemplateParams is email params for RequestApprovedToCLAManagersTemplate
+type RequestApprovedToCLAManagersTemplateParams struct {
+ CLAManagerTemplateParams
+ RequesterName string
+ RequesterEmail string
+}
+
+const (
+ // RequestApprovedToCLAManagersTemplateName is email template name for RequestApprovedToCLAManagersTemplate
+ RequestApprovedToCLAManagersTemplateName = "RequestApprovedToCLAManagersTemplateName"
+ // RequestApprovedToCLAManagersTemplate is email template for
+ RequestApprovedToCLAManagersTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
The following user has been approved as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they can now
+maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the
+list of company’s CLA Managers for {{.ProjectName}}.
+
+
{{.RequesterName}} ({{.RequesterEmail}})
+
+`
+)
+
+// RequestApprovedToRequesterTemplateParams email template params for RequestApprovedToRequesterTemplate
+type RequestApprovedToRequesterTemplateParams struct {
+ CLAManagerTemplateParams
+ CorporateURL string
+}
+
+const (
+ // RequestApprovedToRequesterTemplateName is email template name for RequestApprovedToRequesterTemplate
+ RequestApprovedToRequesterTemplateName = "RequestApprovedToRequesterTemplate"
+ // RequestApprovedToRequesterTemplate is email template for
+ RequestApprovedToRequesterTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
You have now been approved as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can now maintain the
+list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the list of your
+company’s CLA Managers for {{.ProjectName}}.
+
To get started, please log into the EasyCLA Corporate Console, and select your
+company and then the project {{.ProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
+`
+)
+
+// RequestDeniedToCLAManagersTemplateParams is email params for RequestDeniedToCLAManagersTemplate
+type RequestDeniedToCLAManagersTemplateParams struct {
+ CLAManagerTemplateParams
+ RequesterName string
+ RequesterEmail string
+}
+
+const (
+ // RequestDeniedToCLAManagersTemplateName is email template name for RequestDeniedToCLAManagersTemplate
+ RequestDeniedToCLAManagersTemplateName = "RequestDeniedToCLAManagersTemplate"
+ // RequestDeniedToCLAManagersTemplate is email template for
+ RequestDeniedToCLAManagersTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
The following user has been denied as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they will not
+be able to maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company.
+
+
{{.RequesterName}} ({{.RequesterEmail}})
+
+`
+)
+
+// RequestDeniedToRequesterTemplateParams is email params for RequestDeniedToRequesterTemplate
+type RequestDeniedToRequesterTemplateParams struct {
+ CLAManagerTemplateParams
+}
+
+const (
+ // RequestDeniedToRequesterTemplateName is email template name for RequestDeniedToRequesterTemplate
+ RequestDeniedToRequesterTemplateName = "RequestDeniedToRequesterTemplate"
+ // RequestDeniedToRequesterTemplate is email template for
+ RequestDeniedToRequesterTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
You have been denied as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can not maintain the
+list of employees allowed to contribute to {{.ProjectName}} on behalf of your company.
+`
+)
+
+// ClaManagerAddedEToUserTemplateParams is email params for ClaManagerAddedEToUserTemplate
+type ClaManagerAddedEToUserTemplateParams struct {
+ CLAManagerTemplateParams
+ CorporateURL string
+}
+
+const (
+ // ClaManagerAddedEToUserTemplateName is email template name for ClaManagerAddedEToUserTemplate
+ ClaManagerAddedEToUserTemplateName = "ClaManagerAddedEToUserTemplate"
+ // ClaManagerAddedEToUserTemplate is email template for
+ ClaManagerAddedEToUserTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can now maintain the
+list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the list of your
+company’s CLA Managers for {{.ProjectName}}.
+
To get started, please log into the EasyCLA Corporate Console, and select your
+company and then the project {{.ProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
+`
+)
+
+// ClaManagerAddedToCLAManagersTemplateParams is email params for ClaManagerAddedToCLAManagersTemplate
+type ClaManagerAddedToCLAManagersTemplateParams struct {
+ CLAManagerTemplateParams
+ Name string
+ Email string
+}
+
+const (
+ // ClaManagerAddedToCLAManagersTemplateName is email template name for ClaManagerAddedToCLAManagersTemplate
+ ClaManagerAddedToCLAManagersTemplateName = "ClaManagerAddedToCLAManagersTemplate"
+ // ClaManagerAddedToCLAManagersTemplate is email template for
+ ClaManagerAddedToCLAManagersTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
The following user has been added as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they can now
+maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the
+list of company’s CLA Managers for {{.ProjectName}}.
+
+
{{.Name}} ({{.Email}})
+
+`
+)
+
+// ClaManagerDeletedToCLAManagersTemplateParams is template params for ClaManagerDeletedToCLAManagersTemplate
+type ClaManagerDeletedToCLAManagersTemplateParams struct {
+ CLAManagerTemplateParams
+ Name string
+ Email string
+}
+
+const (
+ // ClaManagerDeletedToCLAManagersTemplateName is template name for ClaManagerDeletedToCLAManagersTemplate
+ ClaManagerDeletedToCLAManagersTemplateName = "ClaManagerDeletedToCLAManagersTemplate"
+ // ClaManagerDeletedToCLAManagersTemplate is template for
+ ClaManagerDeletedToCLAManagersTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
+
{{.Name}} ({{.Email}}) has been removed as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}.
+`
+)
diff --git a/cla-backend-go/emails/cla_manager_templates_test.go b/cla-backend-go/emails/cla_manager_templates_test.go
new file mode 100644
index 000000000..0d962af55
--- /dev/null
+++ b/cla-backend-go/emails/cla_manager_templates_test.go
@@ -0,0 +1,233 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package emails
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRemovedCLAManagerTemplate(t *testing.T) {
+ params := RemovedCLAManagerTemplateParams{
+ CLAManagerTemplateParams: CLAManagerTemplateParams{
+ RecipientName: "JohnsClaManager",
+ ProjectName: "JohnsProject",
+ ExternalProjectName: "JohnsProjectExternal",
+ CompanyName: "JohnsCompany",
+ CLAManagers: []ClaManagerInfoParams{
+ {LfUsername: "LFUserName", Email: "LFEmail"},
+ },
+ },
+ }
+
+ result, err := RenderTemplate(utils.V1, RemovedCLAManagerTemplateName, RemovedCLAManagerTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello JohnsClaManager")
+ assert.Contains(t, result, "regarding the project JohnsProject")
+ assert.Contains(t, result, "CLA Manager from JohnsCompany for the project JohnsProject")
+ assert.Contains(t, result, "
This is a notification email from EasyCLA regarding the Project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}} in the EasyCLA system.
+
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for Project {{.ProjectName}} but was unable to identify your account details in
+the EasyCLA system. In order to become a CLA Manager for Project {{.ProjectName}}, you will need to accept invite below.
+Once complete, notify the user {{.RequesterUserName}} and they will be able to add you as a CLA Manager.
This is a notification email from EasyCLA regarding the organization %s.
-
The following contributor would like to submit a contribution to the %s CLA Group
- and is requesting to be approved as a contributor for your organization:
-
%s - Signing Entity Name: %s
-
Approval can be done at %s
-
Please notify the contributor once they are added so that they may complete the contribution process.
This is a notification email from EasyCLA regarding the CLA setup and signing process for %s.
-
%s %s has identified you as a potential candidate to setup the Corporate CLA for %s in support of the following projects:
-%s
-
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom to designate from your company can login to this portal (%s) and sign the CLA for this project %s
-
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
-
Please notify the user once CLA setup is complete.
-%s
-%s`,
- admin, company, senderName, senderEmail, company, projectList, corporateConsole, projectNames[0],
- utils.GetEmailHelpContent(true), utils.GetEmailSignOffContent())
-
- err := utils.SendEmail(subject, body, recipients)
+ body, err := emails.RenderTemplate(
+ utils.V2, emails.V2OrgAdminTemplateName,
+ emails.V2OrgAdminTemplate,
+ emails.V2OrgAdminTemplateParams{
+ CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
+ RecipientName: admin,
+ CompanyName: company,
+ ProjectName: projectNames[0],
+ },
+ SenderName: senderName,
+ SenderEmail: senderEmail,
+ ProjectList: projectNames,
+ CorporateConsole: corporateConsole,
+ },
+ )
+ if err != nil {
+ log.Warnf("rendering email template : %s failed : %v", emails.V2OrgAdminTemplateName, err)
+ return
+ }
+ err = utils.SendEmail(subject, body, recipients)
if err != nil {
log.Warnf("problem sending email with subject: %s to recipients: %+v, error: %+v", subject, recipients, err)
} else {
@@ -1266,20 +1268,24 @@ Either you or someone whom to designate from your company can login to this port
func contributorEmailToOrgAdmin(adminEmail string, admin string, company string, projectNames []string, contributor *v1Models.User, corporateConsole string) {
subject := fmt.Sprintf("EasyCLA: Invitation to Sign the %s Corporate CLA and add to approved list %s ", company, getBestUserName(contributor))
recipients := []string{adminEmail}
- body := fmt.Sprintf(`
-
Hello %s,
-
This is a notification email from EasyCLA regarding the project(s) %s.
-
The following contributor is requesting to sign CLA for organization:
-
%s
-
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal %s and sign the CLA for any of the projects %s.
-
Please notify the contributor once they are added so that they may complete the contribution process.
This is a notification email from EasyCLA regarding the CLA setup and signing process for %s.
-
%s %s has identified you as a potential candidate to setup the Corporate CLA for %s in support of the following projects:
-%s
-
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom to designate from your company can login to this portal (%s) and sign the CLA for this project %s
-
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
-
Please notify the user once CLA setup is complete.
User %s (%s) was trying to add you as a CLA Manager for Project %s and Company %s but was unable to identify your account details in the EasyCLA system
-
This email will guide you to completing the CLA Manager role assignment
-
1. Accept Invite link below will take you SSO login page where you can login with your LF Login or create a LF Login and then login.
-
2. After logging in SSO screen should direct you to CLA Corporate Console page where you will see the project you a re associated with.
-
3. Click on workflow steps to complete the signup process. Please follow this documentation to help you guide through the process - https://docs.linuxfoundation.org/lfx/v/v2/easycla/corporate-cla-manager-designee-or-initial-cla-manager/sign-corporate-cla-for-a-company
-
4. Once you have completed CLA Manager workflow you will be able to manage the approved list of contributors
- %s
- %s
- `, userWithNoLFIDName, requesterUsername, requesterEmail, projectName, organizationName,
- utils.GetEmailHelpContent(true), utils.GetEmailSignOffContent())
+ body, err := emails.RenderV2DesigneeToUserWithNoLFIDTemplate(repository, *projectID,
+ emails.V2DesigneeToUserWithNoLFIDTemplateParams{
+ CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
+ RecipientName: userWithNoLFIDName,
+ ProjectName: projectName,
+ CompanyName: organizationName,
+ },
+ RequesterUserName: requesterUsername,
+ RequesterEmail: requesterEmail,
+ })
+
+ if err != nil {
+ log.Warnf("rendering template : %s failed : %v", emails.V2DesigneeToUserWithNoLFIDTemplateName, err)
+ return err
+ }
+
acsClient := v2AcsService.GetClient()
automate := false
log.WithFields(f).Debug("sending user invite request...")
@@ -1395,7 +1406,7 @@ func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, requesterUsername, r
}
// sendEmailToUserWithNoLFID helper function to send email to a given user with no LFID
-func sendEmailToUserWithNoLFID(ctx context.Context, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error {
+func sendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error {
f := logrus.Fields{
"functionName": "cla_manager.service.sendEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1411,20 +1422,12 @@ func sendEmailToUserWithNoLFID(ctx context.Context, projectName, requesterUserna
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager with %s role", role)
- body := fmt.Sprintf(`
-
Hello %s,
-
This is a notification email from EasyCLA regarding the Project %s in the EasyCLA system.
-
User %s (%s) was trying to add you as a CLA Manager for Project %s but was unable to identify your account details in
-the EasyCLA system. In order to become a CLA Manager for Project %s, you will need to accept invite below.
-Once complete, notify the user %s and they will be able to add you as a CLA Manager.
Please notify the contributor once they are added so that they may complete the contribution process.
+
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
`
)
@@ -79,7 +79,7 @@ const (
{{.UserDetails}}
Before the user contribution can be accepted, your organization must sign a CLA.
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the projects {{range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}.
-
Please notify the contributor once they are added so that they may complete the contribution process.
+
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
`
)
@@ -141,7 +141,7 @@ const (
{{.ContributorID}} ({{.ContributorName}})
Before the user contribution can be accepted, your organization must sign a CLA.
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s) {{ range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}.
-
Please notify the contributor once they are added so that they may complete the contribution process.
+
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
`
}
From 8d26c7eacda9b2ea2bfdd74e7c23843422437108 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 10 Feb 2021 18:20:35 +0300
Subject: [PATCH 0070/1276] [#2611] Bug/Get GH orgs for given Project (#2613)
- Resolved not found response for SF Projects of type Project Group
Signed-off-by: wanyaland
---
cla-backend-go/v2/github_organizations/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 51a0b11ec..a80e133ca 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,7 +85,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if projectServiceRecord.Foundation != nil && projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation {
+ if (projectServiceRecord.Foundation != nil && projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation) || projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
From 07e7b169f81921dd3cdc357833b547b08bc0e2c9 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 10 Feb 2021 18:46:51 +0300
Subject: [PATCH 0071/1276] [#2612] Bug/Sign Flow (#2614)
- Resolved repository issue caused in the corporate sign flow
Signed-off-by: wanyaland
---
cla-backend-go/repositories/repository.go | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index 293f807e0..1c60c73d3 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -150,7 +150,6 @@ func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string,
func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
externalID := utils.StringValue(input.RepositoryExternalID)
- projectSFID := utils.StringValue(input.RepositoryProjectID)
repositoryName := utils.StringValue(input.RepositoryName)
repositoryOrganizationName := utils.StringValue(input.RepositoryOrganizationName)
repositoryType := utils.StringValue(input.RepositoryType)
@@ -161,7 +160,6 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
"externalProjectID": externalID,
- "projectSFID": projectSFID,
"repositoryName": repositoryName,
"repositoryOrganizationName": repositoryOrganizationName,
"repositoryType": repositoryType,
@@ -185,13 +183,6 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
updateExpression := "SET "
- if projectSFID != "" && repoModel.ProjectSFID != projectSFID {
- log.WithFields(f).Debugf("adding projectSFID : %s ", projectSFID)
- expressionAttributeNames["#P"] = aws.String("project_sfid")
- expressionAttributeValues[":p"] = &dynamodb.AttributeValue{S: aws.String(projectSFID)}
- updateExpression = updateExpression + " #P = :p, "
- }
-
if externalID != "" && repoModel.RepositoryExternalID != externalID {
log.WithFields(f).Debugf("adding externalID : %s ", externalID)
expressionAttributeNames["#E"] = aws.String("repository_external_id")
From 5240f86468e445eb92228f2d7fadd777c7910964 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 10 Feb 2021 19:27:05 -0500
Subject: [PATCH 0072/1276] Always Use UTC when Creating Records (#2615)
- Updatd the python database base model to ensure that times are in UTC
for create and modified records
Signed-off-by: David Deal
---
.github/workflows/snyk.yml | 60 -------------------------
cla-backend/cla/models/dynamo_models.py | 6 +--
2 files changed, 3 insertions(+), 63 deletions(-)
delete mode 100644 .github/workflows/snyk.yml
diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml
deleted file mode 100644
index 4a4515b94..000000000
--- a/.github/workflows/snyk.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-name: Snyk Scanning
-on: push
-jobs:
- security:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version: [ 3.7 ]
-
- steps:
- - uses: actions/checkout@master
-# - name: Run Snyk to check for vulnerabilities (Node)
-# uses: snyk/actions/node@master
-# continue-on-error: true
-# env:
-# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-# with:
-# command: monitor
-# - name: Run Snyk to check for vulnerabilities (Golang)
-# uses: snyk/actions/golang@master
-# continue-on-error: true
-# env:
-# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-# with:
-# command: monitor
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v1
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install dependencies (cla-backend)
- run: |
- cd cla-backend
- python -m pip install --upgrade pip wheel
- pip install -r requirements.txt
- - name: Install Go
- uses: actions/setup-go@v2
- with:
- go-version: '1.15.7'
- - name: Install Dependencies (cla-backend-go)
- run: |
- cd cla-backend-go
- go mod download
- - uses: snyk/actions/setup@master
- - name: Run Snyk to check for vulnerabilities (all)
- env:
- SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- run: |
- snyk monitor --detection-depth=99 --dev --all-projects --org=${{ secrets.SNYK_ORG }}
-
-# - name: Run Snyk to check for vulnerabilities (Python)
-# uses: snyk/actions/python@master
-# continue-on-error: true
-# env:
-# SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-# with:
-# command: monitor
-# - name: Upload result to GitHub Code Scanning
-# uses: github/codeql-action/upload-sarif@v1
-# with:
-# sarif_file: snyk.sarif
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 214c46a25..771ce9aea 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -583,8 +583,8 @@ class BaseModel(Model):
Base pynamodb model used for all CLA models.
"""
- date_created = UTCDateTimeAttribute(default=datetime.datetime.now())
- date_modified = UTCDateTimeAttribute(default=datetime.datetime.now())
+ date_created = UTCDateTimeAttribute(default=datetime.datetime.utcnow())
+ date_modified = UTCDateTimeAttribute(default=datetime.datetime.utcnow())
version = UnicodeAttribute(default="v1") # Schema version.
def __iter__(self):
@@ -4149,7 +4149,7 @@ class Meta:
event_project_name_lower = UnicodeAttribute(null=True)
event_user_name = UnicodeAttribute(null=True)
event_user_name_lower = UnicodeAttribute(null=True)
- event_time = UTCDateTimeAttribute(default=datetime.datetime.now())
+ event_time = UTCDateTimeAttribute(default=datetime.datetime.utcnow())
event_time_epoch = NumberAttribute(default=int(time.time()))
event_data = UnicodeAttribute(null=True)
event_summary = UnicodeAttribute(null=True)
From de0afc29029be59d8f49556db4c51655730c878a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 10 Feb 2021 19:57:02 -0500
Subject: [PATCH 0073/1276] Resolved #2616 - Corporate Console Support for
Linux LLC Projects (#2617)
- Added support for projects with 'LF Projects, LLC' parent
Signed-off-by: David Deal
---
.../github_organizations/service.go | 3 ++-
cla-backend-go/utils/constants.go | 3 +++
cla-backend-go/v2/cla_groups/helpers.go | 22 +++++++++----------
.../v2/github_organizations/service.go | 6 +++--
cla-backend-go/v2/project-service/client.go | 14 ++++++------
cla-backend-go/v2/project/handlers.go | 2 +-
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 ++--
8 files changed, 31 insertions(+), 25 deletions(-)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index 562d7b9b0..3aa8fb11c 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -100,7 +100,8 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
}
- log.WithFields(f).Debugf("no parent or parent is %s - search criteria exhausted", utils.TheLinuxFoundation)
+ log.WithFields(f).Debugf("no parent or parent is %s or %s - search criteria exhausted",
+ utils.TheLinuxFoundation, utils.TheLinuxFoundation)
return gitHubOrgModels, err
}
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index edb04e560..98b23103b 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -45,6 +45,9 @@ const GitHubBotName = "EasyCLA"
// TheLinuxFoundation is the name of the super parent for many Salesforce Foundations/Project Groups
const TheLinuxFoundation = "The Linux Foundation"
+// LFProjectsLLC is the LF project LLC name of the super parent for many Salesforce Foundations/Project Groups
+const LFProjectsLLC = "LF Projects, LLC"
+
// ProjectUnfunded is a constant that represents a SF project that is unfunded
const ProjectUnfunded = "Unfunded"
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 6bb1ad257..dfe13ce9d 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -136,7 +136,7 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
log.WithFields(f).Debugf("looking up LF parent project record...")
isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
if err != nil {
- log.WithFields(f).Warnf("validation failure - unable to lookup %s project, error: %+v", utils.TheLinuxFoundation, err)
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return false, err
}
@@ -201,16 +201,16 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
log.WithFields(f).Debugf("looking up LF parent project record...")
isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
if err != nil {
- log.WithFields(f).Warnf("validation failure - unable to lookup %s project, error: %+v", utils.TheLinuxFoundation, err)
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return err
}
// Let's check the foundation provided - does it have a parent? Only allowed parent is TLF
if foundationProjectDetails.Parent != "" && !isLFParent {
- log.WithFields(f).Warnf("input validation failure - foundation_sfid of %s has a parent other than %s which is: %s",
- foundationSFID, utils.TheLinuxFoundation, foundationProjectDetails.Parent)
- return fmt.Errorf("bad request: input validation failure - foundation_sfid of %s has a parent other than %s which is: %s",
- foundationSFID, utils.TheLinuxFoundation, foundationProjectDetails.Parent)
+ log.WithFields(f).Warnf("input validation failure - foundation_sfid of %s has a parent other than %s or %s which is: %s",
+ foundationSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC, foundationProjectDetails.Parent)
+ return fmt.Errorf("bad request: input validation failure - foundation_sfid of %s has a parent other than %s or %s which is: %s",
+ foundationSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC, foundationProjectDetails.Parent)
}
// Comment out the below as we want to support stand-alone projects
@@ -304,15 +304,15 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
log.WithFields(f).Debugf("looking up LF parent project record...")
isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
if err != nil {
- log.WithFields(f).Warnf("validation failure - unable to lookup %s project, error: %+v", utils.TheLinuxFoundation, err)
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return err
}
if foundationProjectDetails.Parent != "" && !isLFParent {
- log.WithFields(f).Warnf("input validation failure - foundation_sfid of %s has a parent other than %s which is: %s",
- foundationSFID, utils.TheLinuxFoundation, foundationProjectDetails.Parent)
- return fmt.Errorf("bad request: input validation failure - foundation_sfid of %s has a parent other than %s which is: %s",
- foundationSFID, utils.TheLinuxFoundation, foundationProjectDetails.Parent)
+ log.WithFields(f).Warnf("input validation failure - foundation_sfid of %s has a parent other than %s or %s which is: %s",
+ foundationSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC, foundationProjectDetails.Parent)
+ return fmt.Errorf("bad request: input validation failure - foundation_sfid of %s has a parent other than %s or %s which is: %s",
+ foundationSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC, foundationProjectDetails.Parent)
}
// Comment out the below as we want to support stand-alone projects
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index a80e133ca..88d47b320 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,7 +85,9 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if (projectServiceRecord.Foundation != nil && projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation) || projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
+ if (projectServiceRecord.Foundation != nil &&
+ (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
+ projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
@@ -261,7 +263,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation {
+ if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index cae1349fc..2f5711d09 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,8 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || projectModel.Parent == utils.TheLinuxFoundation {
- log.WithFields(f).Debugf("no parent for projectSFID or %s is the parent...", utils.TheLinuxFoundation)
+ if projectModel.Parent == "" || projectModel.Parent == utils.TheLinuxFoundation || projectModel.Parent == utils.LFProjectsLLC {
+ log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
@@ -116,7 +116,7 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
// IsTheLinuxFoundation returns true if the specified project SFID is the The Linux Foundation project
func (pmm *Client) IsTheLinuxFoundation(projectSFID string) (bool, error) {
f := logrus.Fields{
- "functionName": "IsTheLinuxFoundation",
+ "functionName": "project-service.IsTheLinuxFoundation",
}
log.WithFields(f).Debug("querying project...")
@@ -126,9 +126,9 @@ func (pmm *Client) IsTheLinuxFoundation(projectSFID string) (bool, error) {
return false, err
}
- if projectModel.Name == utils.TheLinuxFoundation {
+ if projectModel.Name == utils.TheLinuxFoundation || projectModel.Name == utils.LFProjectsLLC {
// Save into our cache for next time
- log.WithFields(f).Debug("project is the linux foundation...")
+ log.WithFields(f).Debugf("project is %s or %s...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return true, nil
}
@@ -158,9 +158,9 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
return false, err
}
- if parentProjectModel.Name == utils.TheLinuxFoundation {
+ if parentProjectModel.Name == utils.TheLinuxFoundation || parentProjectModel.Name == utils.LFProjectsLLC {
// Save into our cache for next time
- log.WithFields(f).Debug("parent project is the linux foundation...")
+ log.WithFields(f).Debugf("parent project is %s or %s...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return true, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 1eb1eeda1..e7b7351f5 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -319,6 +319,6 @@ func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetail
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || sfProject.Parent == utils.TheLinuxFoundation),
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || sfProject.Parent == utils.TheLinuxFoundation || sfProject.Parent == utils.LFProjectsLLC),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index de09e6fcf..3a16fdcfb 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -86,7 +86,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
return nil, err
}
var externalProjectID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation {
+ if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index fa7384486..0d37c4b06 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -123,7 +123,7 @@ func validateCorporateSignatureInput(input *models.CorporateSignatureInput) erro
return nil
}
-func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername string, authorizationHeader string, input *models.CorporateSignatureInput) (*models.CorporateSignatureOutput, error) {
+func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername string, authorizationHeader string, input *models.CorporateSignatureInput) (*models.CorporateSignatureOutput, error) { // nolint
f := logrus.Fields{
"functionName": "sign.RequestCorporateSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -173,7 +173,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation {
+ if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From 88e60cdb669ce45f2bdb5bb39ca216a851d752c3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 10 Feb 2021 20:33:32 -0500
Subject: [PATCH 0074/1276] Added Additional Logging (#2618)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_manager/handlers.go | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 3e3a4e142..be09ea7dc 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -192,14 +192,10 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
log.WithFields(f).Debugf("getting project IDs for CLA group")
projectCLAGroups, getErr := projectClaGroupRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
if getErr != nil {
- msg := fmt.Sprintf("Error getting SF projects for claGroup: %s ", params.ClaGroupID)
- log.WithFields(f).Warn(msg)
+ msg := fmt.Sprintf("error getting SF projects for claGroup: %s ", params.ClaGroupID)
+ log.WithFields(f).WithError(getErr).Warn(msg)
return cla_manager.NewCreateCLAManagerDesigneeByGroupBadRequest().WithXRequestID(reqID).WithPayload(
- &models.ErrorResponse{
- Message: msg,
- Code: BadRequest,
- XRequestID: reqID,
- })
+ utils.ErrorResponseBadRequestWithError(reqID, msg, getErr))
}
log.WithFields(f).Debugf("found %d project IDs for CLA group", len(projectCLAGroups))
@@ -211,10 +207,10 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
designeeScopes, msg, err := service.CreateCLAManagerDesigneeByGroup(ctx, params, projectCLAGroups)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating cla manager designee for CLA Group: %s with user email: %s", params.ClaGroupID, params.Body.UserEmail)
if err == ErrCLAManagerDesigneeConflict {
return cla_manager.NewCreateCLAManagerDesigneeByGroupConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
}
- log.WithFields(f).Warn(msg)
return cla_manager.NewCreateCLAManagerDesigneeByGroupBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
From 07e507752e1337b74c5b96e48a56c7e6c8d2425e Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 12 Feb 2021 11:44:33 +0300
Subject: [PATCH 0075/1276] [#2611] Bug/Add GH org Standalone Project
- Resolved issue caused when adding standlone projects
- Updated organization-service client functions for logo pointer fields
Signed-off-by: wanyaland
---
cla-backend-go/v2/github_organizations/service.go | 2 +-
cla-backend-go/v2/organization-service/client.go | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 88d47b320..229730a2e 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -263,7 +263,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
+ if project.Parent == "" || project.Foundation.Name == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/organization-service/client.go b/cla-backend-go/v2/organization-service/client.go
index 8ffcb077a..131ac0565 100644
--- a/cla-backend-go/v2/organization-service/client.go
+++ b/cla-backend-go/v2/organization-service/client.go
@@ -600,7 +600,7 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
Industry: &industry,
Source: &companySource,
Type: &companyType,
- LogoURL: &logoURL,
+ LogoURL: logoURL,
SigningEntityName: []string{signingEntityName},
},
Context: ctx,
@@ -613,7 +613,7 @@ func (osc *Client) CreateOrg(ctx context.Context, companyName, signingEntityName
Industry: &industry,
Source: &companySource,
Type: &companyType,
- LogoURL: &logoURL,
+ LogoURL: logoURL,
SigningEntityName: []string{signingEntityName},
})
result, err := osc.cl.Organizations.CreateOrg(params, clientAuth)
From 95b21cddf77691af93846ad860f1f9cacea93b43 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 12 Feb 2021 14:44:10 +0300
Subject: [PATCH 0076/1276] Bug/Foundation Name check
- Resolved unexpected behaviour when enforcing CLA for repositories and orgs
Signed-off-by: wanyaland
---
.../v2/github_organizations/service.go | 3 ++-
cla-backend-go/v2/project-service/client.go | 3 ++-
cla-backend-go/v2/project/handlers.go | 25 ++++++++++---------
cla-backend-go/v2/repositories/service.go | 3 ++-
cla-backend-go/v2/sign/service.go | 3 ++-
5 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 229730a2e..0640fc3ed 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -263,7 +263,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || project.Foundation.Name == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 2f5711d09..8b682a9ea 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,7 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || projectModel.Parent == utils.TheLinuxFoundation || projectModel.Parent == utils.LFProjectsLLC {
+ if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index e7b7351f5..6fa24ff72 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -308,17 +308,18 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetailed, parentName string) *models.SfProjectSummary {
return &models.SfProjectSummary{
- EntityName: sfProject.EntityName,
- EntityType: sfProject.EntityType,
- Funding: sfProject.Funding,
- ID: sfProject.ID,
- LfSupported: sfProject.LFSponsored,
- Name: sfProject.Name,
- ParentID: sfProject.Parent,
- ParentName: parentName,
- Slug: sfProject.Slug,
- Status: sfProject.Status,
- Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || sfProject.Parent == utils.TheLinuxFoundation || sfProject.Parent == utils.LFProjectsLLC),
+ EntityName: sfProject.EntityName,
+ EntityType: sfProject.EntityType,
+ Funding: sfProject.Funding,
+ ID: sfProject.ID,
+ LfSupported: sfProject.LFSponsored,
+ Name: sfProject.Name,
+ ParentID: sfProject.Parent,
+ ParentName: parentName,
+ Slug: sfProject.Slug,
+ Status: sfProject.Status,
+ Type: sfProject.Type,
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
+ (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 3a16fdcfb..5f128e9cd 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -86,7 +86,8 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
return nil, err
}
var externalProjectID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 0d37c4b06..f77044e59 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,7 +173,8 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || project.Parent == utils.TheLinuxFoundation || project.Parent == utils.LFProjectsLLC {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From 4e0e8621e22d0588b9a1120375ba21c8029d1bbf Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 12 Feb 2021 17:13:33 +0300
Subject: [PATCH 0077/1276] Bug/SF Parent Hierachy
- Use 'ParentHeirachy' key in salesforce Project when checking Parent Name (LF Projects,LLC && TLF)
Signed-off-by: wanyaland
---
cla-backend-go/v2/github_organizations/service.go | 8 ++++----
cla-backend-go/v2/project-service/client.go | 4 ++--
cla-backend-go/v2/project/handlers.go | 4 ++--
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 ++--
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 0640fc3ed..b2018c187 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,8 +85,8 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if (projectServiceRecord.Foundation != nil &&
- (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
+ if (projectServiceRecord.ParentHierarchy != nil &&
+ (projectServiceRecord.ParentHierarchy.Name == utils.TheLinuxFoundation || projectServiceRecord.ParentHierarchy.Name == utils.LFProjectsLLC)) ||
projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
@@ -263,8 +263,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.ParentHierarchy != nil &&
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 8b682a9ea..68fc1c569 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,8 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.Foundation != nil &&
- (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
+ if projectModel.Parent == "" || (projectModel.ParentHierarchy != nil &&
+ (projectModel.ParentHierarchy.Name == utils.TheLinuxFoundation || projectModel.ParentHierarchy.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 6fa24ff72..c158afbe9 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -319,7 +319,7 @@ func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetail
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
- (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.ParentHierarchy != nil &&
+ (sfProject.ParentHierarchy.Name == utils.TheLinuxFoundation || sfProject.ParentHierarchy.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 5f128e9cd..48437b30f 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -87,7 +87,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
}
var externalProjectID string
if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index f77044e59..b9a051963 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,8 +173,8 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.ParentHierarchy != nil &&
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From 60bde592a6f77609614f8be5e52122451778d9b1 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 12 Feb 2021 17:29:27 +0300
Subject: [PATCH 0078/1276] Revert "Bug/SF Parent Hierachy"
---
cla-backend-go/v2/github_organizations/service.go | 8 ++++----
cla-backend-go/v2/project-service/client.go | 4 ++--
cla-backend-go/v2/project/handlers.go | 4 ++--
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 ++--
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index b2018c187..0640fc3ed 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,8 +85,8 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if (projectServiceRecord.ParentHierarchy != nil &&
- (projectServiceRecord.ParentHierarchy.Name == utils.TheLinuxFoundation || projectServiceRecord.ParentHierarchy.Name == utils.LFProjectsLLC)) ||
+ if (projectServiceRecord.Foundation != nil &&
+ (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
@@ -263,8 +263,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.ParentHierarchy != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 68fc1c569..8b682a9ea 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,8 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.ParentHierarchy != nil &&
- (projectModel.ParentHierarchy.Name == utils.TheLinuxFoundation || projectModel.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index c158afbe9..6fa24ff72 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -319,7 +319,7 @@ func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetail
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.ParentHierarchy != nil &&
- (sfProject.ParentHierarchy.Name == utils.TheLinuxFoundation || sfProject.ParentHierarchy.Name == utils.LFProjectsLLC))),
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
+ (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 48437b30f..5f128e9cd 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -87,7 +87,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
}
var externalProjectID string
if project.Parent == "" || (project.Foundation != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index b9a051963..f77044e59 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,8 +173,8 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || (project.ParentHierarchy != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From c5a82b41bd7e62ad52c28959c231295b108a2f82 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 12 Feb 2021 14:52:35 -0500
Subject: [PATCH 0079/1276] Added Search Company to Include Organization
Service (#2629)
Signed-off-by: David Deal
---
cla-backend-go/v2/company/handlers.go | 35 ++++++++++++++++++++++-----
cla-backend-go/v2/company/service.go | 33 ++++++++++++++++++++++---
2 files changed, 58 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index d68fe3866..3533154b8 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -9,6 +9,8 @@ import (
"fmt"
"strings"
+ organization_service "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
+
"github.com/aws/aws-sdk-go/aws"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -344,7 +346,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("creating company...")
- companyModel, err := service.CreateCompany(ctx, *params.Input.CompanyName, params.Input.SigningEntityName, *params.Input.CompanyWebsite, params.Input.UserEmail.String(), params.UserID)
+ companyModel, err := service.CreateCompany(ctx, *params.Input.CompanyName, params.Input.SigningEntityName, *params.Input.CompanyWebsite, params.Input.UserEmail.String(), params.UserID, "")
if err != nil {
log.Warnf("error returned from create company api: %+v", err)
if strings.Contains(err.Error(), "website already exists") {
@@ -373,14 +375,35 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
log.WithFields(f).Debug("loading company by name")
companyModel, err := service.GetCompanyByName(ctx, params.CompanyName)
- if err != nil {
- msg := fmt.Sprintf("unable to locate company by name: %s", params.CompanyName)
- log.WithFields(f).WithError(err).Warn(msg)
- return company.NewGetCompanyByNameBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ if err != nil || companyModel == nil {
+ log.WithFields(f).Warn("unable to lookup company by name in local database. trying organization service...")
+ osClient := organization_service.GetClient()
+ orgModels, orgLookupErr := osClient.SearchOrganization(ctx, params.CompanyName, "", "")
+ if orgLookupErr != nil || len(orgModels) == 0 {
+ msg := fmt.Sprintf("unable to locate organization '%s' in the organization service", params.CompanyName)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyByNameNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("found company: '%s' in the organization service - creating local record...", params.CompanyName)
+ companyModels, companyCreateErr := service.CreateCompanyFromSFModel(ctx, orgModels[0])
+ if companyCreateErr != nil || companyModels == nil {
+ msg := fmt.Sprintf("unable to create company '%s' from salesforce record", params.CompanyName)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyByNameInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, companyCreateErr))
+ }
+
+ log.WithFields(f).Debugf("loading company: %s by name after creation...", params.CompanyName)
+ companyModel, err = service.GetCompanyByName(ctx, params.CompanyName)
+ if err != nil {
+ msg := fmt.Sprintf("unable to locate company '%s' after creating...", params.CompanyName)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return company.NewGetCompanyByNameNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
}
if companyModel == nil {
- msg := fmt.Sprintf("unable to locate company by name: %s", params.CompanyName)
+ msg := fmt.Sprintf("unable to load company by name: %s", params.CompanyName)
log.WithFields(f).Warn(msg)
return company.NewGetCompanyByNameNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
}
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index f41d43d12..409351c9f 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -37,6 +37,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/utils"
acs_service "github.com/communitybridge/easycla/cla-backend-go/v2/acs-service"
+ orgModels "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/models"
orgService "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
"github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/client/organizations"
@@ -89,7 +90,8 @@ type Service interface {
GetCompanyProjectActiveCLAs(ctx context.Context, companyID string, projectSFID string) (*models.ActiveClaList, error)
GetCompanyProjectContributors(ctx context.Context, projectSFID string, companySFID string, searchTerm string) (*models.CorporateContributorList, error)
GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string, companyID *string) (*models.CompanyProjectClaList, error)
- CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string) (*models.CompanyOutput, error)
+ CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID, note string) (*models.CompanyOutput, error)
+ CreateCompanyFromSFModel(ctx context.Context, orgModel *orgModels.Organization) (*models.CompanyOutput, error)
GetCompanyByName(ctx context.Context, companyName string) (*models.Company, error)
GetCompanyBySigningEntityName(ctx context.Context, signingEntityName string) (*models.Company, error)
GetCompanyByID(ctx context.Context, companyID string) (*models.Company, error)
@@ -369,7 +371,7 @@ func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID
}, nil
}
-func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string) (*models.CompanyOutput, error) {
+func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string, note string) (*models.CompanyOutput, error) {
f := logrus.Fields{
"functionName": "CreateCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -378,6 +380,7 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
"companyWebsite": companyWebsite,
"userEmail": userEmail,
"userID": userID,
+ "note": note,
}
var lfUser *v2UserServiceModels.User
@@ -399,11 +402,11 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
log.WithFields(f).Warn(msg)
}
if lfUser != nil && lfUser.Username == "" {
- msg := fmt.Sprintf("User: %s has no LF username", userEmail)
+ msg := fmt.Sprintf("User: %+v has no LF login/username", lfUser)
log.WithFields(f).Warn(msg)
}
if lfUser != nil && lfUser.Username != "" {
- log.WithFields(f).Debugf("User :%s has been assigned the %s role to organization: %s ",
+ log.WithFields(f).Debugf("User: %s has been assigned the %s role to organization: %s ",
userEmail, utils.CompanyAdminRole, org.Name)
// Assign company-admin to user
roleID, adminErr := acsClient.GetRoleID(utils.CompanyAdminRole)
@@ -444,6 +447,12 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
CompanyName: companyName,
SigningEntityName: signingEntityName,
}
+ if lfUser != nil && lfUser.Username != "" {
+ createCompanyModel.CompanyACL = []string{lfUser.Username}
+ }
+ if note != "" {
+ createCompanyModel.Note = note
+ }
_, createErr := s.companyRepo.CreateCompany(ctx, createCompanyModel)
//easyCLAErr := s.repo.CreateCompany(companyName, org.ID, userID)
@@ -462,6 +471,22 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
}, nil
}
+func (s *service) CreateCompanyFromSFModel(ctx context.Context, orgModel *orgModels.Organization) (*models.CompanyOutput, error) {
+ f := logrus.Fields{
+ "functionName": "company.service.CreateCompanyFromSFModel",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "organizationID": orgModel.Name,
+ "organizationName": orgModel.Name,
+ "organizationType": orgModel.Type,
+ "organizationLink": orgModel.Link,
+ "organizationStatus": orgModel.Status,
+ }
+
+ log.WithFields(f).Debug("Creating company...")
+ return s.CreateCompany(ctx, orgModel.Name, orgModel.Name, orgModel.Link,
+ "", "", fmt.Sprintf("created from platform organization service model: %s", orgModel.ID))
+}
+
// GetCompanyByName deletes the company by name
func (s *service) GetCompanyByName(ctx context.Context, companyName string) (*models.Company, error) {
f := logrus.Fields{
From 56bc865ddf7f178081dfcbda6193470af5e77134 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 12 Feb 2021 20:04:28 -0500
Subject: [PATCH 0080/1276] Resolved #2630 Enrolled Repositories Note (#2631)
- Added note as optional input to data model
- Updated repos update reposistory logic to set or append note if provided
Signed-off-by: David Deal
---
cla-backend-go/repositories/repository.go | 21 +++++++++++++++++++
cla-backend-go/swagger/cla.v2.yaml | 7 ++++++-
.../common/github-repository-input.yaml | 17 ++++++++++++++-
cla-backend-go/v2/repositories/service.go | 8 +++++--
4 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index 1c60c73d3..d16381a84 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
"github.com/sirupsen/logrus"
@@ -154,6 +155,7 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
repositoryOrganizationName := utils.StringValue(input.RepositoryOrganizationName)
repositoryType := utils.StringValue(input.RepositoryType)
repositoryURL := utils.StringValue(input.RepositoryURL)
+ note := input.Note
f := logrus.Fields{
"functionName": "repositories.repository.UpdateGitHubRepository",
@@ -218,6 +220,25 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
updateExpression = updateExpression + " #U = :u, "
}
+ if note != "" {
+ log.WithFields(f).Debugf("adding note: %s ", note)
+ noteValue := note
+ if !strings.HasSuffix(noteValue, ".") {
+ noteValue = fmt.Sprintf("%s.", noteValue)
+ }
+ // If we have a previous value - just concat the value to the end
+ if repoModel.Note != "" {
+ if strings.HasSuffix(strings.TrimSpace(repoModel.Note), ".") {
+ noteValue = fmt.Sprintf("%s %s", repoModel.Note, noteValue)
+ } else {
+ noteValue = fmt.Sprintf("%s. %s", repoModel.Note, noteValue)
+ }
+ }
+ expressionAttributeNames["#N"] = aws.String("note")
+ expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(noteValue)}
+ updateExpression = updateExpression + " #N = :n, "
+ }
+
if input.Enabled != nil && repoModel.Enabled != *input.Enabled {
log.WithFields(f).Debugf("adding enabled flag: %+v", *input.Enabled)
expressionAttributeNames["#EN"] = aws.String("enabled")
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index abbad12d2..3113d1416 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3785,10 +3785,15 @@ definitions:
properties:
repository_github_id:
type: string
+ description: the repository external identifier, such as the GitHub ID of the repo
+ example: '337730995'
github_organization_name:
type: string
+ description: the repository organization
+ example: 'cncf'
cla_group_id:
- type: string
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
github-repository-branch-protection-status-checks:
type: object
diff --git a/cla-backend-go/swagger/common/github-repository-input.yaml b/cla-backend-go/swagger/common/github-repository-input.yaml
index 524277ee6..c861af306 100644
--- a/cla-backend-go/swagger/common/github-repository-input.yaml
+++ b/cla-backend-go/swagger/common/github-repository-input.yaml
@@ -12,16 +12,31 @@ required:
properties:
repositoryExternalID:
type: string
+ description: the repository external identifier, such as the GitHub ID of the repo
+ example: '337730995'
repositoryName:
type: string
+ description: the repository name
+ example: 'cncf/landscape'
repositoryOrganizationName:
type: string
+ description: the repository organization
+ example: 'cncf'
repositoryProjectID:
- type: string
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
repositoryType:
type: string
+ description: the repository type
+ example: 'github'
repositoryUrl:
type: string
+ description: the repository URL
+ example: 'https://github.com/cncf/landscape'
enabled:
type: boolean
+ description: 'the enabled flag: true or false Repositories can be disabled from CLA to prevent CLA checks'
default: true
+ note:
+ description: optional note added to the record
+ type: string
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 5f128e9cd..7a54a4cec 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -140,13 +140,17 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
// We already have an existing repository model with the same name
if existingRepositoryModel != nil && !existingRepositoryModel.Enabled {
- msg := fmt.Sprintf("Github repository : %s disabled and shall get re-enabled... ", utils.StringValue(ghRepo.FullName))
+ msg := fmt.Sprintf("Github repository: %s previously disabled - will re-enabled... ", utils.StringValue(ghRepo.FullName))
log.WithFields(f).Debug(msg)
enabled := true
+
+ _, now := utils.CurrentTime()
+
v1Input := &v1Models.GithubRepositoryInput{
Enabled: &enabled,
- RepositoryProjectID: input.ClaGroupID,
RepositoryOrganizationName: input.GithubOrganizationName,
+ RepositoryProjectID: input.ClaGroupID,
+ Note: fmt.Sprintf("re-enabling repository on %s.", now),
}
// Update Repo details in case of any changes
From c85b4bccf5471612b465a58aafd4de1dd388c0e4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Sun, 14 Feb 2021 14:42:06 -0800
Subject: [PATCH 0081/1276] Updated Logging - Resolved Logging error
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 54 +++++++++++------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index c4a12dcdf..6f47e7a17 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -934,8 +934,6 @@ def request_corporate_signature(self, auth_user: object,
f'send email: {send_as_email}, ',
f'signatory name: {signatory_name}, '
f'signatory email: {signatory_email}, '
- f'return url type: {return_url_type}, ',
- f'return url: {return_url}'
)
# Auth user is the currently logged in user - the user who started the signing process
@@ -1140,9 +1138,10 @@ def populate_sign_url(self, signature, callback_url=None,
default_values: Optional[Dict[str, Any]] = None,
preferred_email: str = None): # pylint: disable=too-many-locals
+ fn = 'populate_sign_url'
sig_type = signature.get_signature_reference_type()
- cla.log.debug(f'populate_sign_url - Populating sign_url for signature {signature.get_signature_id()} '
+ cla.log.debug(f'{fn} - Populating sign_url for signature {signature.get_signature_id()} '
f'using callback: {callback_url} '
f'with authority_or_signatory_name {authority_or_signatory_name} '
f'with authority_or_signatory_email {authority_or_signatory_email} '
@@ -1160,35 +1159,35 @@ def populate_sign_url(self, signature, callback_url=None,
user_signature_name = 'Unknown'
user_signature_email = 'Unknown'
- cla.log.debug(f'populate_sign_url - {sig_type} - processing signing request...')
+ cla.log.debug(f'{fn} - {sig_type} - processing signing request...')
if sig_type == 'company':
# For CCLA - use provided CLA Manager information
user_signature_name = cla_manager_name
user_signature_email = cla_manager_email
- cla.log.debug(f'populate_sign_url - {sig_type} - user_signature name/email will be CLA Manager name/info: '
+ cla.log.debug(f'{fn} - {sig_type} - user_signature name/email will be CLA Manager name/info: '
f'{user_signature_name} / {user_signature_email}...')
try:
# Grab the company id from the signature
- cla.log.debug('populate_sign_url - CCLA - '
+ cla.log.debug('{fn} - CCLA - '
f'Loading company id: {signature.get_signature_reference_id()}')
company.load(signature.get_signature_reference_id())
- cla.log.debug(f'populate_sign_url - {sig_type} - loaded company: {company}')
+ cla.log.debug(f'{fn} - {sig_type} - loaded company: {company}')
except DoesNotExist:
- cla.log.warning(f'populate_sign_url - {sig_type} - '
+ cla.log.warning(f'{fn} - {sig_type} - '
'No CLA manager associated with this company - can not sign CCLA')
return
except Exception as e:
- cla.log.warning(f'populate_sign_url - {sig_type} - No CLA manager lookup error: {e}')
+ cla.log.warning(f'{fn} - {sig_type} - No CLA manager lookup error: {e}')
return
elif sig_type == 'user':
if not send_as_email:
try:
- cla.log.debug(f'populate_sign_url - {sig_type} - '
+ cla.log.debug(f'{fn} - {sig_type} - '
f'loading user by reference id: {signature.get_signature_reference_id()}')
user.load(signature.get_signature_reference_id())
- cla.log.debug(f'populate_sign_url - {sig_type} - loaded user by '
+ cla.log.debug(f'{fn} - {sig_type} - loaded user by '
f'id: {user.get_user_id()}, '
f'name: {user.get_user_name()}, '
f'email: {user.get_user_email()}')
@@ -1197,45 +1196,45 @@ def populate_sign_url(self, signature, callback_url=None,
if not user.get_user_email() is None:
user_signature_email = user.get_user_email()
except DoesNotExist:
- cla.log.warning(f'populate_sign_url - {sig_type} - no user associated with this signature '
+ cla.log.warning(f'{fn} - {sig_type} - no user associated with this signature '
f'id: {signature.get_signature_reference_id()} - can not sign ICLA')
return
except Exception as e:
- cla.log.warning(f'populate_sign_url - {sig_type} - no user associated with this signature - '
+ cla.log.warning(f'{fn} - {sig_type} - no user associated with this signature - '
f'id: {signature.get_signature_reference_id()}, '
f'error: {e}')
return
cla.log.debug(
- f'populate_sign_url - {sig_type} - user_signature name/email will be user from signature: '
+ f'{fn} - {sig_type} - user_signature name/email will be user from signature: '
f'{user_signature_name} / {user_signature_email}...')
else:
- cla.log.warning(f'populate_sign_url - unsupported signature type: {sig_type}')
+ cla.log.warning(f'{fn} - unsupported signature type: {sig_type}')
return
# Fetch the document template to sign.
project = Project()
- cla.log.debug(f'populate_sign_url - {sig_type} - '
+ cla.log.debug(f'{fn} - {sig_type} - '
f'loading project by id: {signature.get_signature_project_id()}')
project.load(signature.get_signature_project_id())
- cla.log.debug(f'populate_sign_url - {sig_type} - '
+ cla.log.debug(f'{fn} - {sig_type} - '
f'loaded project by id: {signature.get_signature_project_id()} - '
f'project: {project}')
# Load the appropriate document
if sig_type == 'company':
- cla.log.debug(f'populate_sign_url - {sig_type} - loading project_corporate_document...')
+ cla.log.debug(f'{fn} - {sig_type} - loading project_corporate_document...')
document = project.get_project_corporate_document()
if document is None:
- cla.log.error(f'populate_sign_url - {sig_type} - Could not get sign url for project: {project}. '
+ cla.log.error(f'{fn} - {sig_type} - Could not get sign url for project: {project}. '
'Project has no corporate CLA document set. Returning...')
return
- cla.log.debug(f'populate_sign_url - {sig_type} - loaded project_corporate_document...')
+ cla.log.debug(f'{fn} - {sig_type} - loaded project_corporate_document...')
else: # sig_type == 'user'
- cla.log.debug(f'populate_sign_url - {sig_type} - loading project_individual_document...')
+ cla.log.debug(f'{fn} - {sig_type} - loading project_individual_document...')
document = project.get_project_individual_document()
if document is None:
- cla.log.error(f'populate_sign_url - {sig_type} - Could not get sign url for project: {project}. '
+ cla.log.error(f'{fn} - {sig_type} - Could not get sign url for project: {project}. '
'Project has no individual CLA document set. Returning...')
return
cla.log.debug(f'populate_sign_url - {sig_type} - loaded project_individual_document...')
@@ -1250,7 +1249,7 @@ def populate_sign_url(self, signature, callback_url=None,
cla.log.debug(message)
self.client.void_envelope(envelope_id, message)
except Exception as e:
- cla.log.warning(f'populate_sign_url - {sig_type} - DocuSign error while voiding the envelope - '
+ cla.log.warning(f'{fn} - {sig_type} - DocuSign error while voiding the envelope - '
f'regardless, continuing on..., error: {e}')
# Not sure what should be put in as documentId.
@@ -1258,7 +1257,7 @@ def populate_sign_url(self, signature, callback_url=None,
tabs = get_docusign_tabs_from_document(document, document_id, default_values=default_values)
if send_as_email:
- cla.log.warning(f'populate_sign_url - {sig_type} - assigning signatory name/email: '
+ cla.log.warning(f'{fn} - {sig_type} - assigning signatory name/email: '
f'{authority_or_signatory_name} / {authority_or_signatory_email}')
# Sending email to authority
signatory_email = authority_or_signatory_email
@@ -1268,7 +1267,7 @@ def populate_sign_url(self, signature, callback_url=None,
project_name = project.get_project_name()
company_name = company.get_company_name()
- cla.log.debug(f'populate_sign_url - {sig_type} - sending document as email with '
+ cla.log.debug(f'{fn} - {sig_type} - sending document as email with '
f'name: {signatory_name}, email: {signatory_email} '
f'project name: {project_name}, company: {company_name}')
@@ -1330,7 +1329,7 @@ def populate_sign_url(self, signature, callback_url=None,
pdf = io.BytesIO(content)
doc_name = document.get_document_name()
- cla.log.debug(f'populate_sign_url - {sig_type} - docusign document '
+ cla.log.debug(f'{fn} - {sig_type} - docusign document '
f'name: {doc_name}, id: {document_id}, content type: {content_type}')
document = pydocusign.Document(name=doc_name, documentId=document_id, data=pdf)
@@ -1373,9 +1372,10 @@ def populate_sign_url(self, signature, callback_url=None,
signature.set_signature_sign_url(sign_url)
# Save Envelope ID in signature.
- cla.log.debug(f'populate_sign_url - {sig_type} - saving signature to database...')
+ cla.log.debug(f'{fn} - {sig_type} - saving signature to database...')
signature.set_signature_envelope_id(envelope.envelopeId)
signature.save()
+ cla.log.debug(f'{fn} - {sig_type} - saved signature to database - id: {signature.get_signature_id()}...')
cla.log.debug(f'populate_sign_url - {sig_type} - complete')
def signed_individual_callback(self, content, installation_id, github_repository_id, change_request_id):
From 99782049aa2d8efa7e824bab61d298617d5172cf Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 12 Feb 2021 17:57:11 +0300
Subject: [PATCH 0082/1276] Revert "Revert "Bug/SF Parent Hierachy""
This reverts commit 60bde592a6f77609614f8be5e52122451778d9b1.
---
cla-backend-go/v2/github_organizations/service.go | 8 ++++----
cla-backend-go/v2/project-service/client.go | 4 ++--
cla-backend-go/v2/project/handlers.go | 4 ++--
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 ++--
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 0640fc3ed..b2018c187 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -85,8 +85,8 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
var parentProjectSFID string
- if (projectServiceRecord.Foundation != nil &&
- (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
+ if (projectServiceRecord.ParentHierarchy != nil &&
+ (projectServiceRecord.ParentHierarchy.Name == utils.TheLinuxFoundation || projectServiceRecord.ParentHierarchy.Name == utils.LFProjectsLLC)) ||
projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
@@ -263,8 +263,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.ParentHierarchy != nil &&
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 8b682a9ea..68fc1c569 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,8 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.Foundation != nil &&
- (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
+ if projectModel.Parent == "" || (projectModel.ParentHierarchy != nil &&
+ (projectModel.ParentHierarchy.Name == utils.TheLinuxFoundation || projectModel.ParentHierarchy.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 6fa24ff72..c158afbe9 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -319,7 +319,7 @@ func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetail
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
- (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.ParentHierarchy != nil &&
+ (sfProject.ParentHierarchy.Name == utils.TheLinuxFoundation || sfProject.ParentHierarchy.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 7a54a4cec..9567a2bea 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -87,7 +87,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
}
var externalProjectID string
if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index f77044e59..b9a051963 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,8 +173,8 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.ParentHierarchy != nil &&
+ (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From 52d4c11d674ffb3c44fe801c05691f3a10860106 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 15 Feb 2021 06:09:29 +0300
Subject: [PATCH 0083/1276] [#2637] Bug/Get GH Orgs
- Resolved get gh-orgs issue for standalone SF Project
- Deprecated Parent Heirachy check, reverted to Foundation key
Signed-off-by: wanyaland
---
cla-backend-go/v2/github_organizations/service.go | 12 +++++++-----
cla-backend-go/v2/project-service/client.go | 4 ++--
cla-backend-go/v2/project/handlers.go | 4 ++--
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 ++--
5 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index b2018c187..46dd3e082 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -84,10 +84,12 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return nil, err
}
+ log.Debugf("project record: %+v ", projectServiceRecord)
+
var parentProjectSFID string
- if (projectServiceRecord.ParentHierarchy != nil &&
- (projectServiceRecord.ParentHierarchy.Name == utils.TheLinuxFoundation || projectServiceRecord.ParentHierarchy.Name == utils.LFProjectsLLC)) ||
- projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
+ if (projectServiceRecord.Foundation != nil &&
+ (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
+ projectServiceRecord.Parent == "" {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
@@ -263,8 +265,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.ParentHierarchy != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = project.Parent
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 68fc1c569..8b682a9ea 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -104,8 +104,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.ParentHierarchy != nil &&
- (projectModel.ParentHierarchy.Name == utils.TheLinuxFoundation || projectModel.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index c158afbe9..6fa24ff72 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -319,7 +319,7 @@ func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetail
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.ParentHierarchy != nil &&
- (sfProject.ParentHierarchy.Name == utils.TheLinuxFoundation || sfProject.ParentHierarchy.Name == utils.LFProjectsLLC))),
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
+ (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 9567a2bea..7a54a4cec 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -87,7 +87,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
}
var externalProjectID string
if project.Parent == "" || (project.Foundation != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
externalProjectID = projectSFID
} else {
externalProjectID = project.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index b9a051963..f77044e59 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,8 +173,8 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || (project.ParentHierarchy != nil &&
- (project.ParentHierarchy.Name == utils.TheLinuxFoundation || project.ParentHierarchy.Name == utils.LFProjectsLLC)) {
+ if project.Parent == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
From 17f21bdc638b3bd71eb121ef48f1b9a7ec4cba00 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 15 Feb 2021 14:48:16 +0300
Subject: [PATCH 0084/1276] [#2636] Bug/Sign Standalone Project
- Resolved CCLA sign issue for standalone projects
Signed-off-by: wanyaland
---
cla-backend-go/go.sum | 1 +
cla-backend-go/v2/sign/service.go | 16 +++++++++-------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 9554c70c3..50f8cca2c 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -70,6 +70,7 @@ github.com/communitybridge/easycla v1.0.106 h1:NLYUZUZtp9DQ0dHEQkhz9h9EMzLRmuh9u
github.com/communitybridge/easycla v1.0.107 h1:dktHAji1yJ1nMEu54z4paPWOM4Q7A9rryc0OCADfAcY=
github.com/communitybridge/easycla v1.0.117 h1:o+rdmcNgZeMQ/N8HV/d5apNIBrkYH7eyM9UUYnEzewo=
github.com/communitybridge/easycla v1.0.118 h1:8yrsOQ+ENUFi4RFl1krRlIxc51lzZNutidR+yy2HwW0=
+github.com/communitybridge/easycla v1.0.123 h1:Lh5i/9aajrTYItxNpVCmi9T1yyIfnQIOk0tC2Wtslvk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index f77044e59..185730c3a 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -175,6 +175,14 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
var claGroupID string
if project.Parent == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ cgm, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(utils.StringValue(input.ProjectSfid))
+ if perr != nil {
+ log.WithFields(f).WithError(err).Warn("unable to lookup CLA Group ID for this project SFID")
+ return nil, perr
+ }
+ claGroupID = cgm.ClaGroupID
+
+ } else {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
if perr != nil {
@@ -195,13 +203,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
return nil, errors.New("invalid project_sfid. multiple cla-groups are associated with this project_sfid")
}
claGroupID = (claGroups.List())[0]
- } else {
- cgm, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(utils.StringValue(input.ProjectSfid))
- if perr != nil {
- log.WithFields(f).WithError(err).Warn("unable to lookup CLA Group ID for this project SFID")
- return nil, perr
- }
- claGroupID = cgm.ClaGroupID
+
}
f["claGroupID"] = claGroupID
From 7cb6a44156327ce6826459cf17160012b37aaf53 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 15 Feb 2021 18:12:31 +0300
Subject: [PATCH 0085/1276] [#2642] Feature/Contributors GH details
- Udpated GitHubID value with GItHub Username
Signed-off-by: wanyaland
---
cla-backend-go/v2/company/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 409351c9f..47964191b 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1472,7 +1472,7 @@ func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepos
}
var contributor models.CorporateContributor
var sigSignedTime = sig.SignatureCreated
- contributor.GithubID = user.GithubID
+ contributor.GithubID = user.GithubUsername
contributor.LinuxFoundationID = user.LfUsername
contributor.Name = user.Username
t, err := utils.ParseDateTime(sig.SignatureCreated)
From 1bdbcae8fb115eb76b67d836f8bd09f4a6af0a1d Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 15 Feb 2021 18:54:22 +0300
Subject: [PATCH 0086/1276] [#2643] Bug/Sign Parent Project Under TLF
- Resolved issue for Parent project sign flow under TLF
Signed-off-by: wanyaland
---
cla-backend-go/v2/github_organizations/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 46dd3e082..734891751 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -89,7 +89,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
var parentProjectSFID string
if (projectServiceRecord.Foundation != nil &&
(projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
- projectServiceRecord.Parent == "" {
+ projectServiceRecord.Parent == "" || projectServiceRecord.ProjectType == utils.ProjectTypeProjectGroup {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 185730c3a..765e27da6 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -174,7 +174,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
var claGroupID string
if project.Parent == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) || project.ProjectType == utils.ProjectTypeProjectGroup {
cgm, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(utils.StringValue(input.ProjectSfid))
if perr != nil {
log.WithFields(f).WithError(err).Warn("unable to lookup CLA Group ID for this project SFID")
From 2bdc3024b09197c419d31e5247c83ad609d33ba0 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Tue, 16 Feb 2021 13:17:10 +0200
Subject: [PATCH 0087/1276] change the text
Signed-off-by: makkalot
---
cla-backend/cla/models/docusign_models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 6f47e7a17..cafdc60cb 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -1275,7 +1275,7 @@ def populate_sign_url(self, signature, callback_url=None,
email_body = f'
Hello {signatory_name},
'
email_body += f'
This is a notification email from EasyCLA regarding the project {project_name}. {cla_manager_name} has designated you as being an authorized signatory for {company_name}. In order for employees of your company to contribute to the open source project {project_name}, they must do so under a Contributor License Agreement signed by someone with authority to sign on behalf of your company.
'
email_body += f'
After you sign, {cla_manager_name} (as the initial CLA Manager for your company) will be able to maintain the list of specific employees authorized to contribute to the project under this signed CLA.
'
- email_body += f'
If you are authorized to sign on your company’s behalf, and if you approve {cla_manager_name} as your initial CLA Manager for {project_name}, please click the link below to review and sign the CLA.If you have questions, or if you are not an authorized signatory of this company, please contact the requester at {cla_manager_email}.
'
+ email_body += f'
If you are authorized to sign on your company’s behalf, and if you approve {cla_manager_name} as your initial CLA Manager for {project_name}, please review the document and sign the CLA.If you have questions, or if you are not an authorized signatory of this company, please contact the requester at {cla_manager_email}.
This is a notification email from EasyCLA regarding the organization {{.CompanyName}}.
-
The following contributor would like to submit a contribution to the {{.SigningEntityName}} CLA Group
+
The following contributor would like to submit a contribution to the {{if .SigningEntityName}}{{.SigningEntityName}}{{else}}{{.CompanyName}}{{end}} CLA Group {{.CLAGroupName}}
and is requesting to be approved as a contributor for your organization:
Approval can be done at {{.CorporateConsoleV2URL}}
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
`
)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates_test.go b/cla-backend-go/emails/v2_cla_manager_templates_test.go
index a65723807..3ac5d9d7c 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates_test.go
@@ -18,9 +18,8 @@ func TestV2ContributorApprovalRequestTemplate(t *testing.T) {
CLAGroupName: "JohnsCLAGroupName",
CompanyName: "JohnsCompany",
},
- SigningEntityName: "SigningEntityNameValue",
- UserDetails: "UserDetailsValue",
- LfxPortalURL: "http://LfxPortalURL.com",
+ UserDetails: "UserDetailsValue",
+ CorporateConsoleV2URL: "http://CorporateConsoleV2URL.com",
}
result, err := RenderTemplate(utils.V1, V2ContributorApprovalRequestTemplateName, V2ContributorApprovalRequestTemplate,
@@ -28,9 +27,20 @@ func TestV2ContributorApprovalRequestTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the organization JohnsCompany")
- assert.Contains(t, result, "contribution to the SigningEntityNameValue CLA Group")
- assert.Contains(t, result, "JohnsCLAGroupName - Signing Entity Name: UserDetailsValue")
- assert.Contains(t, result, "Approval can be done at http://LfxPortalURL.com")
+ assert.Contains(t, result, "contribution to the JohnsCompany CLA Group JohnsCLAGroupName")
+ assert.Contains(t, result, "JohnsCLAGroupName - UserDetailsValue")
+ assert.Contains(t, result, "Approval can be done at http://CorporateConsoleV2URL.com")
+
+ params.SigningEntityName = "SigningEntityNameValue"
+
+ result, err = RenderTemplate(utils.V1, V2ContributorApprovalRequestTemplateName, V2ContributorApprovalRequestTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello JohnsClaManager")
+ assert.Contains(t, result, "regarding the organization JohnsCompany")
+ assert.Contains(t, result, "contribution to the SigningEntityNameValue CLA Group JohnsCLAGroupName")
+ assert.Contains(t, result, "JohnsCLAGroupName - UserDetailsValue")
+ assert.Contains(t, result, "Approval can be done at http://CorporateConsoleV2URL.com")
}
func TestV2OrgAdminTemplate(t *testing.T) {
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index be09ea7dc..d3d279202 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -37,7 +37,7 @@ const (
)
// Configure is the API handler routine for CLA Manager routes
-func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1Company.IService, LfxPortalURL string, projectClaGroupRepo projects_cla_groups.Repository, easyCLAUserRepo v1User.RepositoryService) { // nolint
+func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1Company.IService, LfxPortalURL, CorporateConsoleV2URL string, projectClaGroupRepo projects_cla_groups.Repository, easyCLAUserRepo v1User.RepositoryService) { // nolint
api.ClaManagerCreateCLAManagerHandler = cla_manager.CreateCLAManagerHandlerFunc(func(params cla_manager.CreateCLAManagerParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
@@ -377,7 +377,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
"claGroupName": params.Body.ClaGroupName,
}
log.WithFields(f).Debug("notifying CLA managers...")
- err := service.NotifyCLAManagers(ctx, params.Body, LfxPortalURL)
+ err := service.NotifyCLAManagers(ctx, params.Body, CorporateConsoleV2URL)
if err != nil {
if err == ErrCLAUserNotFound {
msg := fmt.Sprintf("unable to notify cla managers - user not found: %s", params.Body.UserID)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 6b1c30235..67942dae9 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -99,7 +99,7 @@ type Service interface {
InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, lFxPortalURL string) ([]*models.ClaManagerDesignee, error)
CreateCLAManagerDesignee(ctx context.Context, companyID string, projectID string, userEmail string) (*models.ClaManagerDesignee, error)
CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
- NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, LfxPortalURL string) error
+ NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, CorporateConsoleV2URL string) error
CreateCLAManagerDesigneeByGroup(ctx context.Context, params cla_manager.CreateCLAManagerDesigneeByGroupParams, projectCLAGroups []*projects_cla_groups.ProjectClaGroup) ([]*models.ClaManagerDesignee, string, error)
IsCLAManagerDesignee(ctx context.Context, companySFID, claGroupID, userLFID string) (*models.UserRoleStatus, error)
}
@@ -1067,7 +1067,7 @@ func validateInviteCompanyAdmin(contactAdmin bool, userEmail string, name string
return nil
}
-func (s *service) NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, LfxPortalURL string) error {
+func (s *service) NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, CorporateConsoleV2URL string) error {
f := logrus.Fields{
"functionName": "cla_manager.service.NotifyCLAManagers",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1087,13 +1087,13 @@ func (s *service) NotifyCLAManagers(ctx context.Context, notifyCLAManagers *mode
log.Debugf("Sending notification emails to CLA Managers: %+v", notifyCLAManagers.List)
for _, claManager := range notifyCLAManagers.List {
- sendEmailToCLAManager(ctx, claManager.Name, claManager.Email.String(), userModel, notifyCLAManagers.CompanyName, notifyCLAManagers.SigningEntityName, notifyCLAManagers.ClaGroupName, LfxPortalURL)
+ sendEmailToCLAManager(ctx, claManager.Name, claManager.Email.String(), userModel, notifyCLAManagers.CompanyName, notifyCLAManagers.SigningEntityName, notifyCLAManagers.ClaGroupName, CorporateConsoleV2URL)
}
return nil
}
-func sendEmailToCLAManager(ctx context.Context, manager string, managerEmail string, userModel *v1Models.User, company, signingEntityName, claGroupName, lfxPortalURL string) {
+func sendEmailToCLAManager(ctx context.Context, manager string, managerEmail string, userModel *v1Models.User, company, signingEntityName, claGroupName, corporateConsoleV2URL string) {
f := logrus.Fields{
"functionName": "cla_manager.service.sendEmailToCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1115,9 +1115,9 @@ func sendEmailToCLAManager(ctx context.Context, manager string, managerEmail str
CompanyName: company,
CLAGroupName: claGroupName,
},
- SigningEntityName: signingEntityName,
- UserDetails: getFormattedUserDetails(userModel),
- LfxPortalURL: lfxPortalURL,
+ SigningEntityName: signingEntityName,
+ UserDetails: getFormattedUserDetails(userModel),
+ CorporateConsoleV2URL: corporateConsoleV2URL,
},
)
if err != nil {
From 4b8c4ec88cf775722f843de406418585f682e160 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 16 Feb 2021 11:17:01 -0800
Subject: [PATCH 0093/1276] Updated new user create logic/logging (#2663)
Signed-off-by: David Deal
---
cla-backend-go/cmd/server.go | 25 ++++++++++++++----
cla-backend-go/users/repository.go | 12 +++++++--
cla-backend-go/users/service.go | 42 ++++++++++++------------------
3 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 816ce5150..82cea5da4 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -591,13 +591,23 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
log.WithFields(f).WithError(err).Warn("parsing failed")
return
}
+ f["claUserName"] = claUser.Name
+ f["claUserID"] = claUser.UserID
+ f["claUserLFUsername"] = claUser.LFUsername
+ f["claUserLFEmail"] = claUser.LFEmail
+ f["claUserEmails"] = strings.Join(claUser.Emails, ",")
// search if user exist in database by username
userModel, err := usersService.GetUserByLFUserName(claUser.LFUsername)
if err != nil {
- log.WithFields(f).WithError(err).Warn("searching user by lf-username failed")
- return
+ if err, ok := err.(*utils.UserNotFound); ok {
+ log.WithFields(f).Debug("unable to locate user by lf-email")
+ } else {
+ log.WithFields(f).WithError(err).Warn("searching user by lf-username failed")
+ return
+ }
}
+ // If found - just return
if userModel != nil {
return
}
@@ -605,9 +615,14 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
// search if user exist in database by username
userModel, err = usersService.GetUserByEmail(claUser.LFEmail)
if err != nil {
- log.WithFields(f).WithError(err).Warn("searching user by lf-email failed")
- return
+ if err, ok := err.(*utils.UserNotFound); ok {
+ log.WithFields(f).Debug("unable to locate user by lf-email")
+ } else {
+ log.WithFields(f).WithError(err).Warn("searching user by lf-email failed")
+ return
+ }
}
+ // If found - just return
if userModel != nil {
return
}
@@ -618,7 +633,7 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
LfUsername: claUser.LFUsername,
Username: claUser.Name,
}
- log.WithFields(f).WithField("user", newUser).Debug("creating new user")
+ log.WithFields(f).Debug("creating new user")
userModel, err = usersService.CreateUser(newUser, nil)
if err != nil {
log.WithFields(f).WithField("user", newUser).WithError(err).Warn("creating new user failed")
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 51dc21b76..1412b91d4 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -9,6 +9,8 @@ import (
"strings"
"time"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+
"github.com/sirupsen/logrus"
"github.com/go-openapi/errors"
@@ -678,9 +680,15 @@ func (repo repository) GetUserByEmail(userEmail string) (*models.User, error) {
}
if len(dbUserModels) == 0 {
- return nil, errors.NotFound("user not found when searching by lf_email: %s", userEmail)
+ return nil, &utils.UserNotFound{
+ Message: fmt.Sprintf("user not found when searching by lf email: %s", userEmail),
+ UserLFID: "",
+ UserName: "",
+ UserEmail: userEmail,
+ Err: nil,
+ }
} else if len(dbUserModels) > 1 {
- log.WithFields(f).WithError(err).Warnf("retrieved %d results for the lf_email query when we should return 0 or 1", len(dbUserModels))
+ log.WithFields(f).Warnf("retrieved %d results for the lf_email query when we should return 0 or 1", len(dbUserModels))
}
return convertDBUserModel(dbUserModels[0]), nil
diff --git a/cla-backend-go/users/service.go b/cla-backend-go/users/service.go
index 19b918628..d1e17158f 100644
--- a/cla-backend-go/users/service.go
+++ b/cla-backend-go/users/service.go
@@ -81,6 +81,9 @@ func (s service) Save(user *models.UserUpdate, claUser *user.CLAUser) (*models.U
// Delete deletes the user record
func (s service) Delete(userID string, claUser *user.CLAUser) error {
+ if userID == "" {
+ return errors.New("userID is empty")
+ }
err := s.repo.Delete(userID)
if err != nil {
return err
@@ -100,12 +103,10 @@ func (s service) Delete(userID string, claUser *user.CLAUser) error {
// GetUser attempts to locate the user by the user id field
func (s service) GetUser(userID string) (*models.User, error) {
- userModel, err := s.repo.GetUser(userID)
- if err != nil {
- return nil, err
+ if userID == "" {
+ return nil, errors.New("userID is empty")
}
-
- return userModel, nil
+ return s.repo.GetUser(userID)
}
// GetuserByLFUserName returns the user record associated with the LF Username value
@@ -118,40 +119,29 @@ func (s service) GetUserByLFUserName(lfUserName string) (*models.User, error) {
// GetUserByUserName attempts to locate the user by the user name field
func (s service) GetUserByUserName(userName string, fullMatch bool) (*models.User, error) {
- userModel, err := s.repo.GetUserByUserName(userName, fullMatch)
- if err != nil {
- return nil, err
+ if userName == "" {
+ return nil, errors.New("username is empty")
}
-
- return userModel, nil
+ return s.repo.GetUserByUserName(userName, fullMatch)
}
// GetUserByEmail fetches the user by email
func (s service) GetUserByEmail(userEmail string) (*models.User, error) {
- userModel, err := s.repo.GetUserByEmail(userEmail)
- if err != nil {
- return nil, err
+ if userEmail == "" {
+ return nil, errors.New("userEmail is empty")
}
-
- return userModel, nil
+ return s.repo.GetUserByEmail(userEmail)
}
// GetUserByGitHubUsername fetches the user by GitHub username
func (s service) GetUserByGitHubUsername(gitHubUsername string) (*models.User, error) {
- userModel, err := s.repo.GetUserByGitHubUsername(gitHubUsername)
- if err != nil {
- return nil, err
+ if gitHubUsername == "" {
+ return nil, errors.New("gitHubUsername is empty")
}
-
- return userModel, nil
+ return s.repo.GetUserByGitHubUsername(gitHubUsername)
}
// SearchUsers attempts to locate the user by the searchField and searchTerm fields
func (s service) SearchUsers(searchField string, searchTerm string, fullMatch bool) (*models.Users, error) {
- userModel, err := s.repo.SearchUsers(searchField, searchTerm, fullMatch)
- if err != nil {
- return nil, err
- }
-
- return userModel, nil
+ return s.repo.SearchUsers(searchField, searchTerm, fullMatch)
}
From 831c861b5fc6bb61d4b87c414bb93e1449f09356 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 16 Feb 2021 14:00:47 -0800
Subject: [PATCH 0094/1276] Resolved Create Company Issue when Company Name Was
Updated By Clearbit (#2665)
- handled scenario when the company name was changed by the organization
service/clearbit on create - EasyCLA company record now honors the
SF/Clearbit name that was assigned
- Updated logging
Signed-off-by: David Deal
---
cla-backend-go/v2/company/handlers.go | 3 ++-
cla-backend-go/v2/company/service.go | 18 ++++++++++++++----
.../v2/organization-service/client.go | 8 ++------
3 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 3533154b8..3d519fd72 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -333,7 +333,8 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
"userID": params.UserID,
"companyName": aws.StringValue(params.Input.CompanyName),
"companyWebsite": aws.StringValue(params.Input.CompanyWebsite),
- "signingEntityName": aws.StringValue(¶ms.Input.SigningEntityName),
+ "signingEntityName": params.Input.SigningEntityName,
+ "userEmail": params.Input.UserEmail.String(),
}
// No permissions needed - anyone can create a company
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 47964191b..46324ed6c 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -373,7 +373,7 @@ func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID
func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityName, companyWebsite, userEmail, userID string, note string) (*models.CompanyOutput, error) {
f := logrus.Fields{
- "functionName": "CreateCompany",
+ "functionName": "service.CreateCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": companyName,
"signingEntityName": signingEntityName,
@@ -386,13 +386,22 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
// Create SalesForce company
orgClient := orgService.GetClient()
- log.WithFields(f).Debugf("Creating Organization: %s, Signing Entity Name: %s, Website: %s", companyName, signingEntityName, companyWebsite)
+ log.WithFields(f).Debugf("Creating Organization: %s, Signing Entity Name: %s, Website: %s in SalesForce...", companyName, signingEntityName, companyWebsite)
org, err := orgClient.CreateOrg(ctx, companyName, signingEntityName, companyWebsite)
if err != nil {
log.WithFields(f).Warnf("unable to create platform organization service, error: %+v", err)
return nil, err
}
+ // Company Service switched the company name based on ClearBit???
+ if org.Name != companyName {
+ log.WithFields(f).Debugf("create SalesForce company changed the company name - new name is: %s", org.Name)
+ companyName = org.Name
+ signingEntityName = org.Name
+ f["updatedCompanyName"] = org.Name
+ f["updatedSigningEntityName"] = org.Name
+ }
+
acsClient := acs_service.GetClient()
userClient := v2UserService.GetClient()
@@ -432,12 +441,13 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
}
// Create Easy CLA Company
- log.WithFields(f).Debugf("Creating EasyCLA company: %s ", companyName)
+ log.WithFields(f).Debugf("Creating EasyCLA company: %s", companyName)
if signingEntityName == "" {
- log.WithFields(f).Debugf("Setting signing entity with company name value :%s ", companyName)
+ log.WithFields(f).Debugf("Setting signing entity with company name value: %s", companyName)
signingEntityName = companyName
}
+
// OrgID used as externalID for the easyCLA Company
// Create a new company model for the create function
createCompanyModel := &v1Models.Company{
diff --git a/cla-backend-go/v2/organization-service/client.go b/cla-backend-go/v2/organization-service/client.go
index d309e23aa..eae267238 100644
--- a/cla-backend-go/v2/organization-service/client.go
+++ b/cla-backend-go/v2/organization-service/client.go
@@ -703,12 +703,8 @@ func (osc *Client) ListOrg(ctx context.Context, orgName string) (*models.Organiz
func (osc *Client) SearchOrgLookup(ctx context.Context, orgName, websiteName *string) (*organizations.LookupOK, error) {
f := logrus.Fields{
"functionName": "organization_service.Lookup",
- }
- if orgName != nil {
- f["orgName"] = *orgName
- }
- if websiteName != nil {
- f["websiteName"] = *websiteName
+ "orgName": utils.StringValue(orgName),
+ "websiteName": utils.StringValue(websiteName),
}
tok, err := token.GetToken()
From 2dd3bdbd563586b41fef1161c7e9a77b7aa1377e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 16 Feb 2021 17:12:06 -0800
Subject: [PATCH 0095/1276] Resolved #2427 Email to NonLF User (#2666)
Signed-off-by: David Deal
---
.../emails/v2_cla_manager_templates.go | 16 +++++++--------
.../emails/v2_cla_manager_templates_test.go | 7 ++++---
cla-backend-go/swagger/cla.v2.yaml | 8 +++-----
.../swagger/common/properties/user-name.yaml | 8 ++++++++
cla-backend-go/v2/cla_manager/service.go | 20 ++++++++++---------
5 files changed, 34 insertions(+), 25 deletions(-)
create mode 100644 cla-backend-go/swagger/common/properties/user-name.yaml
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index b790ece07..1d122f04d 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -150,6 +150,7 @@ type V2DesigneeToUserWithNoLFIDTemplateParams struct {
CLAManagerTemplateParams
RequesterUserName string
RequesterEmail string
+ CorporateConsole string
}
const (
@@ -157,14 +158,13 @@ const (
V2DesigneeToUserWithNoLFIDTemplateName = "V2DesigneeToUserWithNoLFIDTemplateName"
// V2DesigneeToUserWithNoLFIDTemplate is email template for
V2DesigneeToUserWithNoLFIDTemplate = `
-
Hello {{.RecipientName}},
-
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for Project {{.GetProjectNameOrFoundation}}, CLA Group {{.CLAGroupName}} and Company {{.CompanyName}} but was unable to identify your account details in the EasyCLA system
-
This email will guide you to completing the CLA Manager role assignment
-
1. Accept Invite link below will take you SSO login page where you can login with your LF Login or create a LF Login and then login.
-
2. After logging in SSO screen should direct you to CLA Corporate Console page where you will see the project you a re associated with.
-
3. Click on workflow steps to complete the signup process. Please follow this documentation to help you guide through the process - https://docs.linuxfoundation.org/lfx/v/v2/easycla/corporate-cla-manager-designee-or-initial-cla-manager/sign-corporate-cla-for-a-company
-
4. Once you have completed CLA Manager workflow you will be able to manage the approved list of contributors
This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}}.
+
The following contributor would like to contribute to {{.GetProjectNameOrFoundation}} on behalf of your organization: {{.CompanyName}}.
+
{{.RequesterUserName}} ({{.RequesterEmail}})
+
Before the user's contribution can be accepted, your organization must sign a CLA.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for the project {{.GetProjectNameOrFoundation}}.
+
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
`
)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates_test.go b/cla-backend-go/emails/v2_cla_manager_templates_test.go
index 3ac5d9d7c..e30e91021 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates_test.go
@@ -152,14 +152,15 @@ func TestV2DesigneeToUserWithNoLFIDTemplate(t *testing.T) {
},
RequesterUserName: "RequesterUserNameValue",
RequesterEmail: "RequesterEmailValue",
+ CorporateConsole: "https://corporate.dev.lfcla.com",
}
result, err := RenderTemplate(utils.V1, V2DesigneeToUserWithNoLFIDTemplateName, V2DesigneeToUserWithNoLFIDTemplate,
params)
assert.NoError(t, err)
- assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "User RequesterUserNameValue (RequesterEmailValue) was trying")
- assert.Contains(t, result, "CLA Manager for Project JohnsProjectExternal, CLA Group JohnsCLAGroupName and Company JohnsCompany")
+ assert.Contains(t, result, "Hello JohnsClaManager,")
+ assert.Contains(t, result, "The following contributor would like to contribute to JohnsProjectExternal on behalf of your organization: JohnsCompany.")
+ assert.Contains(t, result, "Kindly login to this portal https://corporate.dev.lfcla.com and sign the CLA for the project JohnsProjectExternal.")
}
func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 3113d1416..9752b6d4d 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -4763,13 +4763,11 @@ definitions:
example: false
description: send signing request as email. This should be set to true when requestor is not signatory.
authority_name:
- type: string
- example: 'John Doe'
- description: name of the cla signatory
- pattern: "^[a-zA-Z0-9]+(([',. -][a-zA-Z0-9 ])?[a-zA-Z0-9]*)*$"
+ description: the name of the CLA signatory
+ $ref: './common/properties/user-name.yaml'
authority_email:
$ref: './common/properties/email.yaml'
- description: Email of the CLA Signatory
+ description: the email of the CLA Signatory
return_url:
type: string
example: 'https://corporate.dev.lfcla.com/#/company/eb4d7d71-693f-4047-bf8d-10d0e7764969'
diff --git a/cla-backend-go/swagger/common/properties/user-name.yaml b/cla-backend-go/swagger/common/properties/user-name.yaml
new file mode 100644
index 000000000..a3f1f4819
--- /dev/null
+++ b/cla-backend-go/swagger/common/properties/user-name.yaml
@@ -0,0 +1,8 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: string
+example: "Derk Miyamoto"
+pattern: ^[a-zA-Z0-9][a-zA-Z0-9 ',.;:-_&@\#\$\(\)\+]*$
+minLength: 2
+maxLength: 255
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 67942dae9..bda760359 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -970,7 +970,7 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
}
contibutorEmail := GetNonNoReplyUserEmail(contributor.UserEmails)
- sendErr := sendDesigneeEmailToUserWithNoLFID(ctx, s.projectCGRepo, contributor.UserName, contibutorEmail, name, userEmail, organization.Name, organization.ID, sfProject.Name, &foundationSFID, "cla-manager-designee")
+ sendErr := sendDesigneeEmailToUserWithNoLFID(ctx, s.projectCGRepo, contributor.UserName, contibutorEmail, name, userEmail, organization.Name, organization.ID, sfProject.Name, &foundationSFID, "cla-manager-designee", LfxPortalURL)
if sendErr != nil {
msg := fmt.Sprintf("Problem sending email to user: %s , error: %+v", userEmail, sendErr)
log.Warn(msg)
@@ -1370,15 +1370,16 @@ func sendEmailToCLAManagerDesignee(ctx context.Context, corporateConsole string,
}
}
-func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID, projectName string, projectID *string, role string) error {
+func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID, projectName string, projectID *string, role string, corporateConsoleV2URL string) error {
f := logrus.Fields{
- "functionName": "cla_manager.service.sendDesigneeEmailToUserWithNoLFID",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "userWithNoLFIDName": userWithNoLFIDName,
- "userWithNoLFIDEmail": userWithNoLFIDEmail,
- "organizationID": organizationID,
- "projectID": utils.StringValue(projectID),
- "role": role,
+ "functionName": "cla_manager.service.sendDesigneeEmailToUserWithNoLFID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "userWithNoLFIDName": userWithNoLFIDName,
+ "userWithNoLFIDEmail": userWithNoLFIDEmail,
+ "organizationID": organizationID,
+ "projectID": utils.StringValue(projectID),
+ "role": role,
+ "corporateConsoleV2URL": corporateConsoleV2URL,
}
subject := fmt.Sprintf("EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager for project: %s ", projectName)
@@ -1391,6 +1392,7 @@ func sendDesigneeEmailToUserWithNoLFID(ctx context.Context, repository projects_
},
RequesterUserName: requesterUsername,
RequesterEmail: requesterEmail,
+ CorporateConsole: corporateConsoleV2URL,
})
if err != nil {
From 2c00a25cb3ce5f3f375d745efb216a36685aaecd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 16 Feb 2021 17:51:59 -0800
Subject: [PATCH 0096/1276] Sorted Search Results (#2667)
Signed-off-by: David Deal
---
cla-backend-go/company/service.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/cla-backend-go/company/service.go b/cla-backend-go/company/service.go
index 68c5b154b..2d3c69072 100644
--- a/cla-backend-go/company/service.go
+++ b/cla-backend-go/company/service.go
@@ -6,6 +6,8 @@ package company
import (
"context"
"fmt"
+ "sort"
+ "strings"
"github.com/sirupsen/logrus"
@@ -727,6 +729,18 @@ func (s service) SearchOrganizationByName(ctx context.Context, orgName string, w
})
}
}
+
+ // Sort the results
+ sort.Slice(result.List, func(i, j int) bool {
+ switch strings.Compare(strings.ToLower(result.List[i].OrganizationName), strings.ToLower(result.List[j].OrganizationName)) {
+ case -1:
+ return true
+ case 1:
+ return false
+ }
+ return strings.ToLower(result.List[i].OrganizationWebsite) > strings.ToLower(result.List[j].OrganizationWebsite)
+ })
+
return result, nil
}
From f9981a45c9e8f119824f6b26bc978c00f5ae46e0 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Wed, 17 Feb 2021 12:47:46 +0200
Subject: [PATCH 0097/1276] signature signed email content changed
Signed-off-by: makkalot
---
cla-backend/cla/models/docusign_models.py | 61 +++++++++++------
.../cla/tests/unit/test_docusign_models.py | 68 ++++++++++++++++++-
2 files changed, 105 insertions(+), 24 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 642bf03fd..9ffbced79 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -1775,31 +1775,11 @@ def send_signed_document(self, signature, document_data, user, icla=True):
project = Project()
project.load(signature.get_signature_project_id())
except DoesNotExist as err:
- cla.log.warning(f'{fn} - unable to load project by id: {project.get_project_id()} - '
+ cla.log.warning(f'{fn} - unable to load project by id: {signature.get_signature_project_id()} - '
'unable to send email to user')
return
- # subject = 'EasyCLA: Signed Document'
- # body = 'Thank you for signing the CLA! Your signed document is attached to this email.'
- if icla:
- pdf_link = (f'{cla.conf["API_BASE_URL"]}/v3/'
- f'signatures/{project.get_project_id()}/'
- f'{user.get_user_id()}/icla/pdf')
- else:
- pdf_link = (f'{cla.conf["API_BASE_URL"]}/v3/'
- f'signatures/{project.get_project_id()}/'
- f'{signature.get_signature_reference_id()}/ccla/pdf')
- subject = f'EasyCLA: CLA Signature Signed for {project.get_project_name()}'
- body = f'''
-
Hello {"Contributor" if icla else "CLA Signatory"},
-
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
- '''
- body = append_email_help_sign_off_content(body, project.get_version())
-
+ subject, body = document_signed_email_content(icla=icla, project=project, signature=signature, user=user)
# Third, send the email.
cla.log.debug(f'{fn} - sending signed CLA document to {recipient} with subject: {subject}')
cla.utils.get_email_service().send(subject, body, recipient)
@@ -2187,3 +2167,40 @@ def generate_manager_and_contributor_list(managers, contributors=None):
lines = '\n'.join([str(line) for line in lines])
return lines
+
+
+def document_signed_email_content(icla: bool, project: Project, signature: Signature, user: User) -> (str, str):
+ """
+ document_signed_email_content prepares the email subject and body content for the signed documents
+ :return:
+ """
+ # subject = 'EasyCLA: Signed Document'
+ # body = 'Thank you for signing the CLA! Your signed document is attached to this email.'
+ if icla:
+ pdf_link = (f'{cla.conf["API_BASE_URL"]}/v3/'
+ f'signatures/{project.get_project_id()}/'
+ f'{user.get_user_id()}/icla/pdf')
+ else:
+ pdf_link = (f'{cla.conf["API_BASE_URL"]}/v3/'
+ f'signatures/{project.get_project_id()}/'
+ f'{signature.get_signature_reference_id()}/ccla/pdf')
+
+ recipient_name = user.get_user_name() or user.get_lf_username() or None
+ # some defensive code
+ if not recipient_name:
+ if icla:
+ recipient_name = "Contributor"
+ else:
+ recipient_name = "CLA Manager"
+
+ subject = f'EasyCLA: CLA Signature Signed for {project.get_project_name()}'
+ body = f'''
+
Hello {recipient_name},
+
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
+
The CLA for {project.get_project_name()} has been signed. You can download the PDF document
+
+ from our website.
+
+ '''
+ body = append_email_help_sign_off_content(body, project.get_version())
+ return subject, body
diff --git a/cla-backend/cla/tests/unit/test_docusign_models.py b/cla-backend/cla/tests/unit/test_docusign_models.py
index 04805d75d..3e87a9c51 100644
--- a/cla-backend/cla/tests/unit/test_docusign_models.py
+++ b/cla-backend/cla/tests/unit/test_docusign_models.py
@@ -4,8 +4,8 @@
import xml.etree.ElementTree as ET
from cla.models.docusign_models import populate_signature_from_ccla_callback, populate_signature_from_icla_callback, \
- create_default_company_values
-from cla.models.dynamo_models import Signature, Company
+ create_default_company_values, document_signed_email_content
+from cla.models.dynamo_models import Signature, Company, Project, User
content_icla_agreement_date = """
Date: Wed, 17 Feb 2021 19:09:46 +0200
Subject: [PATCH 0098/1276] cla manager designee request email fixes (#2670)
Signed-off-by: makkalot
---
.../emails/v2_cla_manager_templates.go | 17 ++++++++++++++---
.../emails/v2_cla_manager_templates_test.go | 14 ++++++++++++--
cla-backend-go/v2/cla_manager/handlers.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 8 ++++----
4 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 1d122f04d..4ba4f5fdb 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -4,6 +4,8 @@
package emails
import (
+ "strings"
+
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
@@ -130,18 +132,27 @@ type V2ToCLAManagerDesigneeTemplateParams struct {
CorporateConsole string
}
+// GetProjectsOrProject returns the single Project or comma saparated projects if more than one
+func (p V2ToCLAManagerDesigneeTemplateParams) GetProjectsOrProject() string {
+ if len(p.ProjectNames) == 1 {
+ return " " + p.ProjectNames[0]
+ }
+
+ return "s " + strings.Join(p.ProjectNames, ", ")
+}
+
const (
// V2ToCLAManagerDesigneeTemplateName is email template name for V2ToCLAManagerDesigneeTemplate
V2ToCLAManagerDesigneeTemplateName = "V2ToCLAManagerDesigneeTemplateName"
// V2ToCLAManagerDesigneeTemplate is email template for
V2ToCLAManagerDesigneeTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project(s) {{range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}.
+
This is a notification email from EasyCLA regarding the project{{.GetProjectsOrProject}}.
The following contributor is requesting to sign CLA for organization:
{{.ContributorID}} ({{.ContributorName}})
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s) {{ range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}.
-
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project{{.GetProjectsOrProject}}.
+
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
")
+ assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
+ assert.Contains(t, result, "CLA for one of the projects Project1, Project2")
+
+ params.ProjectNames = []string{"Project1"}
+ result, err = RenderTemplate(utils.V1, V2ToCLAManagerDesigneeTemplateName, V2ToCLAManagerDesigneeTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello JohnsClaManager")
+ assert.Contains(t, result, "regarding the project Project1")
assert.Contains(t, result, "
ContributorIDValue (ContributorNameValue)
")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, "CLA for one of the project(s) Project1,Project2")
+ assert.Contains(t, result, "CLA for one of the project Project1")
}
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index d3d279202..cf06296a0 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -248,7 +248,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
return cla_manager.NewInviteCompanyAdminBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, userErr))
}
- claManagerDesignees, err := service.InviteCompanyAdmin(ctx, params.Body.ContactAdmin, params.Body.CompanyID, *params.Body.ClaGroupID, params.Body.UserEmail.String(), params.Body.Name, &user, LfxPortalURL)
+ claManagerDesignees, err := service.InviteCompanyAdmin(ctx, params.Body.ContactAdmin, params.Body.CompanyID, *params.Body.ClaGroupID, params.Body.UserEmail.String(), params.Body.Name, &user, LfxPortalURL, CorporateConsoleV2URL)
if err != nil {
statusCode := buildErrorStatusCode(err)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index bda760359..65152da6c 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -96,7 +96,7 @@ type service struct {
type Service interface {
CreateCLAManager(ctx context.Context, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse)
DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse
- InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, lFxPortalURL string) ([]*models.ClaManagerDesignee, error)
+ InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, lFxPortalURL, CorporateConsoleV2URL string) ([]*models.ClaManagerDesignee, error)
CreateCLAManagerDesignee(ctx context.Context, companyID string, projectID string, userEmail string) (*models.ClaManagerDesignee, error)
CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User, LfxPortalURL string) (*models.ClaManagerDesignee, error)
NotifyCLAManagers(ctx context.Context, notifyCLAManagers *models.NotifyClaManagerList, CorporateConsoleV2URL string) error
@@ -836,7 +836,7 @@ func (s *service) ValidateInviteCompanyAdminCheck(ctx context.Context, f logrus.
return nil
}
-func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, LfxPortalURL string) ([]*models.ClaManagerDesignee, error) {
+func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, LfxPortalURL, CorporateConsoleV2URL string) ([]*models.ClaManagerDesignee, error) {
f := logrus.Fields{
"functionName": "cla_manager.service.InviteCompanyAdmin",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1006,10 +1006,10 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
log.Debugf("Sending Email to CLA Manager Designee email: %s ", userEmail)
if contributor.LFUsername != "" && contributor.LFEmail != "" && len(projectSFs) > 0 {
- sendEmailToCLAManagerDesignee(ctx, LfxPortalURL, organization.Name, projectSFs, userEmail, user.Name, contributor.LFEmail, contributor.LFUsername)
+ sendEmailToCLAManagerDesignee(ctx, CorporateConsoleV2URL, organization.Name, projectSFs, userEmail, user.Name, contributor.LFEmail, contributor.LFUsername)
} else {
contributorUserName, contributorEmail := getContributorPublicEmail(contributor)
- sendEmailToCLAManagerDesignee(ctx, LfxPortalURL, organization.Name, projectSFs, userEmail, user.Name, contributorUserName, contributorEmail)
+ sendEmailToCLAManagerDesignee(ctx, CorporateConsoleV2URL, organization.Name, projectSFs, userEmail, user.Name, contributorUserName, contributorEmail)
}
log.Debugf("CLA Manager designee created : %+v", designeeScopes)
From 380be38330c2a4342d1d000cd7a39a08798dccc0 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 17 Feb 2021 20:10:18 +0300
Subject: [PATCH 0099/1276] [#2634] Bug/Contact Admin (#2671)
---
cla-backend-go/v2/cla_manager/service.go | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 65152da6c..b33e917a7 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -932,8 +932,17 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
}
for _, admin := range scopes.Userroles {
- // Check if is Gerrit User or GH User
- contributorEmailToOrgAdmin(admin.Contact.EmailAddress, admin.Contact.Name, organization.Name, projectSFs, userModel, LfxPortalURL)
+ // Email details are masked so an extra query to get user details is used
+ log.WithFields(f).Debugf("Getting email for user with ID: %s ", admin.Contact.ID)
+
+ adminUser, adminErr := userService.GetUser(admin.Contact.ID)
+ if adminErr != nil {
+ msg := fmt.Sprintf("Failed to get user for ID: %s ", admin.Contact.ID)
+ log.Warn(msg)
+ return nil, adminErr
+ }
+
+ contributorEmailToOrgAdmin(userService.GetPrimaryEmail(adminUser), admin.Contact.Name, organization.Name, projectSFs, userModel, LfxPortalURL)
designeeScope := models.ClaManagerDesignee{
Email: strfmt.Email(admin.Contact.EmailAddress),
Name: admin.Contact.Name,
From 49d558879aee12dc900a918273cc238cf40317c8 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Thu, 18 Feb 2021 15:52:33 +0300
Subject: [PATCH 0100/1276] [#2673] Feature/Sign off
- Updated Sign off content to 'EasyCLA Support Team'
Signed-off-by: wanyaland
---
cla-backend/cla/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index e9d097d90..5f49be87e 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1541,7 +1541,7 @@ def get_email_help_content(show_v2_help_link: bool) -> str:
def get_email_sign_off_content() -> str:
- return '
Thanks,
The LF Engineering Team
'
+ return '
Thanks,
EasyCLA Support Team
'
def append_email_help_sign_off_content(body: str, project_version: str) -> str:
From c542c5beeab48b4ae9fe9b09ba87b40732ef36a1 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Thu, 18 Feb 2021 15:46:53 +0200
Subject: [PATCH 0101/1276] add "Accept Invite" link to Designee to User with
no lfid email template
Signed-off-by: makkalot
---
cla-backend-go/emails/v2_cla_manager_templates.go | 3 ++-
cla-backend-go/emails/v2_cla_manager_templates_test.go | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 4ba4f5fdb..bcec889e8 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -174,7 +174,8 @@ const (
The following contributor would like to contribute to {{.GetProjectNameOrFoundation}} on behalf of your organization: {{.CompanyName}}.
{{.RequesterUserName}} ({{.RequesterEmail}})
Before the user's contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for the project {{.GetProjectNameOrFoundation}}.
+
Please click on Accept Invite to create your LF Login.
+
After login, you will be redirected to this portal {{.CorporateConsole}} where you can sign the CLA for the project {{.GetProjectNameOrFoundation}}.
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
`
)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates_test.go b/cla-backend-go/emails/v2_cla_manager_templates_test.go
index 09f037de5..ee8170b66 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates_test.go
@@ -170,7 +170,7 @@ func TestV2DesigneeToUserWithNoLFIDTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager,")
assert.Contains(t, result, "The following contributor would like to contribute to JohnsProjectExternal on behalf of your organization: JohnsCompany.")
- assert.Contains(t, result, "Kindly login to this portal https://corporate.dev.lfcla.com and sign the CLA for the project JohnsProjectExternal.")
+ assert.Contains(t, result, "you will be redirected to this portal https://corporate.dev.lfcla.com where you can sign the CLA for the project JohnsProjectExternal")
}
func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
From 3effd3d4316b455b004b226136169a7e90cc8d6d Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Thu, 18 Feb 2021 17:51:20 +0300
Subject: [PATCH 0102/1276] [#2647] Feature/Signatory Email
- Updated email content sent to CLA Manager when they are not signatory
Signed-off-by: wanyaland
---
cla-backend/cla/config.py | 1 +
cla-backend/cla/models/docusign_models.py | 14 +++++++++-----
cla-backend/cla/tests/unit/test_docusign_models.py | 2 +-
cla-backend/cla/utils.py | 9 +++++++++
cla-backend/serverless.yml | 1 +
5 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/cla-backend/cla/config.py b/cla-backend/cla/config.py
index b143d5cd9..8208f019d 100644
--- a/cla-backend/cla/config.py
+++ b/cla-backend/cla/config.py
@@ -61,6 +61,7 @@ def get_ssm_key(region, key):
# Corporate Console base URL
CORPORATE_BASE_URL = os.environ.get('CLA_CORPORATE_BASE', '')
+CORPORATE_V2_BASE_URL = os.environ.get('CLA_CORPORATE_V2_BASE', '')
# Landing Page
CLA_LANDING_PAGE = os.environ.get('CLA_LANDING_PAGE', '')
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 9ffbced79..5ddaf461b 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -29,7 +29,7 @@
from cla.models.event_types import EventType
from cla.models.s3_storage import S3Storage
from cla.user_service import UserService
-from cla.utils import get_email_help_content, append_email_help_sign_off_content
+from cla.utils import get_email_help_content, append_email_help_sign_off_content, get_corporate_url
api_base_url = os.environ.get('CLA_API_BASE', '')
root_url = os.environ.get('DOCUSIGN_ROOT_URL', '')
@@ -44,6 +44,8 @@
lf_group = LFGroup(lf_group_client_url, lf_group_client_id, lf_group_client_secret, lf_group_refresh_token)
+
+
class ProjectDoesNotExist(Exception):
pass
@@ -1895,7 +1897,7 @@ def get_org_from_return_url(repo_provider_type, return_url, orgs):
:param return_url: The URL will be redirected after signature done.
:type return_url: string
:return: List of Organizations of any repo service provider.
- :rtype: [any_repo_service_provider.Organization]
+ :rtype: [any_repo_service_provider.Organization]
"""
if repo_provider_type == 'github':
split_url = return_url.split('/') # parse repo name from URL
@@ -2184,6 +2186,8 @@ def document_signed_email_content(icla: bool, project: Project, signature: Signa
pdf_link = (f'{cla.conf["API_BASE_URL"]}/v3/'
f'signatures/{project.get_project_id()}/'
f'{signature.get_signature_reference_id()}/ccla/pdf')
+
+ corporate_url = get_corporate_url(project.get_version())
recipient_name = user.get_user_name() or user.get_lf_username() or None
# some defensive code
@@ -2194,12 +2198,12 @@ def document_signed_email_content(icla: bool, project: Project, signature: Signa
recipient_name = "CLA Manager"
subject = f'EasyCLA: CLA Signature Signed for {project.get_project_name()}'
- body = f'''
+ body = f'''
Hello {recipient_name},
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
-
The CLA for {project.get_project_name()} has been signed. You can download the PDF document
+
'''
body = append_email_help_sign_off_content(body, project.get_version())
diff --git a/cla-backend/cla/tests/unit/test_docusign_models.py b/cla-backend/cla/tests/unit/test_docusign_models.py
index 3e87a9c51..1c0f6437c 100644
--- a/cla-backend/cla/tests/unit/test_docusign_models.py
+++ b/cla-backend/cla/tests/unit/test_docusign_models.py
@@ -825,7 +825,7 @@ def test_document_signed_email_content():
assert "Signature Signed for JohnsProject" in subject
assert "Hello john" in body
assert "EasyCLA regarding the project JohnsProject" in body
- assert "The CLA for JohnsProject has been signed" in body
+ assert "The CLA has now been signed." in body
# try with different recipient names
user.set_user_name(None)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 5f49be87e..2c8d0a5c7 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -28,6 +28,8 @@
API_BASE_URL = os.environ.get('CLA_API_BASE', '')
CLA_LOGO_URL = os.environ.get('CLA_BUCKET_LOGO_URL', '')
+CORPORATE_BASE = os.environ.get('CLA_CORPORATE_BASE', '')
+CORPORATE_V2_BASE = os.environ.get('CLA_CORPORATE_V2_BASE', '')
def get_cla_path():
@@ -1543,6 +1545,13 @@ def get_email_help_content(show_v2_help_link: bool) -> str:
def get_email_sign_off_content() -> str:
return '
Thanks,
EasyCLA Support Team
'
+def get_corporate_url(project_version: str) -> str:
+ """
+ helper method that returns appropriate corporate link based on EasyCLA version
+ :param project_version: cla_group version(v1|v2)
+ :return: default is v1 corporate console
+ """
+ return CORPORATE_V2_BASE if project_version == 'v2' else CORPORATE_BASE
def append_email_help_sign_off_content(body: str, project_version: str) -> str:
"""
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index 478d8ee05..9044a474e 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -313,6 +313,7 @@ provider:
CLA_CONTRIBUTOR_BASE: ${file(./env.json):cla-contributor-base, ssm:/cla-contributor-base-${opt:stage}}
CLA_CONTRIBUTOR_V2_BASE: ${file(./env.json):cla-contributor-v2-base, ssm:/cla-contributor-v2-base-${opt:stage}}
CLA_CORPORATE_BASE: ${file(./env.json):cla-corporate-base, ssm:/cla-corporate-base-${opt:stage}}
+ CLA_CORPORATE_V2_BASE: ${file(./env.json):cla-corporate-v2-base, ssm:/cla-corporate-v2-base-${opt:stage}}
CLA_LANDING_PAGE: ${file(./env.json):cla-landing-page, ssm:/cla-landing-page-${opt:stage}}
CLA_SIGNATURE_FILES_BUCKET: ${file(./env.json):cla-signature-files-bucket, ssm:/cla-signature-files-bucket-${opt:stage}~true}
CLA_BUCKET_LOGO_URL: ${file(./env.json):cla-logo-url, ssm:/cla-logo-url-${opt:stage}~true}
From 7ecd71699b0bcdef0008c9c9b1cbe1143363b2ac Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 18 Feb 2021 18:00:49 -0800
Subject: [PATCH 0103/1276] Added Support for Bulk Add GitHub Repositories
(#2679)
Signed-off-by: David Deal
---
cla-backend-go/swagger/cla.v2.yaml | 9 +-
cla-backend-go/tests/project_helpers_test.go | 178 +++++++++++++++++++
cla-backend-go/utils/project_helpers.go | 25 +++
cla-backend-go/v2/repositories/handlers.go | 63 ++++---
cla-backend-go/v2/repositories/service.go | 151 ++++++++++------
5 files changed, 346 insertions(+), 80 deletions(-)
create mode 100644 cla-backend-go/tests/project_helpers_test.go
create mode 100644 cla-backend-go/utils/project_helpers.go
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 9752b6d4d..70b97d040 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1433,7 +1433,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/github-repository'
+ $ref: '#/definitions/list-github-repositories'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -3779,10 +3779,15 @@ definitions:
github-repository-input:
type: object
required:
- - repository_github_id
- github_organization_name
- cla_group_id
properties:
+ repository_github_ids:
+ type: array
+ items:
+ description: the repository external identifier, such as the GitHub ID of the repo
+ type: string
+ example: '337730995'
repository_github_id:
type: string
description: the repository external identifier, such as the GitHub ID of the repo
diff --git a/cla-backend-go/tests/project_helpers_test.go b/cla-backend-go/tests/project_helpers_test.go
new file mode 100644
index 000000000..4c7bfe472
--- /dev/null
+++ b/cla-backend-go/tests/project_helpers_test.go
@@ -0,0 +1,178 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/models"
+ "github.com/go-openapi/strfmt"
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ testProjectParentID = "abc1234"
+ testProjectID = "def13456"
+ testProjectLogo = "testlogurl.com"
+)
+
+func TestIsProjectHasRootParentNoParent(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = ""
+ project.Foundation = nil
+ assert.True(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - Empty Parent")
+}
+
+func TestIsProjectHasRootParentLF(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.TheLinuxFoundation,
+ }
+ assert.True(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - LF Parent")
+}
+
+func TestIsProjectHasRootParentLFProjectsLLC(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.LFProjectsLLC,
+ }
+ assert.True(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - LF Projects LLC Parent")
+}
+
+func TestIsProjectHasRootParentNonLF(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: "other",
+ }
+ assert.False(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - Non LF Project Parent")
+}
+
+func TestIsStandaloneProject(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = ""
+ project.Foundation = nil
+ project.Projects = []*models.ProjectOutput{}
+ assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with No Parent with No Children")
+}
+
+func TestLFParent(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.TheLinuxFoundation,
+ }
+ project.Projects = []*models.ProjectOutput{}
+ assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with LF Parent with No Children")
+}
+
+func TestLFProjectsLLCParent(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.LFProjectsLLC,
+ }
+ project.Projects = []*models.ProjectOutput{}
+ assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with LF Projects LLC parent with No Children")
+}
+
+func TestLFParentWithChildren(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.TheLinuxFoundation,
+ }
+ project.Projects = []*models.ProjectOutput{}
+ assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with LF Parent with Children")
+}
+
+func TestLFProjectsLLCParentWithChildren(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = &models.Foundation{
+ ID: testProjectID,
+ LogoURL: testProjectLogo,
+ Name: utils.LFProjectsLLC,
+ }
+ project.Projects = []*models.ProjectOutput{}
+ child := &models.ProjectOutput{
+ ProjectCommon: models.ProjectCommon{},
+ CreatedDate: nil,
+ DocuSignStatus: nil,
+ EndDate: nil,
+ EntityType: "",
+ ExecutiveDirector: nil,
+ Foundation: nil,
+ HerokuConnectID: "",
+ ID: testProjectID,
+ IsDeleted: false,
+ LFSponsored: false,
+ LegalParent: nil,
+ ModifiedDate: nil,
+ OpportunityOwner: nil,
+ Owner: nil,
+ ProgramManager: nil,
+ ProjectType: "SubProject",
+ RenewalOwner: nil,
+ Slug: "another-slug",
+ SystemModStamp: strfmt.DateTime{},
+ Type: "",
+ }
+ project.Projects = []*models.ProjectOutput{child}
+ assert.False(t, utils.IsStandaloneProject(project), "Standalone Project with LF Projects LLC parent with Children")
+}
+
+func TestIsProjectHaveChildrenNoChildren(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = nil
+ project.Projects = []*models.ProjectOutput{}
+ assert.False(t, utils.IsProjectHaveChildren(project), "Project has no children")
+}
+
+func TestIsProjectHaveChildrenWithChildren(t *testing.T) {
+ project := &models.ProjectOutputDetailed{}
+ project.Parent = testProjectParentID
+ project.Foundation = nil
+ child := &models.ProjectOutput{
+ ProjectCommon: models.ProjectCommon{},
+ CreatedDate: nil,
+ DocuSignStatus: nil,
+ EndDate: nil,
+ EntityType: "",
+ ExecutiveDirector: nil,
+ Foundation: nil,
+ HerokuConnectID: "",
+ ID: testProjectID,
+ IsDeleted: false,
+ LFSponsored: false,
+ LegalParent: nil,
+ ModifiedDate: nil,
+ OpportunityOwner: nil,
+ Owner: nil,
+ ProgramManager: nil,
+ ProjectType: "SubProject",
+ RenewalOwner: nil,
+ Slug: "random-slug",
+ SystemModStamp: strfmt.DateTime{},
+ Type: "",
+ }
+ project.Projects = []*models.ProjectOutput{child}
+ assert.True(t, utils.IsProjectHaveChildren(project), "Project has Children")
+}
diff --git a/cla-backend-go/utils/project_helpers.go b/cla-backend-go/utils/project_helpers.go
new file mode 100644
index 000000000..e90f3b3a1
--- /dev/null
+++ b/cla-backend-go/utils/project_helpers.go
@@ -0,0 +1,25 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/models"
+
+// IsProjectHasRootParent determines if the a given project has a root parent. A root parent is a parent that is empty parent or the parent is TLF or LFProjects
+func IsProjectHasRootParent(project *models.ProjectOutputDetailed) bool {
+ return project.Parent == "" || (project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
+}
+
+// IsStandaloneProject determines if a given project is a standalone project. A standalone project is a project with no parent or the parent is TLF/LFProjects and does not have any children
+func IsStandaloneProject(project *models.ProjectOutputDetailed) bool {
+ // standalone: No parent or parent is TLF/LFProjects....and no children
+ return (project.Parent == "" ||
+ (project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))) &&
+ len(project.Projects) == 0
+}
+
+// IsProjectHaveChildren determines if a given project has children
+func IsProjectHaveChildren(project *models.ProjectOutputDetailed) bool {
+ // a project model with a project list means it has children
+ return len(project.Projects) > 0
+}
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 8636a2f3e..14aae468c 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -82,11 +82,15 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesAddProjectGithubRepositoryHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
- "projectSFID": params.ProjectSFID,
+ "functionName": "GitHubRepositoriesAddProjectGithubRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "claGroupID": utils.StringValue(params.GithubRepositoryInput.ClaGroupID),
+ "githubOrganizationName": utils.StringValue(params.GithubRepositoryInput.GithubOrganizationName),
+ "repositoryGitHubID": params.GithubRepositoryInput.RepositoryGithubID,
+ "repositoryGitHubIDs": strings.Join(params.GithubRepositoryInput.RepositoryGithubIds, ","),
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
@@ -97,7 +101,17 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.AddGithubRepository(ctx, params.ProjectSFID, params.GithubRepositoryInput)
+ // If no repository GitHub ID values provided...
+ // RepositoryGithubID - provided by the older retool UI which provides only one value
+ // RepositoryGithubIds - provided by new PCC which passes multiple values
+ if params.GithubRepositoryInput.RepositoryGithubID == "" && len(params.GithubRepositoryInput.RepositoryGithubIds) == 0 {
+ msg := "missing repository GitHub ID value(s)"
+ log.WithFields(f).Warn(msg)
+ return github_repositories.NewAddProjectGithubRepositoryBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ results, err := service.AddGithubRepositories(ctx, params.ProjectSFID, params.GithubRepositoryInput)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryExists); ok {
msg := fmt.Sprintf("unable to add repository - repository already exists for projectSFID: %s", params.ProjectSFID)
@@ -111,23 +125,25 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- // Log the event
- eventService.LogEvent(&events.LogEventArgs{
- EventType: events.RepositoryAdded,
- ProjectID: utils.StringValue(params.GithubRepositoryInput.ClaGroupID),
- ExternalProjectID: params.ProjectSFID,
- LfUsername: authUser.UserName,
- ClaGroupModel: &v1Models.ClaGroup{
- ProjectExternalID: params.ProjectSFID,
+ // Log the events
+ for _, result := range results {
+ eventService.LogEvent(&events.LogEventArgs{
+ EventType: events.RepositoryAdded,
ProjectID: utils.StringValue(params.GithubRepositoryInput.ClaGroupID),
- },
- EventData: &events.RepositoryAddedEventData{
- RepositoryName: result.RepositoryName,
- },
- })
+ ExternalProjectID: params.ProjectSFID,
+ LfUsername: authUser.UserName,
+ ClaGroupModel: &v1Models.ClaGroup{
+ ProjectExternalID: params.ProjectSFID,
+ ProjectID: utils.StringValue(params.GithubRepositoryInput.ClaGroupID),
+ },
+ EventData: &events.RepositoryAddedEventData{
+ RepositoryName: result.RepositoryName,
+ },
+ })
+ }
- response := &models.GithubRepository{}
- err = copier.Copy(response, result)
+ var v2ResponseList []*models.GithubRepository
+ err = copier.Copy(&v2ResponseList, results)
if err != nil {
msg := fmt.Sprintf("problem converting response for projectSFID: %s", params.ProjectSFID)
log.WithFields(f).WithError(err).Warn(msg)
@@ -135,7 +151,10 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
}
- return github_repositories.NewAddProjectGithubRepositoryOK().WithPayload(response)
+ v2Response := &models.ListGithubRepositories{}
+ v2Response.List = v2ResponseList
+
+ return github_repositories.NewAddProjectGithubRepositoryOK().WithPayload(v2Response)
})
api.GithubRepositoriesDeleteProjectGithubRepositoryHandler = github_repositories.DeleteProjectGithubRepositoryHandlerFunc(
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 7a54a4cec..f42da1796 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -31,7 +31,7 @@ import (
// Service contains functions of GitHub Repositories service
type Service interface {
- AddGithubRepository(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) (*v1Models.GithubRepository, error)
+ AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error)
EnableRepository(ctx context.Context, repositoryID string) error
DisableRepository(ctx context.Context, repositoryID string) error
ListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.ListGithubRepositories, error)
@@ -70,21 +70,24 @@ func NewService(repo v1Repositories.Repository, pcgRepo projects_cla_groups.Repo
}
}
-func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) (*v1Models.GithubRepository, error) {
+func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "AddGitHubRepository",
+ "functionName": "AddGithubRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"claGroupID": utils.StringValue(input.ClaGroupID),
"githubOrganizationName": utils.StringValue(input.GithubOrganizationName),
- "repositoryGitHubID": utils.StringValue(input.RepositoryGithubID),
+ "repositoryGitHubID": input.RepositoryGithubID,
+ "repositoryGithubIds": input.RepositoryGithubIds,
}
+
psc := v2ProjectService.GetClient()
project, err := psc.GetProject(projectSFID)
if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to load projectSFID")
+ log.WithFields(f).WithError(err).Warn("unable to load projectSFID from the platform project service")
return nil, err
}
+
var externalProjectID string
if project.Parent == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
@@ -92,6 +95,7 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
} else {
externalProjectID = project.Parent
}
+
allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(aws.StringValue(input.ClaGroupID))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to get project IDs for CLA Group")
@@ -115,69 +119,104 @@ func (s *service) AddGithubRepository(ctx context.Context, projectSFID string, i
if len(org.List) == 0 {
return nil, errors.New("github app not installed on github organization")
}
- repoGithubID, err := strconv.ParseInt(utils.StringValue(input.RepositoryGithubID), 10, 64)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to convert repository github ID to an integer - invalid value")
- return nil, err
- }
- ghRepo, err := github.GetRepositoryByExternalID(ctx, org.List[0].OrganizationInstallationID, repoGithubID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to get repository by external ID")
- return nil, err
- }
- // Check if exists already
- existingRepositoryModel, lookupErr := s.GetRepositoryByName(ctx, utils.StringValue(ghRepo.FullName))
- if lookupErr != nil {
- // If we have the repository not found error - this is ok - we are expecting this
- if notFoundErr, ok := lookupErr.(*utils.GitHubRepositoryNotFound); ok {
- log.WithFields(f).WithError(notFoundErr).Debugf("GitHub repository lookup didn't find a match for existing repository name: %s - ok to create", utils.StringValue(ghRepo.FullName))
- } else {
- // Some other error - not good...
- return nil, lookupErr
- }
+ // Updated to process a list of repository IDs - take the list (may be empty) and add the single repository GH ID if it was set
+ repositoryIDList := input.RepositoryGithubIds
+ if input.RepositoryGithubID != "" {
+ repositoryIDList = append(repositoryIDList, input.RepositoryGithubID)
}
- // We already have an existing repository model with the same name
- if existingRepositoryModel != nil && !existingRepositoryModel.Enabled {
- msg := fmt.Sprintf("Github repository: %s previously disabled - will re-enabled... ", utils.StringValue(ghRepo.FullName))
- log.WithFields(f).Debug(msg)
- enabled := true
+ // Remove any silly duplicates that may come
+ repositoryIDList = utils.RemoveDuplicates(repositoryIDList)
- _, now := utils.CurrentTime()
+ var response []*v1Models.GithubRepository
- v1Input := &v1Models.GithubRepositoryInput{
- Enabled: &enabled,
- RepositoryOrganizationName: input.GithubOrganizationName,
- RepositoryProjectID: input.ClaGroupID,
- Note: fmt.Sprintf("re-enabling repository on %s.", now),
+ // For each repository ID provided...
+ // If this is slow, may want to optimize by making separate go routines for each item in the list
+ for _, repoID := range repositoryIDList {
+ // Convert the string value to an integer
+ repoGithubID, err := strconv.ParseInt(repoID, 10, 64)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to convert repository github ID %s to an integer - invalid value", repoID)
+ return nil, err
}
- // Update Repo details in case of any changes
- updatedRepository, updateErr := s.repo.UpdateGithubRepository(ctx, existingRepositoryModel.RepositoryID, v1Input)
- if updateErr != nil {
- return nil, updateErr
+ log.WithFields(f).Debugf("loading GitHub repository by external id: %d", repoGithubID)
+ ghRepo, err := github.GetRepositoryByExternalID(ctx, org.List[0].OrganizationInstallationID, repoGithubID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to load repository by external ID: %d", repoGithubID)
+ return nil, err
+ }
+ log.WithFields(f).Debugf("loaded GitHub repository by external id: %d - url: %s", repoGithubID, utils.StringValue(ghRepo.URL))
+
+ // Check if this repository exists in our database
+ log.WithFields(f).Debugf("checking if GitHub repository by name: %s exists...", utils.StringValue(ghRepo.FullName))
+ existingRepositoryModel, lookupErr := s.GetRepositoryByName(ctx, utils.StringValue(ghRepo.FullName))
+ if lookupErr != nil {
+ // If we have the repository not found error - this is ok - we are expecting this
+ if notFoundErr, ok := lookupErr.(*utils.GitHubRepositoryNotFound); ok {
+ log.WithFields(f).WithError(notFoundErr).Debugf("GitHub repository lookup didn't find a match for existing repository name: %s - ok to create", utils.StringValue(ghRepo.FullName))
+ } else {
+ // Some other error - not good...
+ log.WithFields(f).WithError(lookupErr).Warnf("GitHub repository lookup failed for repository name: %s", utils.StringValue(ghRepo.FullName))
+ return nil, lookupErr
+ }
}
- return updatedRepository, nil
- }
- if existingRepositoryModel != nil {
- msg := fmt.Sprintf("GitHub repository already exists with repository name: %s", utils.StringValue(ghRepo.FullName))
- log.WithFields(f).Warn(msg)
- return nil, &utils.GitHubRepositoryExists{
- Message: msg,
+ // We already have an existing repository model with the same name
+ if existingRepositoryModel != nil {
+ if !existingRepositoryModel.Enabled {
+ msg := fmt.Sprintf("Github repository: %s previously disabled - will re-enabled... ", utils.StringValue(ghRepo.FullName))
+ log.WithFields(f).Debug(msg)
+ enabled := true
+
+ _, now := utils.CurrentTime()
+
+ log.WithFields(f).Debugf("Updating GitHub repository - setting enabled: true, OrgName: %s, CLA Group ID: %s",
+ utils.StringValue(input.GithubOrganizationName), utils.StringValue(input.ClaGroupID))
+ v1Input := &v1Models.GithubRepositoryInput{
+ Enabled: &enabled,
+ RepositoryOrganizationName: input.GithubOrganizationName,
+ RepositoryProjectID: input.ClaGroupID,
+ Note: fmt.Sprintf("re-enabling repository on %s.", now),
+ }
+
+ // Update Repo details in case of any changes
+ updatedRepository, updateErr := s.repo.UpdateGithubRepository(ctx, existingRepositoryModel.RepositoryID, v1Input)
+ if updateErr != nil {
+ log.WithFields(f).WithError(updateErr).Warnf("unable to update GitHub repository with name: %s, id: %s, using input: %+v", utils.StringValue(ghRepo.FullName), existingRepositoryModel.RepositoryID, v1Input)
+ return nil, updateErr
+ }
+
+ // Append the results to our response model
+ response = append(response, updatedRepository)
+ } else {
+ log.WithFields(f).Warnf("GitHub repository already exists with repository name: %s and is already enabled - skipping update", utils.StringValue(ghRepo.FullName))
+ continue
+ }
+ } else {
+ // No record exists...
+ log.WithFields(f).Debug("no existing GitHub repository configured - creating...")
+ in := &v1Models.GithubRepositoryInput{
+ RepositoryExternalID: &repoID, // nolint
+ RepositoryName: ghRepo.FullName,
+ RepositoryOrganizationName: input.GithubOrganizationName,
+ RepositoryProjectID: input.ClaGroupID,
+ RepositoryType: aws.String("github"),
+ RepositoryURL: ghRepo.HTMLURL,
+ }
+
+ addedModel, addErr := s.repo.AddGithubRepository(ctx, externalProjectID, projectSFID, in)
+ if addErr != nil {
+ return nil, addErr
+ }
+
+ // Append the results to our response model
+ response = append(response, addedModel)
}
}
- in := &v1Models.GithubRepositoryInput{
- RepositoryExternalID: input.RepositoryGithubID,
- RepositoryName: ghRepo.FullName,
- RepositoryOrganizationName: input.GithubOrganizationName,
- RepositoryProjectID: input.ClaGroupID,
- RepositoryType: aws.String("github"),
- RepositoryURL: ghRepo.HTMLURL,
- }
- return s.repo.AddGithubRepository(ctx, externalProjectID, projectSFID, in)
+ return response, nil
}
func (s *service) EnableRepository(ctx context.Context, repositoryID string) error {
From 9fd2fdb254ac2af29ddbe4adfae188d5ca31cd6e Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 19 Feb 2021 13:22:15 +0300
Subject: [PATCH 0104/1276] [#LFX3038] Bug/Get Company Project CLA
- Catered for multi level (>3 levels) Projects to handle the ONAP use case
Signed-off-by: wanyaland
---
cla-backend-go/v2/company/service.go | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 46324ed6c..706e09135 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1145,8 +1145,27 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, proj
}
log.WithFields(f).Debug("loaded project SFID")
+ parentProject, err := psc.GetParentProject(projectSFID)
+ if err != nil {
+ return nil, err
+ }
+
+ log.WithFields(f).Debug("loading parent project...")
+
+ var parentProjectDetails *v2ProjectServiceModels.ProjectOutputDetailed
+
+ if parentProject != "" {
+ parentProjectDetails, err = psc.GetProject(parentProject)
+ if err != nil {
+ return nil, err
+ }
+ }
+ log.WithFields(f).Debug("loaded parent Project")
+
var allProjectMapping []*projects_cla_groups.ProjectClaGroup
- if projectDetails.ProjectType == FoundationType {
+
+ // Determine query index (foundation or project)
+ if parentProjectDetails != nil && (parentProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup) {
// get all projects for all cla group under foundation
allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForFoundation(projectSFID)
if err != nil {
From 499543ff995d9220159a67b2db33710f19ba2d29 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Thu, 18 Feb 2021 16:02:54 +0200
Subject: [PATCH 0105/1276] include project corporate console links inside of
the email templates + support the extra fetching of the data and the
rendering
Signed-off-by: makkalot
---
cla-backend-go/Makefile | 2 +-
cla-backend-go/approval_list/service.go | 4 +-
cla-backend-go/cla_manager/handlers.go | 10 +-
cla-backend-go/cla_manager/service.go | 6 +-
.../emails/approval_list_templates.go | 10 +-
.../emails/approval_list_templates_test.go | 13 +--
.../emails/cla_manager_templates.go | 105 ++++++------------
.../emails/cla_manager_templates_test.go | 76 ++++++-------
cla-backend-go/emails/params.go | 85 ++++++++++++++
cla-backend-go/emails/params_test.go | 58 ++++++++++
cla-backend-go/emails/uitls.go | 56 +++++++++-
.../emails/v2_cla_manager_templates.go | 103 +++++++++++++----
.../emails/v2_cla_manager_templates_test.go | 86 ++++++++------
cla-backend-go/v2/cla_manager/handlers.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 79 ++++++-------
15 files changed, 453 insertions(+), 242 deletions(-)
create mode 100644 cla-backend-go/emails/params.go
create mode 100644 cla-backend-go/emails/params_test.go
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 25805ac3e..28124b956 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -293,6 +293,6 @@ $(LINT_TOOL):
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell go env GOPATH)/bin $(LINT_VERSION)
lint: $(LINT_TOOL)
- @cd $(MAKEFILE_DIR) && echo "Running lint..." && $(LINT_TOOL) run --allow-parallel-runners --config=.golangci.yaml ./... && echo "Lint check passed."
+ @cd $(MAKEFILE_DIR) && echo "Running lint..." && $(LINT_TOOL) run --exclude="this method will not auto-escape HTML. Verify data is well formed" --allow-parallel-runners --config=.golangci.yaml ./... && echo "Lint check passed."
@cd $(MAKEFILE_DIR) && ./check-headers.sh
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index 1bb039f12..d9d5b34b7 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -269,7 +269,7 @@ func (s service) sendRequestEmailToRecipient(projectClaGroupRepository projects_
emails.RequestToAuthorizeTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
ContributorName: contributorName,
@@ -327,7 +327,7 @@ func (s service) sendRequestRejectedEmailToRecipient(companyModel *models.Compan
emails.ApprovalListRejectedTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
CLAManagers: emailCLAManagerParams,
},
diff --git a/cla-backend-go/cla_manager/handlers.go b/cla-backend-go/cla_manager/handlers.go
index 764e728c4..0af1dcb55 100644
--- a/cla-backend-go/cla_manager/handlers.go
+++ b/cla-backend-go/cla_manager/handlers.go
@@ -893,7 +893,7 @@ func sendRequestAccessEmailToCLAManagers(companyModel *models.Company, claGroupM
emails.RequestAccessToCLAManagersTemplate, emails.RequestAccessToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
RequesterName: requesterName,
@@ -927,7 +927,7 @@ func sendRequestApprovedEmailToCLAManagers(companyModel *models.Company, claGrou
emails.RequestApprovedToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
RequesterName: requesterName,
@@ -957,7 +957,7 @@ func sendRequestApprovedEmailToRequester(companyModel *models.Company, claGroupM
emails.RequestApprovedToRequesterTemplate, emails.RequestApprovedToRequesterTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: requesterName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
CorporateURL: utils.GetCorporateURL(claGroupModel.Version == utils.V2),
@@ -988,7 +988,7 @@ func sendRequestDeniedEmailToCLAManagers(companyModel *models.Company, claGroupM
emails.RequestDeniedToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
RequesterName: requesterName,
@@ -1021,7 +1021,7 @@ func sendRequestDeniedEmailToRequester(companyModel *models.Company, claGroupMod
emails.RequestDeniedToRequesterTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: requesterName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
})
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index 6579c1370..7fc330117 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -368,7 +368,7 @@ func sendClaManagerAddedEmailToUser(companyModel *models.Company, claGroupModel
emails.ClaManagerAddedEToUserTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: requesterName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
CorporateURL: utils.GetCorporateURL(claGroupModel.Version == utils.V2),
@@ -399,7 +399,7 @@ func sendClaManagerAddedEmailToCLAManagers(companyModel *models.Company, claGrou
emails.ClaManagerAddedToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
Name: name,
@@ -503,7 +503,7 @@ func sendClaManagerDeleteEmailToCLAManagers(companyModel *models.Company, claGro
emails.ClaManagerDeletedToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
- ProjectName: projectName,
+ Project: emails.CLAProjectParams{ExternalProjectName: projectName},
CompanyName: companyName,
},
Name: name,
diff --git a/cla-backend-go/emails/approval_list_templates.go b/cla-backend-go/emails/approval_list_templates.go
index 2baf301c1..478cf4cac 100644
--- a/cla-backend-go/emails/approval_list_templates.go
+++ b/cla-backend-go/emails/approval_list_templates.go
@@ -16,8 +16,8 @@ const (
// ApprovalListRejectedTemplate is email template for
ApprovalListRejectedTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
Your request to get added to the approval list from {{.CompanyName}} for {{.ProjectName}} was denied by one of the existing CLA Managers.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
Your request to get added to the approval list from {{.CompanyName}} for {{.Project.ExternalProjectName}} was denied by one of the existing CLA Managers.
If you have further questions about this denial, please contact one of the existing CLA Managers from
{{.CompanyName}} for {{.CompanyName}}:
@@ -46,8 +46,8 @@ const (
Hello {{.RecipientName}},
This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}}.
{{.ContributorName}} ({{.ContributorEmail}}) has requested to be added to the Approved List as an authorized contributor from
-{{.CompanyName}} to the project {{.ProjectName}}. You are receiving this message as a CLA Manager from {{.CompanyName}} for
-{{.ProjectName}}.
+{{.CompanyName}} to the project {{.Project.ExternalProjectName}}. You are receiving this message as a CLA Manager from {{.CompanyName}} for
+{{.Project.ExternalProjectName}}.
{{if .OptionalMessage}}
{{.ContributorName}} included the following message in the request:
{{.OptionalMessage}}
@@ -56,7 +56,7 @@ const (
log into the EasyCLA Corporate
Console, where you can approve this user's request by selecting the 'Manage Approved List' and adding the
contributor's email, the contributor's entire email domain, their GitHub ID or the entire GitHub Organization for the
-repository. This will permit them to begin contributing to {{.ProjectName}} on behalf of {{.CompanyName}}.
+repository. This will permit them to begin contributing to {{.Project.ExternalProjectName}} on behalf of {{.CompanyName}}.
If you are not certain whether to add them to the Approved List, please reach out to them directly to discuss.
If you want to permit this, please log into the EasyCLA Corporate Console,
-select your company, then select the {{.ProjectName}} project. From the CLA Manager requests, you can approve this user as an
+select your company, then select the {{.Project.ExternalProjectName}} project. From the CLA Manager requests, you can approve this user as an
additional CLA Manager.
`
)
@@ -122,10 +83,10 @@ const (
// RequestApprovedToCLAManagersTemplate is email template for
RequestApprovedToCLAManagersTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
The following user has been approved as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they can now
-maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the
-list of company’s CLA Managers for {{.ProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
The following user has been approved as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that they can now
+maintain the list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the
+list of company’s CLA Managers for {{.Project.ExternalProjectName}}.
{{.RequesterName}} ({{.RequesterEmail}})
@@ -144,12 +105,12 @@ const (
// RequestApprovedToRequesterTemplate is email template for
RequestApprovedToRequesterTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
You have now been approved as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can now maintain the
-list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for {{.ProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
You have now been approved as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
+list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
+company’s CLA Managers for {{.Project.ExternalProjectName}}.
To get started, please log into the EasyCLA Corporate Console, and select your
-company and then the project {{.ProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
+company and then the project {{.Project.ExternalProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
`
)
@@ -166,9 +127,9 @@ const (
// RequestDeniedToCLAManagersTemplate is email template for
RequestDeniedToCLAManagersTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
The following user has been denied as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they will not
-be able to maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
The following user has been denied as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that they will not
+be able to maintain the list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company.
{{.RequesterName}} ({{.RequesterEmail}})
@@ -186,9 +147,9 @@ const (
// RequestDeniedToRequesterTemplate is email template for
RequestDeniedToRequesterTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
You have been denied as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can not maintain the
-list of employees allowed to contribute to {{.ProjectName}} on behalf of your company.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
You have been denied as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that you can not maintain the
+list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company.
`
)
@@ -204,12 +165,12 @@ const (
// ClaManagerAddedEToUserTemplate is email template for
ClaManagerAddedEToUserTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that you can now maintain the
-list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for {{.ProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
+list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
+company’s CLA Managers for {{.Project.ExternalProjectName}}.
To get started, please log into the EasyCLA Corporate Console, and select your
-company and then the project {{.ProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
+company and then the project {{.Project.ExternalProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
`
)
@@ -226,10 +187,10 @@ const (
// ClaManagerAddedToCLAManagersTemplate is email template for
ClaManagerAddedToCLAManagersTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
The following user has been added as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}. This means that they can now
-maintain the list of employees allowed to contribute to {{.ProjectName}} on behalf of your company, as well as view and manage the
-list of company’s CLA Managers for {{.ProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
The following user has been added as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that they can now
+maintain the list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the
+list of company’s CLA Managers for {{.Project.ExternalProjectName}}.
{{.Name}} ({{.Email}})
@@ -249,7 +210,7 @@ const (
// ClaManagerDeletedToCLAManagersTemplate is template for
ClaManagerDeletedToCLAManagersTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.ProjectName}}.
-
{{.Name}} ({{.Email}}) has been removed as a CLA Manager from {{.CompanyName}} for the project {{.ProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
{{.Name}} ({{.Email}}) has been removed as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}.
This is a notification email from EasyCLA regarding the CLA setup and signing process for {{.CompanyName}}.
-
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for {{.CompanyName}} in support of the following projects:
+
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for {{.CompanyName}} in support of the following project:
- {{range .ProjectList}}
-
{{.}}
- {{end}}
+
{{.Project.ExternalProjectName}}
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom to designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.ProjectName}}
+Either you or someone whom to designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.Project.GetProjectFullURL}}
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
Please notify the user once CLA setup is complete.
This is a notification email from EasyCLA regarding the project(s) {{range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}
+
This is a notification email from EasyCLA regarding the project(s) {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.ExternalProjectName}}{{end}}
The following contributor is requesting to sign CLA for organization: {{.CompanyName}}
{{.UserDetails}}
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the projects {{range $index, $projectName := .ProjectNames}}{{if $index}},{{end}}{{$projectName}}{{end}}.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the projects {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
This is a notification email from EasyCLA regarding the CLA setup and signing process for {{.CompanyName}}.
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for {{.CompanyName}} in support of the following projects:
- {{range .ProjectList}}
-
{{.}}
- {{end}}
+
{{.Project.ExternalProjectName}}
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom to designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.ProjectName}}
+Either you or someone whom to designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.Project.GetProjectFullURL}}
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
Please notify the user once CLA setup is complete.
The following contributor is requesting to sign CLA for organization:
{{.ContributorID}} ({{.ContributorName}})
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project{{.GetProjectsOrProject}}.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s) {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
Before the user's contribution can be accepted, your organization must sign a CLA.
Please click on Accept Invite to create your LF Login.
-
After login, you will be redirected to this portal {{.CorporateConsole}} where you can sign the CLA for the project {{.GetProjectNameOrFoundation}}.
+
After login, you will be redirected to this portal {{.CorporateConsole}} where you can sign the CLA for the project {{.Project.GetProjectFullURL}}.
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
`
)
@@ -186,6 +239,9 @@ func RenderV2DesigneeToUserWithNoLFIDTemplate(repository projects_cla_groups.Rep
return "", err
}
+ // assign the corporate console so we can show the link of the project
+ params.Project.CorporateConsole = params.CorporateConsole
+
return RenderTemplate(utils.V2, V2DesigneeToUserWithNoLFIDTemplateName,
V2DesigneeToUserWithNoLFIDTemplate, params)
}
@@ -204,8 +260,8 @@ const (
V2CLAManagerToUserWithNoLFIDTemplate = `
Hello {{.RecipientName}},
This is a notification email from EasyCLA regarding the Project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}} in the EasyCLA system.
-
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for Project {{.ProjectName}} but was unable to identify your account details in
-the EasyCLA system. In order to become a CLA Manager for Project {{.ProjectName}}, you will need to accept invite below.
+
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for Project {{.Project.ExternalProjectName}} but was unable to identify your account details in
+the EasyCLA system. In order to become a CLA Manager for Project {{.Project.ExternalProjectName}}, you will need to accept invite below.
Once complete, notify the user {{.RequesterUserName}} and they will be able to add you as a CLA Manager.
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
-
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
+
This is a notification email from EasyCLA regarding the project {{.CLAGroupName}}.
+
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.CLAGroupName}}. This means that you can now maintain the
list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for {{.Project.ExternalProjectName}}.
+company’s CLA Managers for {{.CLAGroupName}}.
+
To get started, please log into the EasyCLA Corporate Console, and select your
+company and then the project {{.CLAGroupName}}. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}} and CLA Group {{.CLAGroupName}}.
+
You have been added as a CLA Manager for the organization {{.CompanyName}} and the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
+list of employees allowed to contribute to the project {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
+company’s CLA Managers for the CLA Group {{.CLAGroupName}}.
To get started, please log into the EasyCLA Corporate Console, and select your
company and then the project {{.Project.ExternalProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the organization {{.CompanyName}}.
-
The following contributor would like to submit a contribution to the {{if .SigningEntityName}}{{.SigningEntityName}}{{else}}{{.CompanyName}}{{end}} CLA Group {{.CLAGroupName}}
-and is requesting to be approved as a contributor for your organization:
-
{{.CLAGroupName}} - {{.UserDetails}}
+
The following contributor would like to submit a contribution to the CLA Group {{.CLAGroupName}} and is requesting to be approved as a contributor for your organization:
+
{{.UserDetails}}
Approval can be done at {{.CorporateConsoleV2URL}}
-
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
+
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
`
)
@@ -49,8 +48,8 @@ const (
// V2OrgAdminTemplate is email template for
V2OrgAdminTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the CLA setup and signing process for {{.CompanyName}}.
-
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for {{.CompanyName}} in support of the following project:
+
This is a notification email from EasyCLA regarding the CLA setup and signing process for the organization {{.CompanyName}}.
+
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA in support of the following project(s):
This is a notification email from EasyCLA regarding the project(s) {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.ExternalProjectName}}{{end}}
-
The following contributor is requesting to sign CLA for organization: {{.CompanyName}}
+
The following contributor is requesting to sign the CLA for the organization: {{.CompanyName}}
{{.UserDetails}}
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the projects {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
-
Please notify the contributor once they are added to the approved list of contributors so that they can complete their code contribution.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
+
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
`
)
@@ -128,8 +127,8 @@ const (
// V2CLAManagerDesigneeCorporateTemplate is email template for
V2CLAManagerDesigneeCorporateTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the CLA setup and signing process for {{.CompanyName}}.
-
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for {{.CompanyName}} in support of the following projects:
+
This is a notification email from EasyCLA regarding the CLA setup and signing process for the organization {{.CompanyName}}.
+
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for the organization {{.CompanyName}} in support of the following project(s):
{{.Project.ExternalProjectName}}
@@ -164,12 +163,13 @@ type V2ToCLAManagerDesigneeTemplateParams struct {
ContributorID string
ContributorName string
CorporateConsole string
+ CompanyName string
}
-// GetProjectsOrProject returns the single Project or comma saparated projects if more than one
+// GetProjectsOrProject returns the single Project or comma separated projects if more than one
func (p V2ToCLAManagerDesigneeTemplateParams) GetProjectsOrProject() string {
if len(p.Projects) == 1 {
- return " " + p.Projects[0].ExternalProjectName
+ return p.Projects[0].ExternalProjectName
}
var projectNames []string
@@ -177,7 +177,7 @@ func (p V2ToCLAManagerDesigneeTemplateParams) GetProjectsOrProject() string {
projectNames = append(projectNames, p.ExternalProjectName)
}
- return "s " + strings.Join(projectNames, ", ")
+ return strings.Join(projectNames, ", ")
}
const (
@@ -186,12 +186,12 @@ const (
// V2ToCLAManagerDesigneeTemplate is email template for
V2ToCLAManagerDesigneeTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project{{.GetProjectsOrProject}}.
-
The following contributor is requesting to sign CLA for organization:
+
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
+
The following contributor is requesting to sign the CLA for the organization {{.CompanyName}}:
{{.ContributorID}} ({{.ContributorName}})
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s) {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
-
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
+
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they can complete the contribution process.
`
)
@@ -226,10 +226,10 @@ const (
This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}}.
The following contributor would like to contribute to {{.GetProjectNameOrFoundation}} on behalf of your organization: {{.CompanyName}}.
{{.RequesterUserName}} ({{.RequesterEmail}})
-
Before the user's contribution can be accepted, your organization must sign a CLA.
+
Before the user contribution can be accepted, your organization must sign a CLA.
Please click on Accept Invite to create your LF Login.
After login, you will be redirected to this portal {{.CorporateConsole}} where you can sign the CLA for the project {{.Project.GetProjectFullURL}}.
-
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they may complete the contribution process.
+
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they can complete the contribution process.
This is a notification email from EasyCLA regarding the Project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}} in the EasyCLA system.
-
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for Project {{.Project.ExternalProjectName}} but was unable to identify your account details in
-the EasyCLA system. In order to become a CLA Manager for Project {{.Project.ExternalProjectName}}, you will need to accept invite below.
+
This is a notification email from EasyCLA regarding the Project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}}.
+
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for the Project {{.Project.ExternalProjectName}} but was unable to identify your account details in
+the EasyCLA system. In order to become a CLA Manager for the Project {{.Project.ExternalProjectName}}, you will need to accept the invite below.
Once complete, notify the user {{.RequesterUserName}} and they will be able to add you as a CLA Manager.
`
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 97e8bd13e..7018ea7b5 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -29,8 +29,8 @@ func TestV2ContributorApprovalRequestTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the organization JohnsCompany")
- assert.Contains(t, result, "contribution to the JohnsCompany CLA Group JohnsCLAGroupName")
- assert.Contains(t, result, "JohnsCLAGroupName - UserDetailsValue")
+ assert.Contains(t, result, "contribution to the CLA Group JohnsCLAGroupName")
+ assert.Contains(t, result, "UserDetailsValue")
assert.Contains(t, result, "Approval can be done at http://CorporateConsoleV2URL.com")
params.SigningEntityName = "SigningEntityNameValue"
@@ -40,8 +40,8 @@ func TestV2ContributorApprovalRequestTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the organization JohnsCompany")
- assert.Contains(t, result, "contribution to the SigningEntityNameValue CLA Group JohnsCLAGroupName")
- assert.Contains(t, result, "JohnsCLAGroupName - UserDetailsValue")
+ assert.Contains(t, result, "contribution to the CLA Group JohnsCLAGroupName")
+ assert.Contains(t, result, "UserDetailsValue")
assert.Contains(t, result, "Approval can be done at http://CorporateConsoleV2URL.com")
}
@@ -67,9 +67,9 @@ func TestV2OrgAdminTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "signing process for JohnsCompany")
+ assert.Contains(t, result, "signing process for the organization JohnsCompany")
assert.Contains(t, result, "SenderNameValue SenderEmailValue has identified you")
- assert.Contains(t, result, "Corporate CLA for JohnsCompany")
+ assert.Contains(t, result, "Corporate CLA in support of the following project(s):")
assert.Contains(t, result, "
JohnsProject
")
assert.Contains(t, result, "can login to this portal (http://CorporateConsole.com)")
assert.Contains(t, result, `sign the CLA for this project JohnsProject`)
@@ -96,10 +96,10 @@ func TestV2ContributorToOrgAdminTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s) Project1,Project2")
- assert.Contains(t, result, "sign CLA for organization: JohnsCompany")
+ assert.Contains(t, result, "sign the CLA for the organization: JohnsCompany")
assert.Contains(t, result, "
UserDetailsValue
")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, `CLA for any of the projects Project1,Project2`)
+ assert.Contains(t, result, `CLA for any of the project(s): Project1,Project2`)
}
func TestV2CLAManagerDesigneeCorporateTemplate(t *testing.T) {
@@ -124,9 +124,9 @@ func TestV2CLAManagerDesigneeCorporateTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "CLA setup and signing process for JohnsCompany")
+ assert.Contains(t, result, "CLA setup and signing process for the organization JohnsCompany")
assert.Contains(t, result, "SenderNameValue SenderEmailValue has identified you")
- assert.Contains(t, result, "Corporate CLA for JohnsCompany")
+ assert.Contains(t, result, "Corporate CLA for the organization JohnsCompany")
assert.Contains(t, result, "
JohnsProject
")
assert.Contains(t, result, "can login to this portal (http://CorporateConsole.com)")
assert.Contains(t, result, `sign the CLA for this project JohnsProject`)
@@ -148,10 +148,10 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the projects Project1, Project2")
+ assert.Contains(t, result, "regarding the project(s): Project1, Project2")
assert.Contains(t, result, "
ContributorIDValue (ContributorNameValue)
")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, `CLA for one of the project(s) Project1,Project2`)
+ assert.Contains(t, result, `CLA for one of the project(s): Project1,Project2`)
params.Projects = []emails.CLAProjectParams{
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
@@ -160,10 +160,10 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the project Project1")
+ assert.Contains(t, result, "regarding the project(s): Project1")
assert.Contains(t, result, "
ContributorIDValue (ContributorNameValue)
")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, `CLA for one of the project(s) Project1`)
+ assert.Contains(t, result, `CLA for one of the project(s): Project1`)
}
@@ -210,8 +210,8 @@ func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the Project JohnsProjectExternal and CLA Group JohnsCLAGroupName in the")
+ assert.Contains(t, result, "regarding the Project JohnsProjectExternal and CLA Group JohnsCLAGroupName")
assert.Contains(t, result, "User RequesterUserNameValue (RequesterEmailValue) was trying")
- assert.Contains(t, result, "CLA Manager for Project JohnsProject")
+ assert.Contains(t, result, "CLA Manager for the Project JohnsProject")
assert.Contains(t, result, "notify the user RequesterUserNameValue")
}
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 8b4591ce8..781a4d3cb 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -234,7 +234,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
}
// Add CLA Manager to Database
- signature, addErr := s.managerService.AddClaManager(ctx, v1CompanyModel.CompanyID, claGroupID, user.Username)
+ signature, addErr := s.managerService.AddClaManager(ctx, v1CompanyModel.CompanyID, claGroupID, user.Username, projectSF.Name)
if addErr != nil {
msg := buildErrorMessageCreate(params, addErr)
log.WithFields(f).Warn(msg)
From 17a4fc7efcce49e3414c742cc5e7a585b1ab8e22 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Mon, 22 Feb 2021 17:29:00 +0300
Subject: [PATCH 0110/1276] Bug/ Get Foundation CLA Groups
- Resolved foundation cla-groups GET endpoint
Signed-off-by: wanyaland
---
cla-backend-go/v2/cla_groups/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index f1515ff3a..35c7ba620 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -433,7 +433,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1CLAGroupData)
}
- } else if parentDetails != nil && (parentDetails.ProjectType == utils.ProjectTypeProjectGroup && sfProjectModelDetails.ProjectType != utils.ProjectTypeProjectGroup) {
+ } else if (parentDetails != nil && (parentDetails.ProjectType == utils.ProjectTypeProjectGroup && sfProjectModelDetails.ProjectType != utils.ProjectTypeProjectGroup)) || (sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup) {
log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
projectCLAGroups, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
if lookupErr != nil {
From abbbaa551ca3b5ca2a3d4115d54f08025dc5c668 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 22 Feb 2021 18:08:48 +0200
Subject: [PATCH 0111/1276] [#2632]handling cla group name change and
reflecting that to cla-%s-projects-cla-groups table (#2690)
cla-%s-projects-cla-groups table
Signed-off-by: makkalot
---
.../projects_cla_groups/repository.go | 55 +++++++++++++++++++
.../v2/dynamo_events/cla_groups_db_handler.go | 31 +++++++++--
2 files changed, 80 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/projects_cla_groups/repository.go b/cla-backend-go/projects_cla_groups/repository.go
index a6ff2d22a..0126440c5 100644
--- a/cla-backend-go/projects_cla_groups/repository.go
+++ b/cla-backend-go/projects_cla_groups/repository.go
@@ -54,6 +54,7 @@ type Repository interface {
IsExistingFoundationLevelCLAGroup(foundationSFID string) (bool, error)
IsAssociated(projectSFID string, claGroupID string) (bool, error)
UpdateRepositoriesCount(projectSFID string, diff int64, reset bool) error
+ UpdateClaGroupName(projectSFID string, claGroupName string) error
}
type repo struct {
@@ -456,6 +457,60 @@ func (repo *repo) UpdateRepositoriesCount(projectSFID string, diff int64, reset
return updateErr
}
+// UpdateClaGroupName updates cla group name for given projectSFID
+func (repo *repo) UpdateClaGroupName(projectSFID string, claGroupName string) error {
+ f := logrus.Fields{
+ "functionName": "project_cla_groups.repository.UpdateClaGroupName",
+ "projectSFID": projectSFID,
+ "claGroupName": claGroupName,
+ }
+
+ // Check to see if we have an existing record
+ existingProjectCLAGroupMapping, err := repo.GetClaGroupIDForProject(projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to lookup existing project cla group mapping")
+ return err
+ }
+ if existingProjectCLAGroupMapping == nil {
+ log.WithFields(f).Warn("unable to lookup existing project cla group mapping - response is empty")
+ return &utils.ProjectCLAGroupMappingNotFound{
+ ProjectSFID: projectSFID,
+ CLAGroupID: "",
+ Err: nil,
+ }
+ }
+
+ expressionAttributeNames := map[string]*string{}
+ expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
+ var updateExpression string
+
+ // update repositories_count based on reset flag
+ expressionAttributeNames["#N"] = aws.String("cla_group_name")
+ expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: &claGroupName}
+ updateExpression = "SET #N = :n"
+
+ _, now := utils.CurrentTime()
+ expressionAttributeNames["#M"] = aws.String("date_modified")
+ expressionAttributeValues[":m"] = &dynamodb.AttributeValue{S: aws.String(now)}
+ updateExpression = updateExpression + ", #M = :m"
+
+ _, updateErr := repo.dynamoDBClient.UpdateItem(&dynamodb.UpdateItemInput{
+ UpdateExpression: aws.String(updateExpression),
+ ExpressionAttributeNames: expressionAttributeNames,
+ ExpressionAttributeValues: expressionAttributeValues,
+ Key: map[string]*dynamodb.AttributeValue{
+ "project_sfid": {S: aws.String(projectSFID)},
+ },
+ TableName: aws.String(repo.tableName),
+ })
+
+ if updateErr != nil {
+ log.WithFields(f).WithError(updateErr).Warn("update cla group name failed")
+ }
+
+ return updateErr
+}
+
// IsExistingFoundationLevelCLAGroup is a query helper function to determine if the
// specified foundation SFID has an entry in the mapping table to signify that
// it's a foundation level CLA Group (foundationSFID == projectSFID)
diff --git a/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go b/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
index 265c82e32..f83ef6d99 100644
--- a/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
+++ b/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
@@ -23,12 +23,19 @@ func (s *service) ProcessCLAGroupUpdateEvents(event events.DynamoDBEventRecord)
log.WithFields(f).Debug("processing event")
- var updatedProject project.DBProjectModel
+ var oldProject, updatedProject project.DBProjectModel
err := unmarshalStreamImage(event.Change.NewImage, &updatedProject)
if err != nil {
- log.WithFields(f).Warnf("unable to unmarshal project model, error: %+v", err)
+ log.WithFields(f).Warnf("unable to unmarshal new project model, error: %+v", err)
return err
}
+
+ err = unmarshalStreamImage(event.Change.OldImage, &oldProject)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to unmarshal old project model, error: %+v", err)
+ return err
+ }
+
log.WithFields(f).Debugf("decoded project record from stream: %+v", updatedProject)
// Update any DB records that have CLA Approval Requests from Contributors - need to update Name, etc. if that has changed
@@ -44,13 +51,25 @@ func (s *service) ProcessCLAGroupUpdateEvents(event events.DynamoDBEventRecord)
log.WithFields(f).Warnf("unable to update cla manager request with updated CLA Group information, error: %+v", approvalListRequestErr)
}
+ if oldProject.ProjectName != updatedProject.ProjectName {
+ claProjects, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(updatedProject.ProjectID)
+ if err != nil {
+ log.WithFields(f).Warnf("unabled to update cla group name : %v", err)
+ return nil
+ }
+
+ for _, claProject := range claProjects {
+ if err := s.projectsClaGroupRepo.UpdateClaGroupName(claProject.ProjectSFID, updatedProject.ProjectName); err != nil {
+ log.WithFields(f).Warnf("updating cla project : %s with name : %s failed : %v", claProject.ProjectSFID, updatedProject.ProjectName, err)
+ return nil
+ }
+ }
+ log.WithFields(f).Infof("updating related cla projects with name : %s", updatedProject.ProjectName)
+ }
+
// TODO - update other tables:
// cla-%s-metrics,
- // cla-%s-projects-cla-groups,
// cla-%s-gerrit-instances,
- // possibly add/update cla_group_name/project_name to other tables:
- // cla-%-repositories
- // cla-%-signatures
return nil
}
From 7f99586a90353431ac6d6c35e0163e3909e70356 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Mon, 22 Feb 2021 23:57:16 +0300
Subject: [PATCH 0112/1276] Bug/Get Foundation|Project CLA groups (#2695)
- Resolved GET cla-groups endpoint for SF Project and Company endpoints
Signed-off-by: wanyaland
---
cla-backend-go/project/repository.go | 2 ++
cla-backend-go/utils/project_helpers.go | 5 +++++
cla-backend-go/v2/cla_groups/service.go | 4 ++--
cla-backend-go/v2/company/service.go | 2 +-
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 796d5482b..2dfa9863f 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -354,6 +354,8 @@ func (repo *repo) GetClaGroupsByFoundationSFID(ctx context.Context, foundationSF
}
}
+ log.WithFields(f).Debugf("foundation projects!: %#v ", projects)
+
return &models.ClaGroups{
ResultCount: int64(len(projects)),
Projects: projects,
diff --git a/cla-backend-go/utils/project_helpers.go b/cla-backend-go/utils/project_helpers.go
index e90f3b3a1..3457fc55d 100644
--- a/cla-backend-go/utils/project_helpers.go
+++ b/cla-backend-go/utils/project_helpers.go
@@ -23,3 +23,8 @@ func IsProjectHaveChildren(project *models.ProjectOutputDetailed) bool {
// a project model with a project list means it has children
return len(project.Projects) > 0
}
+
+// IsProjectCategory determines if a given project is categorised as cla project sfid
+func IsProjectCategory(project *models.ProjectOutputDetailed, parent *models.ProjectOutputDetailed) bool {
+ return project.ProjectType == ProjectTypeProject || (!IsProjectHasRootParent(project) && parent.ProjectType == ProjectTypeProjectGroup && project.ProjectType == ProjectTypeProjectGroup)
+}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 35c7ba620..c21486fd6 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -390,7 +390,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
var foundationName = sfProjectModelDetails.Name
// If it's a project...
- if sfProjectModelDetails.ProjectType == utils.ProjectTypeProject || (parentDetails != nil && (parentDetails.ProjectType == utils.ProjectTypeProjectGroup && sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup)) {
+ if utils.IsProjectCategory(sfProjectModelDetails, parentDetails) {
// Since this is a project and not a foundation, we'll want to set he parent foundation ID and name (which is
// our parent in this case)
log.WithFields(f).Debug("found 'project' in platform project service.")
@@ -433,7 +433,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1CLAGroupData)
}
- } else if (parentDetails != nil && (parentDetails.ProjectType == utils.ProjectTypeProjectGroup && sfProjectModelDetails.ProjectType != utils.ProjectTypeProjectGroup)) || (sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup) {
+ } else if sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup {
log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
projectCLAGroups, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
if lookupErr != nil {
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 706e09135..6d77153ea 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1165,7 +1165,7 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, proj
var allProjectMapping []*projects_cla_groups.ProjectClaGroup
// Determine query index (foundation or project)
- if parentProjectDetails != nil && (parentProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup) {
+ if !utils.IsProjectCategory(projectDetails, parentProjectDetails) {
// get all projects for all cla group under foundation
allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForFoundation(projectSFID)
if err != nil {
From d0ef9b383115392f868b1fc3ff33820876cf3aec Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Tue, 23 Feb 2021 14:27:33 +0300
Subject: [PATCH 0113/1276] [#2634] Feature/Contributor Contact Admin email
- Updated content sent to company admin in regards the CLA signing
Signed-off-by: wanyaland
---
cla-backend-go/emails/v2_cla_manager_templates.go | 9 ++++-----
cla-backend-go/tests/v2_cla_manager_templates_test.go | 6 +++---
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 46c4a97db..cfdfe0e02 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -90,12 +90,11 @@ const (
// V2ContributorToOrgAdminTemplate is email template for
V2ContributorToOrgAdminTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project(s) {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.ExternalProjectName}}{{end}}
-
The following contributor is requesting to sign the CLA for the organization: {{.CompanyName}}
+
The following contributor would like to submit a contribution to {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.ExternalProjectName}}{{end}} and is requesting to be added to the approval list as a contributor for your organization:
{{.UserDetails}}
-
Before the user contribution can be accepted, your organization must sign a CLA.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
-
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
+
Before the contribution can be accepted, your organization must sign a CLA. Either you or someone whom you designate from your company can login to this portal and sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
+
Please notify the contributor once they are added so that they may complete the contribution process.
+
`
)
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 7018ea7b5..25927155c 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -95,10 +95,10 @@ func TestV2ContributorToOrgAdminTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the project(s) Project1,Project2")
- assert.Contains(t, result, "sign the CLA for the organization: JohnsCompany")
+ assert.Contains(t, result, "would like to submit a contribution to Project1,Project2")
+ assert.Contains(t, result, "your organization must sign a CLA.")
assert.Contains(t, result, "
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
-
The following contributor is requesting to sign the CLA for the organization {{.CompanyName}}:
-
{{.ContributorID}} ({{.ContributorName}})
-
Before the user contribution can be accepted, your organization must sign a CLA.
+
We received a request from {{.ContributorName}} ({{.ContributorEmail}}): to contribute to the above projects on behalf of your organization
+
Before the user contribution can be accepted, your organization must sign a Corporate CLA (CCLA).The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute
+
Please complete the following steps:
+
+
After login, you will be redirected to the portal {{.CorporateConsole}} where you can either sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}, or send it to an authorized signatory for your company.
+
After signing the CLA, you will need to add this contributor to the approved list in the CLA Manager console.
+
After adding the contributor, please notify them so that they can complete the contribution process.
+
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they can complete the contribution process.
This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}}.
-
The following contributor would like to contribute to {{.GetProjectNameOrFoundation}} on behalf of your organization: {{.CompanyName}}.
-
{{.RequesterUserName}} ({{.RequesterEmail}})
-
Before the user contribution can be accepted, your organization must sign a CLA.
-
Please click on Accept Invite to create your LF Login.
-
After login, you will be redirected to this portal {{.CorporateConsole}} where you can sign the CLA for the project {{.Project.GetProjectFullURL}}.
-
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they can complete the contribution process.
+
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
+
We received a request from {{.ContributorName}} ({{.ContributorEmail}}) to contribute to any of the above projects on behalf of your
+organization {{.CompanyName}}.
+
Before the user contribution can be accepted, your organization must sign a Corporate CLA(CCLA).
+The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute.
+
Please complete the following steps:
+
+
Please click on Accept Invite to create your LF Login.This is used to access the EasyCLA CLA Manager console.
+
After login, you will be redirected to the portal {{.CorporateConsole}} where you can either sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}, or send it to an authorized signatory for your company.
+
After signing the CLA, you will need to add this contributor to the approved list in the CLA Manager console.
+
After adding the contributor, please notify them so that they can complete the contribution process.
+
`
)
-// RenderV2DesigneeToUserWithNoLFIDTemplate renders V2DesigneeToUserWithNoLFIDTemplate
-func RenderV2DesigneeToUserWithNoLFIDTemplate(repository projects_cla_groups.Repository, projectSFID string, params V2DesigneeToUserWithNoLFIDTemplateParams) (string, error) {
- if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectSFID, ¶ms.CLAManagerTemplateParams); err != nil {
- return "", err
- }
-
- // assign the corporate console so we can show the link of the project
- params.Project.CorporateConsole = params.CorporateConsole
-
- return RenderTemplate(utils.V2, V2DesigneeToUserWithNoLFIDTemplateName,
- V2DesigneeToUserWithNoLFIDTemplate, params)
-}
-
// V2CLAManagerToUserWithNoLFIDTemplateParams is email params for V2CLAManagerToUserWithNoLFIDTemplate
type V2CLAManagerToUserWithNoLFIDTemplateParams struct {
CLAManagerTemplateParams
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 25927155c..decf5ec26 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -139,7 +139,7 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
{ExternalProjectName: "Project2", ProjectSFID: "ProjectSFID2", FoundationSFID: "FoundationSFID2", CorporateConsole: "http://CorporateConsole.com"},
},
- ContributorID: "ContributorIDValue",
+ ContributorEmail: "ContributorIDValue",
ContributorName: "ContributorNameValue",
CorporateConsole: "http://CorporateConsole.com",
}
@@ -149,7 +149,7 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1, Project2")
- assert.Contains(t, result, "
ContributorIDValue (ContributorNameValue)
")
+ assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue):")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
assert.Contains(t, result, `CLA for one of the project(s): Project1,Project2`)
@@ -161,37 +161,32 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1")
- assert.Contains(t, result, "
ContributorIDValue (ContributorNameValue)
")
+ assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue):")
assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
assert.Contains(t, result, `CLA for one of the project(s): Project1`)
}
func TestV2DesigneeToUserWithNoLFIDTemplate(t *testing.T) {
- params := emails.V2DesigneeToUserWithNoLFIDTemplateParams{
- CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
- RecipientName: "JohnsClaManager",
- Project: emails.CLAProjectParams{
- ExternalProjectName: "JohnsProjectExternal",
- CorporateConsole: "https://corporate.dev.lfcla.com",
- FoundationSFID: "FoundationSFIDValue",
- SignedAtFoundationLevel: true,
- },
- CLAGroupName: "JohnsCLAGroupName",
- CompanyName: "JohnsCompany",
+ params := emails.V2ToCLAManagerDesigneeTemplateParams{
+ RecipientName: "JohnsClaManager",
+ Projects: []emails.CLAProjectParams{
+ {ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "https://corporate.dev.lfcla.com"},
+ {ExternalProjectName: "Project2", ProjectSFID: "ProjectSFID2", FoundationSFID: "FoundationSFID2", CorporateConsole: "https://corporate.dev.lfcla.com"},
},
- RequesterUserName: "RequesterUserNameValue",
- RequesterEmail: "RequesterEmailValue",
- CorporateConsole: "https://corporate.dev.lfcla.com",
+ ContributorEmail: "ContributorIDValue",
+ ContributorName: "ContributorNameValue",
+ CorporateConsole: "https://corporate.dev.lfcla.com",
}
result, err := emails.RenderTemplate(utils.V1, emails.V2DesigneeToUserWithNoLFIDTemplateName, emails.V2DesigneeToUserWithNoLFIDTemplate,
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager,")
- assert.Contains(t, result, "The following contributor would like to contribute to JohnsProjectExternal on behalf of your organization: JohnsCompany.")
- assert.Contains(t, result, "you will be redirected to this portal https://corporate.dev.lfcla.com ")
- assert.Contains(t, result, `where you can sign the CLA for the project JohnsProjectExternal`)
+ assert.Contains(t, result, "We received a request from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, "After login, you will be redirected to the portal https://corporate.dev.lfcla.com ")
+ assert.Contains(t, result, `where you can either sign the CLA for any of the project(s): Project1`)
+ assert.Contains(t, result, "or send it to an authorized signatory for your company.")
}
func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index eab1b55ab..ad3542fc4 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -188,7 +188,7 @@ func (s *service) SendEmailToCLAManagerDesigneeCorporate(ctx context.Context, re
}
}
-func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorID string, contributorName string) {
+func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorEmail string, contributorName string) {
f := logrus.Fields{
"functionName": "cla_manager.service.SendEmailToCLAManagerDesignee",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -197,20 +197,20 @@ func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository
"projectNames": strings.Join(projectNames, ","),
"designeeEmail": designeeEmail,
"designeeName": designeeName,
- "contributorID": contributorID,
+ "contributorEmail": contributorEmail,
"contributorName": contributorName,
}
subject := fmt.Sprintf("EasyCLA: Invitation to Sign the %s Corporate CLA and add to approved list %s ",
- companyName, contributorID)
+ companyName, contributorEmail)
recipients := []string{designeeEmail}
body, err := emails.RenderV2ToCLAManagerDesigneeTemplate(repository, projectService, projectSFIDs,
emails.V2ToCLAManagerDesigneeTemplateParams{
RecipientName: designeeName,
- ContributorID: contributorID,
+ ContributorEmail: contributorEmail,
ContributorName: contributorName,
CorporateConsole: corporateConsole,
- })
+ }, emails.V2ToCLAManagerDesigneeTemplate, emails.V2ToCLAManagerDesigneeTemplateName)
if err != nil {
log.WithFields(f).WithError(err).Warnf("rendering template : %s failed : %v", emails.V2ToCLAManagerDesigneeTemplateName, err)
@@ -224,30 +224,27 @@ func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository
}
}
-func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID, projectName string, projectID *string, role string, corporateConsoleV2URL string) error {
+func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectSFIDs []string, foundationSFID, role string, corporateConsoleV2URL string) error {
f := logrus.Fields{
"functionName": "cla_manager.service.SendDesigneeEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userWithNoLFIDName": userWithNoLFIDName,
"userWithNoLFIDEmail": userWithNoLFIDEmail,
"organizationID": organizationID,
- "projectID": utils.StringValue(projectID),
+ "projectNames": strings.Join(projectNames, ","),
"role": role,
"corporateConsoleV2URL": corporateConsoleV2URL,
}
- subject := fmt.Sprintf("EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager for project: %s ", projectName)
- body, err := emails.RenderV2DesigneeToUserWithNoLFIDTemplate(repository, *projectID,
- emails.V2DesigneeToUserWithNoLFIDTemplateParams{
- CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
- RecipientName: userWithNoLFIDName,
- Project: emails.CLAProjectParams{ExternalProjectName: projectName},
- CompanyName: organizationName,
- },
- RequesterUserName: requesterUsername,
- RequesterEmail: requesterEmail,
- CorporateConsole: corporateConsoleV2URL,
- })
+ subject := "EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager"
+
+ body, err := emails.RenderV2ToCLAManagerDesigneeTemplate(repository, projectService, projectSFIDs,
+ emails.V2ToCLAManagerDesigneeTemplateParams{
+ RecipientName: userWithNoLFIDName,
+ ContributorEmail: requesterEmail,
+ ContributorName: requesterUsername,
+ CorporateConsole: corporateConsoleV2URL,
+ }, emails.V2DesigneeToUserWithNoLFIDTemplate, emails.V2DesigneeToUserWithNoLFIDTemplateName)
if err != nil {
log.WithFields(f).WithError(err).Warnf("rendering template : %s failed : %v", emails.V2DesigneeToUserWithNoLFIDTemplateName, err)
@@ -257,7 +254,7 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, reposit
acsClient := v2AcsService.GetClient()
automate := false
log.WithFields(f).Debug("sending user invite request...")
- return acsClient.SendUserInvite(ctx, &userWithNoLFIDEmail, role, utils.ProjectOrgScope, projectID, organizationID, "userinvite", &subject, &body, automate)
+ return acsClient.SendUserInvite(ctx, &userWithNoLFIDEmail, role, utils.ProjectOrgScope, &foundationSFID, organizationID, "userinvite", &subject, &body, automate)
}
// sendEmailToUserWithNoLFID helper function to send email to a given user with no LFID
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 781a4d3cb..fb9706796 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -96,7 +96,7 @@ type Service interface {
ContributorEmailToOrgAdmin(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, adminEmail string, adminName string, companyName string, projectSFIDs []string, contributor *v1Models.User, corporateConsole string)
SendEmailToCLAManagerDesigneeCorporate(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectSFID string, projectName string, designeeEmail string, designeeName string, senderEmail string, senderName string)
SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorID string, contributorName string)
- SendDesigneeEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID, projectName string, projectID *string, role string, corporateConsoleV2URL string) error
+ SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectIDs []string, foundationSFID, role string, corporateConsoleV2URL string) error
SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error
}
@@ -897,16 +897,34 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
var projectSFs []string
var projectSFIDs []string
- for _, pcg := range projectCLAGroups {
- log.WithFields(f).Debugf("Getting salesforce project by SFID: %s ", pcg.ProjectSFID)
- projectSF, projectErr := projectService.GetProject(pcg.ProjectSFID)
+ foundationSFID := projectCLAGroups[0].FoundationSFID
+
+ if signedAtFoundation {
+
+ // Get salesforce project by FoundationID
+ log.WithFields(f).Debugf("querying project service for project details...")
+ // GetSFProject
+ foundationSF, projectErr := projectService.GetProject(foundationSFID)
if projectErr != nil {
- msg := fmt.Sprintf("Problem getting salesforce Project ID: %s", pcg.ProjectSFID)
+ msg := fmt.Sprintf("EasyCLA - 400 Bad Request - Project service lookup error for SFID: %s, error : %+v",
+ projectID, projectErr)
log.WithFields(f).Warn(msg)
return nil, projectErr
}
- projectSFs = append(projectSFs, projectSF.Name)
- projectSFIDs = append(projectSFIDs, projectSF.ID)
+ projectSFs = append(projectSFs, foundationSF.Name)
+ projectSFIDs = append(projectSFIDs, foundationSFID)
+ } else {
+ for _, pcg := range projectCLAGroups {
+ log.WithFields(f).Debugf("Getting salesforce project by SFID: %s ", pcg.ProjectSFID)
+ projectSF, projectErr := projectService.GetProject(pcg.ProjectSFID)
+ if projectErr != nil {
+ msg := fmt.Sprintf("Problem getting salesforce Project ID: %s", pcg.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return nil, projectErr
+ }
+ projectSFs = append(projectSFs, projectSF.Name)
+ projectSFIDs = append(projectSFIDs, projectSF.ID)
+ }
}
var designeeScopes []*models.ClaManagerDesignee
@@ -960,24 +978,8 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
if userErr != nil || (user != nil && user.Username == "") {
msg := fmt.Sprintf("UserEmail: %s has no LF Login and has been sent an invite email to create an account , error: %+v", userEmail, userErr)
log.Warn(msg)
-
- // Use FoundationSFID
- foundationSFID := projectCLAGroups[0].FoundationSFID
-
- // Get salesforce project by FoundationID
- log.WithFields(f).Debugf("querying project service for project details...")
- // GetSFProject
- ps := v2ProjectService.GetClient()
- sfProject, projectErr := ps.GetProject(foundationSFID)
- if projectErr != nil {
- msg := fmt.Sprintf("EasyCLA - 400 Bad Request - Project service lookup error for SFID: %s, error : %+v",
- projectID, projectErr)
- log.WithFields(f).Warn(msg)
- return nil, projectErr
- }
-
contibutorEmail := GetNonNoReplyUserEmail(contributor.UserEmails)
- sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectCGRepo, contributor.UserName, contibutorEmail, name, userEmail, organization.Name, organization.ID, sfProject.Name, &foundationSFID, "cla-manager-designee", LfxPortalURL)
+ sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectService, s.projectCGRepo, contributor.UserName, contibutorEmail, name, userEmail, organization.Name, organization.ID, projectSFs, projectSFIDs, foundationSFID, "cla-manager-designee", LfxPortalURL)
if sendErr != nil {
msg := fmt.Sprintf("Problem sending email to user: %s , error: %+v", userEmail, sendErr)
log.Warn(msg)
From 041766d699433026306602dae1383bc89375876f Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Feb 2021 15:08:59 -0800
Subject: [PATCH 0118/1276] Resolved [#2702] Return ICLA Signatures (#2712)
- Added logic to ensure the correct filter/query was executing
- Augmented the results to lookup missing github username, name, email, if available - response entries were previously missing some data
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 101 +++++++++++++++++++----
cla-backend-go/signatures/service.go | 13 +--
cla-backend-go/v2/signatures/handlers.go | 29 ++++---
cla-backend-go/v2/signatures/service.go | 11 +++
4 files changed, 117 insertions(+), 37 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 457488782..db0ae7a95 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -431,7 +431,7 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(userID)))
filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value("user"))).
+ And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
@@ -2295,15 +2295,27 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
"functionName": "GetClaGroupICLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
+ "searchTerm": utils.StringValue(searchTerm),
}
- sortKeyPrefix := fmt.Sprintf("%s#%v#%v", utils.ClaTypeICLA, true, true)
+ //sortKeyPrefix := fmt.Sprintf("%s#%v#%v", utils.ClaTypeICLA, true, true)
// This is the key we want to match
- condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
- And(expression.Key("sigtype_signed_approved_id").BeginsWith(sortKeyPrefix))
+ //condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
+ // And(expression.Key("sigtype_signed_approved_id").BeginsWith(sortKeyPrefix))
+ condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
+
+ filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
+ And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
+ And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
+ And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
+ And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
// Use the builder to create the expression
- expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(buildProjection()).Build()
+ expr, err := expression.NewBuilder().
+ WithKeyCondition(condition).
+ WithFilter(filter).
+ WithProjection(buildProjection()).
+ Build()
if err != nil {
log.WithFields(f).Warnf("error building expression for get cla group icla signatures, claGroupID: %s, error: %v",
claGroupID, err)
@@ -2315,15 +2327,23 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
ExpressionAttributeNames: expr.Names(),
ExpressionAttributeValues: expr.Values(),
KeyConditionExpression: expr.KeyCondition(),
+ FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
TableName: aws.String(repo.signatureTableName),
- IndexName: aws.String(SignatureProjectIDSigTypeSignedApprovedIDIndex),
- Limit: aws.Int64(HugePageSize),
+ //IndexName: aws.String(SignatureProjectIDSigTypeSignedApprovedIDIndex),
+ IndexName: aws.String(SignatureProjectIDIndex),
+ Limit: aws.Int64(HugePageSize),
}
- out := &models.IclaSignatures{List: make([]*models.IclaSignature, 0)}
if searchTerm != nil {
searchTerm = aws.String(strings.ToLower(*searchTerm))
}
+
+ type IclaSignatureWithDetails struct {
+ IclaSignature *models.IclaSignature
+ SignatureReferenceID string
+ }
+ var intermediateResponse []*IclaSignatureWithDetails
+
for {
// Make the DynamoDB Query API call
results, queryErr := repo.dynamoDBClient.Query(queryInput)
@@ -2347,20 +2367,24 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
continue
}
}
+
signedOn := sig.DateCreated
if sig.SignedOn != "" {
signedOn = sig.SignedOn
}
- out.List = append(out.List, &models.IclaSignature{
- GithubUsername: sig.UserGithubUsername,
- LfUsername: sig.UserLFUsername,
- SignatureID: sig.SignatureID,
- UserEmail: sig.UserEmail,
- UserName: sig.UserName,
- SignedOn: signedOn,
- UserDocusignName: sig.UserDocusignName,
- UserDocusignDateSigned: sig.UserDocusignDateSigned,
- SignatureModified: sig.DateModified,
+ intermediateResponse = append(intermediateResponse, &IclaSignatureWithDetails{
+ IclaSignature: &models.IclaSignature{
+ GithubUsername: sig.UserGithubUsername,
+ LfUsername: sig.UserLFUsername,
+ SignatureID: sig.SignatureID,
+ UserEmail: sig.UserEmail,
+ UserName: sig.UserName,
+ SignedOn: signedOn,
+ UserDocusignName: sig.UserDocusignName,
+ UserDocusignDateSigned: sig.UserDocusignDateSigned,
+ SignatureModified: sig.DateModified,
+ },
+ SignatureReferenceID: sig.SignatureReferenceID,
})
}
@@ -2370,6 +2394,47 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
queryInput.ExclusiveStartKey = results.LastEvaluatedKey
log.WithFields(f).Debug("querying next page")
}
+
+ log.WithFields(f).Debugf("Adding additional meta-data for %d records...", len(intermediateResponse))
+ // For some older ICLA signatures, we are missing the user's info, but we have their internal ID - let's look up those values before returning
+ responseChannel := make(chan *models.IclaSignature)
+ for _, iclaSignatureWithDetails := range intermediateResponse {
+ go func(iclaSignatureWithDetails *IclaSignatureWithDetails) {
+ userModel, userLookupErr := repo.usersRepo.GetUser(iclaSignatureWithDetails.SignatureReferenceID)
+ if userLookupErr != nil || userModel == nil {
+ log.WithFields(f).WithError(userLookupErr).Warnf("unable to lookup user with id: %s", iclaSignatureWithDetails.SignatureReferenceID)
+ } else {
+ // If the github username is empty, see if it was set in the user model
+ if iclaSignatureWithDetails.IclaSignature.GithubUsername == "" {
+ // Grab and set the github username
+ iclaSignatureWithDetails.IclaSignature.GithubUsername = userModel.GithubUsername
+ }
+ // If the github username is empty, see if it was set in the user model
+ if iclaSignatureWithDetails.IclaSignature.UserName == "" {
+ if userModel.Username != "" {
+ // Grab and set the github username
+ iclaSignatureWithDetails.IclaSignature.UserName = userModel.Username
+ } else if userModel.LfUsername != "" {
+ iclaSignatureWithDetails.IclaSignature.UserName = userModel.LfUsername
+ }
+ }
+ // If the github username is empty, see if it was set in the user model
+ if iclaSignatureWithDetails.IclaSignature.UserEmail == "" {
+ // Grab and set the github username
+ iclaSignatureWithDetails.IclaSignature.UserEmail = getBestEmail(userModel)
+ }
+ }
+
+ responseChannel <- iclaSignatureWithDetails.IclaSignature
+ }(iclaSignatureWithDetails)
+ }
+
+ // Append all the responses to our list
+ out := &models.IclaSignatures{List: make([]*models.IclaSignature, 0)}
+ for i := 0; i < len(intermediateResponse); i++ {
+ out.List = append(out.List, <-responseChannel)
+ }
+
return out, nil
}
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 8d2de5219..b418da046 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
"sync"
"github.com/aws/aws-sdk-go/aws"
@@ -433,7 +434,7 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
// Send an email to the CLA Managers
for _, claManager := range claManagers {
- claManagerEmail := getBestEmail(claManager)
+ claManagerEmail := getBestEmail(&claManager) // nolint
s.sendApprovalListUpdateEmailToCLAManagers(companyModel, claGroupModel, claManager.Username, claManagerEmail, params)
}
@@ -846,13 +847,13 @@ you can now go back to it and follow the link to verify with your organization.<
}
// getBestEmail is a helper function to return the best email address for the user model
-func getBestEmail(claManager models.User) string {
- if claManager.LfEmail != "" {
- return claManager.LfEmail
+func getBestEmail(userModel *models.User) string {
+ if userModel.LfEmail != "" {
+ return userModel.LfEmail
}
- for _, email := range claManager.Emails {
- if email != "" {
+ for _, email := range userModel.Emails {
+ if email != "" && !strings.Contains(email, "noreply.github.com") {
return email
}
}
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index a4a86c281..10bb85a56 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -40,7 +40,7 @@ import (
)
// Configure setups handlers on api with service
-func Configure(api *operations.EasyclaAPI, projectService project.Service, projectRepo project.ProjectRepository, companyService company.IService, v1SignatureService signatureService.SignatureService, sessionStore *dynastore.Store, eventsService events.Service, v2service Service, projectClaGroupsRepo projects_cla_groups.Repository) { //nolint
+func Configure(api *operations.EasyclaAPI, claGroupService project.Service, projectRepo project.ProjectRepository, companyService company.IService, v1SignatureService signatureService.SignatureService, sessionStore *dynastore.Store, eventsService events.Service, v2service Service, projectClaGroupsRepo projects_cla_groups.Repository) { //nolint
const problemLoadingCLAGroupByID = "problem loading cla group by ID"
const iclaNotSupportedForCLAGroup = "individual contribution is not supported for this project"
@@ -135,7 +135,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("loading CLA groups by projectSFID")
- projectModels, projsErr := projectService.GetCLAGroupsByExternalSFID(ctx, params.ProjectSFID)
+ projectModels, projsErr := claGroupService.GetCLAGroupsByExternalSFID(ctx, params.ProjectSFID)
if projsErr != nil || projectModels == nil {
msg := fmt.Sprintf("unable to locate projects by Project SFID: %s", params.ProjectSFID)
log.WithFields(f).Warn(msg)
@@ -143,7 +143,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
// Lookup the internal project ID when provided the external ID via the v1SignatureService call
- claGroupModel, projErr := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, projErr := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if projErr != nil || claGroupModel == nil {
msg := fmt.Sprintf("unable to locate project by CLA Group ID: %s", params.ClaGroupID)
log.WithFields(f).Warn(msg)
@@ -367,7 +367,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -711,7 +711,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
log.WithFields(f).Debug("processing request...")
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -795,6 +795,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
})
})
+ // GET https://api-gw.platform.linuxfoundation.org/v4/cla-group/{claGroupID}/icla/signatures
api.SignaturesListClaGroupIclaSignatureHandler = signatures.ListClaGroupIclaSignatureHandlerFunc(func(params signatures.ListClaGroupIclaSignatureParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
@@ -802,10 +803,12 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
"functionName": "SignaturesListClaGroupIclaSignatureHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
+ "searchTerm": utils.StringValue(params.SearchTerm),
+ "sortOrder": utils.StringValue(params.SortOrder),
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -837,7 +840,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for ICLA signatures...")
- result, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm)
+ results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm)
if err != nil {
msg := fmt.Sprintf("problem loading ICLA signatures by CLA Group ID search term: %s", aws.StringValue(params.SearchTerm))
log.WithFields(f).WithError(err).Warn(msg)
@@ -845,8 +848,8 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- log.WithFields(f).Debugf("returning %d ICLA signatures to caller...", len(result.List))
- return signatures.NewListClaGroupIclaSignatureOK().WithXRequestID(reqID).WithPayload(result)
+ log.WithFields(f).Debugf("returning %d ICLA signatures to caller...", len(results.List))
+ return signatures.NewListClaGroupIclaSignatureOK().WithXRequestID(reqID).WithPayload(results)
})
api.SignaturesListClaGroupCorporateContributorsHandler = signatures.ListClaGroupCorporateContributorsHandlerFunc(func(params signatures.ListClaGroupCorporateContributorsParams, authUser *auth.User) middleware.Responder {
@@ -990,7 +993,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("loading cla group by id...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -1044,7 +1047,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -1111,7 +1114,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
@@ -1164,7 +1167,7 @@ func Configure(api *operations.EasyclaAPI, projectService project.Service, proje
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
- claGroupModel, err := projectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := claGroupService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
if err == project.ErrProjectDoesNotExist {
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 2c0d33b8f..3d04f4075 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -168,15 +168,26 @@ func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID str
}
func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error) {
+ f := logrus.Fields{
+ "functionName": "v2.signatures.service.GetProjectIclaSignatures",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "searchTerm": utils.StringValue(searchTerm),
+ }
+
var out models.IclaSignatures
result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load ICLA signatures using the specified search parameters")
return nil, err
}
+
err = copier.Copy(&out, result)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to convert signature results from v1 to v2")
return nil, err
}
+
return &out, nil
}
From eb1bc2364a453677db5c19d2d4ce12d495a2e59a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Feb 2021 15:22:19 -0800
Subject: [PATCH 0119/1276] Resolved [LFX-3130] Prevent Delete of Last CLA
Manager (#2713)
- Added logic to prevent the last CLA manager from deleting themself
Signed-off-by: David Deal
---
cla-backend-go/cla_manager/service.go | 7 +++++++
cla-backend-go/utils/errors.go | 24 ++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index b6f5fb9c1..cb6e8b4fd 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -308,6 +308,13 @@ func (s service) RemoveClaManager(ctx context.Context, companyID string, claGrou
return nil, sigErr
}
+ if len(sigModel.SignatureACL) <= 1 {
+ // Can't delete the only remaining CLA Manager....
+ return nil, &utils.CLAManagerError{
+ Message: "unable to remove the only remaining CLA Manager - signed CLAs must have at least one CLA Manager",
+ }
+ }
+
// Update the signature ACL
updatedSignature, aclErr := s.sigService.RemoveCLAManager(ctx, sigModel.SignatureID, LFID)
if aclErr != nil || updatedSignature == nil {
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index bbf3b3dc3..ca6b930e1 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -323,3 +323,27 @@ func (e *GitHubRepositoryExists) Error() string {
func (e *GitHubRepositoryExists) Unwrap() error {
return e.Err
}
+
+// CLAManagerError is an error model for when a CLA Manager error occurs
+type CLAManagerError struct {
+ Message string
+ Err error
+}
+
+// Error is an error string function for the CLAManagerError model
+func (e *CLAManagerError) Error() string {
+ msg := "CLA Manager Error"
+ if e.Message != "" {
+ msg = e.Message
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *CLAManagerError) Unwrap() error {
+ return e.Err
+}
From 2e2b1dce90c023c6eed471d6e2f05064224cf9c2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Feb 2021 16:11:36 -0800
Subject: [PATCH 0120/1276] Resolved User Invite with Missing First and Last
Name (#2714)
- Added first and last name to user invite request
- Added utlity method with tests to parse single first/last name string into separate pieces
Signed-off-by: David Deal
---
.../tests/utils_string_utils_test.go | 28 ++++++++
cla-backend-go/utils/string_utils.go | 16 +++++
cla-backend-go/v2/acs-service/client.go | 66 ++++++++++++-------
cla-backend-go/v2/cla_manager/emails.go | 38 +++++++++--
4 files changed, 120 insertions(+), 28 deletions(-)
diff --git a/cla-backend-go/tests/utils_string_utils_test.go b/cla-backend-go/tests/utils_string_utils_test.go
index f612c0427..c55173964 100644
--- a/cla-backend-go/tests/utils_string_utils_test.go
+++ b/cla-backend-go/tests/utils_string_utils_test.go
@@ -38,3 +38,31 @@ func TestTrimSpaceFromItems(t *testing.T) {
assert.ObjectsAreEqualValues(expectedResults[i], utils.TrimSpaceFromItems(testInputs[i]))
}
}
+
+func TestGetFirstAndLastName(t *testing.T) {
+
+ testInputs := []string{
+ "",
+ "John",
+ "John Smith",
+ "John Smith",
+ "John Harold Smith",
+ "John Harold Smith",
+ "John Harold Zeek Smith",
+ }
+ expectedResults := [][]string{
+ {"", ""},
+ {"John", ""},
+ {"John", "Smith"},
+ {"John", "Smith"},
+ {"John", "Smith"},
+ {"John", "Smith"},
+ {"John", "Smith"},
+ }
+
+ for i := range testInputs {
+ firstName, lastName := utils.GetFirstAndLastName(testInputs[i])
+ assert.Equal(t, expectedResults[i][0], firstName)
+ assert.Equal(t, expectedResults[i][1], lastName)
+ }
+}
diff --git a/cla-backend-go/utils/string_utils.go b/cla-backend-go/utils/string_utils.go
index 855c43785..934550ba2 100644
--- a/cla-backend-go/utils/string_utils.go
+++ b/cla-backend-go/utils/string_utils.go
@@ -24,3 +24,19 @@ func TrimSpaceFromItems(arr []string) []string {
return newArr
}
+
+// GetFirstAndLastName parses the user's name into first and last strings
+func GetFirstAndLastName(firstAndLastName string) (string, string) {
+ // Parse the provided user's name
+ userNames := strings.Split(firstAndLastName, " ")
+ var userFirstName string
+ var userLastName string
+ if len(userNames) >= 2 {
+ userFirstName = userNames[0]
+ userLastName = userNames[len(userNames)-1]
+ } else if len(userNames) == 1 {
+ userFirstName = userNames[0]
+ }
+
+ return strings.TrimSpace(userFirstName), strings.TrimSpace(userLastName)
+}
diff --git a/cla-backend-go/v2/acs-service/client.go b/cla-backend-go/v2/acs-service/client.go
index 337efc62c..bdb930504 100644
--- a/cla-backend-go/v2/acs-service/client.go
+++ b/cla-backend-go/v2/acs-service/client.go
@@ -67,19 +67,35 @@ func GetClient() *Client {
return acsServiceClient
}
+// SendUserInviteInput input model for sending user invites
+type SendUserInviteInput struct {
+ InviteUserFirstName string
+ InviteUserLastName string
+ InviteUserEmail string
+ RoleName string
+ Scope string
+ ProjectSFID string
+ OrganizationSFID string
+ InviteType string
+ Subject string
+ EmailContent string
+ Automate bool
+}
+
// SendUserInvite invites users to the LFX platform
-func (ac *Client) SendUserInvite(ctx context.Context, email *string,
- roleName string, scope string, projectID *string, organizationID string, inviteType string, subject *string, emailContent *string, automate bool) error {
+//func (ac *Client) SendUserInvite(ctx context.Context, email *string,
+// roleName string, scope string, projectID *string, organizationID string, inviteType string, subject *string, emailContent *string, automate bool) error {
+func (ac *Client) SendUserInvite(ctx context.Context, input *SendUserInviteInput) error {
f := logrus.Fields{
- "functionName": "SendUserInvite",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "roleName": roleName,
- "scope": scope,
- "projectID": utils.StringValue(projectID),
- "organizationID": organizationID,
- "inviteType": inviteType,
- "subject": utils.StringValue(subject),
- "automate": automate,
+ "functionName": "SendUserInvite",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "roleName": input.RoleName,
+ "scope": input.Scope,
+ "projectSFID": input.ProjectSFID,
+ "organizationSFID": input.OrganizationSFID,
+ "inviteType": input.InviteType,
+ "subject": input.Subject,
+ "automate": input.Automate,
}
tok, err := token.GetToken()
@@ -91,30 +107,32 @@ func (ac *Client) SendUserInvite(ctx context.Context, email *string,
clientAuth := runtimeClient.BearerToken(tok)
params := &invite.CreateUserInviteParams{
SendInvite: &models.CreateInvite{
- Automate: automate,
- Email: email,
- Scope: scope,
- RoleName: roleName,
- Type: inviteType,
+ Automate: input.Automate,
+ Email: &input.InviteUserEmail,
+ FirstName: input.InviteUserFirstName,
+ LastName: input.InviteUserLastName,
+ RoleName: input.RoleName,
+ Scope: input.Scope,
+ Type: input.InviteType,
},
Context: ctx,
}
- if scope == utils.ProjectOrgScope && projectID == nil {
+ if input.Scope == utils.ProjectOrgScope && input.ProjectSFID == "" {
log.WithFields(f).Warnf("Project ID required for project|organization scope, error: %+v", ErrProjectIDMissing)
return ErrProjectIDMissing
}
- if scope == utils.ProjectOrgScope {
+ if input.Scope == utils.ProjectOrgScope {
// Set project|organization scope
- params.SendInvite.ScopeID = fmt.Sprintf("%s|%s", *projectID, organizationID)
+ params.SendInvite.ScopeID = fmt.Sprintf("%s|%s", input.ProjectSFID, input.OrganizationSFID)
} else {
- params.SendInvite.ScopeID = organizationID
+ params.SendInvite.ScopeID = input.OrganizationSFID
}
- if subject != nil {
- params.SendInvite.Subject = *subject
+ if input.Subject != "" {
+ params.SendInvite.Subject = input.Subject
}
// Pass emailContent if passed in the args
- if emailContent != nil {
- params.SendInvite.Body = *emailContent
+ if input.EmailContent != "" {
+ params.SendInvite.Body = input.EmailContent
}
log.WithFields(f).Debugf("Submitting ACS Service CreateUserInvite with payload: %+v", params)
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index ad3542fc4..e400f01d3 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -252,9 +252,24 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, project
}
acsClient := v2AcsService.GetClient()
- automate := false
log.WithFields(f).Debug("sending user invite request...")
- return acsClient.SendUserInvite(ctx, &userWithNoLFIDEmail, role, utils.ProjectOrgScope, &foundationSFID, organizationID, "userinvite", &subject, &body, automate)
+
+ // Parse the provided user's name
+ userFirstName, userLastName := utils.GetFirstAndLastName(userWithNoLFIDName)
+
+ return acsClient.SendUserInvite(ctx, &v2AcsService.SendUserInviteInput{
+ InviteUserFirstName: userFirstName,
+ InviteUserLastName: userLastName,
+ InviteUserEmail: userWithNoLFIDEmail,
+ RoleName: role,
+ Scope: utils.ProjectOrgScope,
+ ProjectSFID: foundationSFID,
+ OrganizationSFID: organizationID,
+ InviteType: "userinvite",
+ Subject: subject,
+ EmailContent: body,
+ Automate: false,
+ })
}
// sendEmailToUserWithNoLFID helper function to send email to a given user with no LFID
@@ -281,8 +296,23 @@ func (s *service) SendEmailToUserWithNoLFID(ctx context.Context, repository proj
return err
}
acsClient := v2AcsService.GetClient()
- automate := false
+
+ // Parse the provided user's name
+ userFirstName, userLastName := utils.GetFirstAndLastName(userWithNoLFIDName)
log.WithFields(f).Debug("sending user invite request...")
- return acsClient.SendUserInvite(ctx, &userWithNoLFIDEmail, role, utils.ProjectOrgScope, projectID, organizationID, "userinvite", &subject, &body, automate)
+ //return acsClient.SendUserInvite(ctx, &userWithNoLFIDEmail, role, utils.ProjectOrgScope, projectID, organizationID, "userinvite", &subject, &body, automate)
+ return acsClient.SendUserInvite(ctx, &v2AcsService.SendUserInviteInput{
+ InviteUserFirstName: userFirstName,
+ InviteUserLastName: userLastName,
+ InviteUserEmail: userWithNoLFIDEmail,
+ RoleName: role,
+ Scope: utils.ProjectOrgScope,
+ ProjectSFID: *projectID,
+ OrganizationSFID: organizationID,
+ InviteType: "userinvite",
+ Subject: subject,
+ EmailContent: body,
+ Automate: false,
+ })
}
From 34a93068e46c43964201d0eda46b0dd91fef709b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Feb 2021 16:53:22 -0800
Subject: [PATCH 0121/1276] Resolved [#2706] Added User Name to Event Log
- Added cla signatory name to docusing event log entries
- Updated unit test for event time - ensure it uses UTC time
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 20 +++++++---
cla-backend/cla/models/dynamo_models.py | 48 +++++++++++++++++------
cla-backend/cla/tests/unit/test_event.py | 33 +++++++++++-----
3 files changed, 73 insertions(+), 28 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 5ddaf461b..8a0f9c0c2 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -600,6 +600,7 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
event_project_id=project_id,
event_project_name=project.get_project_name(),
event_user_id=user.get_user_id(),
+ event_user_name=user.get_user_name() if user else None,
event_data=event_data,
event_summary=event_summary,
contains_pii=True,
@@ -707,6 +708,7 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
event_company_id=company_id,
event_project_id=project_id,
event_user_id=user_id,
+ event_user_name=user.get_user_name() if user else None,
event_data=event_data,
event_summary=event_summary,
contains_pii=True,
@@ -808,6 +810,7 @@ def request_employee_signature_gerrit(self, project_id, company_id, user_id, ret
event_company_id=company_id,
event_project_id=project_id,
event_user_id=user_id,
+ event_user_name=user.get_user_name() if user else None,
event_data=event_data,
event_summary=event_summary,
contains_pii=True,
@@ -1459,6 +1462,7 @@ def signed_individual_callback(self, content, installation_id, github_repository
event_project_id=signature.get_signature_project_id(),
event_company_id=None,
event_user_id=signature.get_signature_reference_id(),
+ event_user_name=user.get_user_name() if user else None,
event_data=event_data,
event_summary=event_summary,
contains_pii=False,
@@ -1512,6 +1516,7 @@ def signed_individual_callback_gerrit(self, content, user_id):
event_project_id=signature.get_signature_project_id(),
event_company_id=None,
event_user_id=user.get_user_id(),
+ event_user_name=user.get_user_name(),
event_data=event_data,
event_summary=event_summary,
contains_pii=False,
@@ -1654,33 +1659,36 @@ def signed_corporate_callback(self, content, project_id, company_id):
# Update our event/activity log
if signature.get_signature_reference_type() == 'user':
event_data = (f'The user {user.get_user_name()} signed an individual CLA for '
- f'project {project.get_project_name()}.')
+ f'the project {project.get_project_name()}.')
event_summary = (f'The user {user.get_user_name()} signed an individual CLA for '
- f'project {project.get_project_name()} with project ID: {project.get_project_id()}.')
+ f'the project {project.get_project_name()} with '
+ f'the project ID: {project.get_project_id()}.')
Event.create_event(
event_type=EventType.IndividualSignatureSigned,
event_project_id=project_id,
event_company_id=None,
event_user_id=user.get_user_id(),
+ event_user_name=user.get_user_name(),
event_data=event_data,
event_summary=event_summary,
contains_pii=False,
)
elif signature.get_signature_reference_type() == 'company':
- event_data = (f'Corporate signature '
- f'signed for project {project.get_project_name()} '
+ event_data = (f'A corporate signature '
+ f'was signed for project {project.get_project_name()} '
f'and company {company.get_company_name()} '
f'by {signature.get_signatory_name()}, '
f'params: {param_str}')
event_summary = (f'A corporate signature '
- f'was signed for project {project.get_project_name()} '
- f'and company {company.get_company_name()} '
+ f'was signed for the project {project.get_project_name()} '
+ f'and the company {company.get_company_name()} '
f'by {signature.get_signatory_name()}.')
Event.create_event(
event_type=EventType.CompanySignatureSigned,
event_project_id=project_id,
event_company_id=company.get_company_id(),
event_user_id=user.get_user_id(),
+ event_user_name=signature.get_signatory_name(),
event_data=event_data,
event_summary=event_summary,
contains_pii=False,
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 771ce9aea..123ec1b79 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -30,6 +30,7 @@
import cla
from cla.models import model_interfaces, key_value_store_interface, DoesNotExist
+from cla.models.event_types import EventType
from cla.models.model_interfaces import User, Signature, ProjectCLAGroup, Repository, Gerrit
stage = os.environ.get("STAGE", "")
@@ -4394,27 +4395,43 @@ def search_events(self, **kwargs):
@classmethod
def create_event(
cls,
- event_type=None,
- event_project_id=None,
- event_company_id=None,
- event_project_name=None,
- event_company_name=None,
- event_data=None,
- event_summary=None,
- event_user_id=None,
- contains_pii=False,
- dry_run=False
+ event_type: Optional[EventType] = None,
+ event_project_id: Optional[str] = None,
+ event_company_id: Optional[str] = None,
+ event_project_name: Optional[str] = None,
+ event_company_name: Optional[str] = None,
+ event_data: Optional[str] = None,
+ event_summary: Optional[str] = None,
+ event_user_id: Optional[str] = None,
+ event_user_name: Optional[str] = None,
+ contains_pii: bool = False,
+ dry_run: bool = False
):
"""
Creates an event returns the newly created event in dict format.
:param event_type: The type of event
:type event_type: EventType
- :param event_user_id: The user that is assocaited with the event
- :type event_user_id: string
:param event_project_id: The project associated with event
:type event_project_id: string
-
+ :param event_project_name: The project name associated with event
+ :type event_project_name: string
+ :param event_company_id: The company associated with event
+ :type event_company_id: string
+ :param event_company_name: The company name associated with event
+ :type event_company_name: string
+ :param event_data: The event message/data
+ :type event_data: string
+ :param event_summary: The event summary message/data
+ :type event_summary: string
+ :param event_user_id: The user that is associated with the event
+ :type event_user_id: string
+ :param event_user_name: The user's name that is associated with the event
+ :type event_user_name: string
+ :param contains_pii: flag to indicate if the message contains personal information (deprecated)
+ :type contains_pii: bool
+ :param dry_run: flag to indicate this is for testing and the record should not be stored/created
+ :type dry_run: bool
"""
try:
event = cls()
@@ -4440,6 +4457,7 @@ def create_event(
event.set_event_company_id(event_company_id)
except DoesNotExist as err:
return {"errors": {"event_company_id": str(err)}}
+
if event_user_id:
try:
user = User()
@@ -4450,6 +4468,10 @@ def create_event(
event.set_event_user_name(user_name)
except DoesNotExist as err:
return {"errors": {"event_": str(err)}}
+
+ if event_user_name:
+ event.set_event_user_name(event_user_name)
+
event.set_event_id(str(uuid.uuid4()))
if event_type:
event.set_event_type(event_type.name)
diff --git a/cla-backend/cla/tests/unit/test_event.py b/cla-backend/cla/tests/unit/test_event.py
index 9769b05c6..efc16696b 100644
--- a/cla-backend/cla/tests/unit/test_event.py
+++ b/cla-backend/cla/tests/unit/test_event.py
@@ -1,13 +1,14 @@
# Copyright The Linux Foundation and each contributor to CommunityBridge.
# SPDX-License-Identifier: MIT
-from cla.models.dynamo_models import Event, User, Project, Company
-from cla.models import event_types
-from unittest.mock import patch, Mock
-import pytest
import datetime
-import cla
import time
+from unittest.mock import Mock
+
+import pytest
+
+from cla.models import event_types
+from cla.models.dynamo_models import Event, User, Project, Company
@pytest.fixture()
@@ -16,6 +17,7 @@ def mock_event():
event.model.save = Mock()
yield event
+
def test_event_user_id(user_instance):
""" Test event_user_id """
Event.save = Mock()
@@ -29,9 +31,10 @@ def test_event_user_id(user_instance):
)
assert 'data' in response
+
def test_event_company_id(company):
""" Test creation of event instance """
- #Case for creating Company
+ # Case for creating Company
Event.save = Mock()
Company.load = Mock()
event_data = 'test company created'
@@ -43,6 +46,7 @@ def test_event_company_id(company):
)
assert 'data' in response
+
def test_event_project_id(project):
""" Test event with event_project_id """
Event.save = Mock()
@@ -56,45 +60,53 @@ def test_event_project_id(project):
)
assert 'data' in response
+
def test_event_user_id_attribute(user_instance, mock_event):
""" Test event_user_id attribute """
mock_event.set_event_user_id(user_instance.get_user_id())
mock_event.save()
assert mock_event.get_event_user_id() == user_instance.get_user_id()
+
def test_event_company_name_lower_attribute(mock_event):
""" Test company_name_lower attribute """
mock_event.set_event_company_name("Company_lower")
mock_event.save()
assert mock_event.get_event_company_name_lower() == "company_lower"
+
def test_event_username_attribute(mock_event):
""" Test event_username attribute """
mock_event.set_event_user_name("foo_username")
mock_event.save()
assert mock_event.get_event_user_name() == "foo_username"
+
def test_event_user_name_lower_attribute(mock_event):
""" Test event_user_name_lower attribute """
mock_event.set_event_user_name("Username")
mock_event.save()
assert mock_event.get_event_user_name_lower() == "username"
+
def test_event_project_name_lower_attribute(mock_event):
- """ Test gettting project """
+ """ Test getting project """
mock_event.set_event_project_name("Project")
mock_event.save()
assert mock_event.get_event_project_name_lower() == "project"
+
def test_event_time(mock_event):
""" Test event time """
mock_event.save()
- assert mock_event.get_event_time() <= datetime.datetime.now()
+ assert mock_event.get_event_time() <= datetime.datetime.utcnow()
+
def test_event_time_epoch(mock_event):
""" Test event time epoch """
mock_event.save()
- assert mock_event.get_event_time_epoch() <= time.time()
+ assert mock_event.get_event_time_epoch() <= datetime.datetime.utcnow().timestamp()
+
def test_company_id_external_project_id(mock_event):
mock_event.set_event_project_external_id("external_id")
@@ -102,16 +114,19 @@ def test_company_id_external_project_id(mock_event):
mock_event.set_company_id_external_project_id()
assert mock_event.get_company_id_external_project_id() == "company_id#external_id"
+
def test_company_id_external_project_id_empty_test1(mock_event):
mock_event.set_event_project_external_id("external_id")
mock_event.set_company_id_external_project_id()
assert mock_event.get_company_id_external_project_id() == None
+
def test_company_id_external_project_id_empty_test2(mock_event):
mock_event.set_event_company_id("company_id")
mock_event.set_company_id_external_project_id()
assert mock_event.get_company_id_external_project_id() == None
+
def test_company_id_external_project_id_empty_test3(mock_event):
mock_event.set_company_id_external_project_id()
assert mock_event.get_company_id_external_project_id() == None
From 326a159c4c7a871a9c003739496f86f6611e8187 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 26 Feb 2021 21:15:15 +0300
Subject: [PATCH 0122/1276] [#2703,#2704,#2705] Feature/Emails (#2717)
- Updated Email subject for Signed CLAs
- Updated content for email sent to designees
- Updated approvals accepted email content
Signed-off-by: wanyaland
---
cla-backend-go/approval_list/handlers.go | 2 +-
cla-backend-go/approval_list/service.go | 124 +++++++++++++-----
cla-backend-go/approval_list/service_test.go | 26 ----
cla-backend-go/cmd/server.go | 2 +-
.../emails/approval_list_templates.go | 54 +++++++-
.../emails/approval_list_templates_test.go | 23 ++++
cla-backend-go/emails/params.go | 9 ++
.../emails/v2_cla_manager_templates.go | 6 +-
.../tests/v2_cla_manager_templates_test.go | 10 +-
cla-backend-go/v2/cla_manager/emails.go | 2 +
cla-backend-go/v2/cla_manager/service.go | 15 ++-
cla-backend/cla/models/docusign_models.py | 2 +-
.../cla/tests/unit/test_docusign_models.py | 2 +-
13 files changed, 200 insertions(+), 77 deletions(-)
delete mode 100644 cla-backend-go/approval_list/service_test.go
diff --git a/cla-backend-go/approval_list/handlers.go b/cla-backend-go/approval_list/handlers.go
index 94193a314..c362d5407 100644
--- a/cla-backend-go/approval_list/handlers.go
+++ b/cla-backend-go/approval_list/handlers.go
@@ -47,7 +47,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
func(params company.ApproveCclaWhitelistRequestParams, claUser *user.CLAUser) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- err := service.ApproveCclaWhitelistRequest(ctx, params.CompanyID, params.ProjectID, params.RequestID)
+ err := service.ApproveCclaWhitelistRequest(ctx, claUser, params.CompanyID, params.ProjectID, params.RequestID)
if err != nil {
return company.NewApproveCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index d9d5b34b7..cf8ad6731 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -9,6 +9,8 @@ import (
"fmt"
"net/http"
+ "github.com/sirupsen/logrus"
+
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/emails"
@@ -20,6 +22,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
@@ -38,7 +41,7 @@ const (
// IService interface defines the service methods/functions
type IService interface {
AddCclaWhitelistRequest(ctx context.Context, companyID string, claGroupID string, args models.CclaWhitelistRequestInput) (string, error)
- ApproveCclaWhitelistRequest(ctx context.Context, companyID, claGroupID, requestID string) error
+ ApproveCclaWhitelistRequest(ctx context.Context, claUser *user.CLAUser, ClacompanyID, claGroupID, requestID string) error
RejectCclaWhitelistRequest(ctx context.Context, companyID, claGroupID, requestID string) error
ListCclaWhitelistRequest(companyID string, claGroupID, status *string) (*models.CclaWhitelistRequestList, error)
ListCclaWhitelistRequestByCompanyProjectUser(companyID string, claGroupID, status, userID *string) (*models.CclaWhitelistRequestList, error)
@@ -46,6 +49,7 @@ type IService interface {
type service struct {
repo IRepository
+ projectService project.Service
userRepo users.UserRepository
companyRepo company.IRepository
projectRepo project.ProjectRepository
@@ -56,10 +60,11 @@ type service struct {
}
// NewService creates a new whitelist service
-func NewService(repo IRepository, projectsCLAGroupRepository projects_cla_groups.Repository, userRepo users.UserRepository, companyRepo company.IRepository, projectRepo project.ProjectRepository, signatureRepo signatures.SignatureRepository, corpConsoleURL string, httpClient *http.Client) IService {
+func NewService(repo IRepository, projectsCLAGroupRepository projects_cla_groups.Repository, projService project.Service, userRepo users.UserRepository, companyRepo company.IRepository, projectRepo project.ProjectRepository, signatureRepo signatures.SignatureRepository, corpConsoleURL string, httpClient *http.Client) IService {
return service{
repo: repo,
projectsCLAGroupRepository: projectsCLAGroupRepository,
+ projectService: projService,
userRepo: userRepo,
companyRepo: companyRepo,
projectRepo: projectRepo,
@@ -128,10 +133,18 @@ func (s service) AddCclaWhitelistRequest(ctx context.Context, companyID string,
}
// ApproveCclaWhitelistRequest is the handler for the approve CLA request
-func (s service) ApproveCclaWhitelistRequest(ctx context.Context, companyID, claGroupID, requestID string) error {
+func (s service) ApproveCclaWhitelistRequest(ctx context.Context, claUser *user.CLAUser, companyID, claGroupID, requestID string) error {
+
+ f := logrus.Fields{
+ "functionName": "ApproveCclaWhitelistRequest",
+ "companyID": companyID,
+ "claGroupID": claGroupID,
+ "requestID": requestID,
+ "Approver": claUser.Name,
+ }
err := s.repo.ApproveCclaWhitelistRequest(requestID)
if err != nil {
- log.Warnf("ApproveCclaWhitelistRequest - problem updating approved list with 'approved' status for request: %s, error: %+v",
+ log.WithFields(f).Warnf("ApproveCclaWhitelistRequest - problem updating approved list with 'approved' status for request: %s, error: %+v",
requestID, err)
return err
}
@@ -160,8 +173,43 @@ func (s service) ApproveCclaWhitelistRequest(ctx context.Context, companyID, cla
return errors.New(msg)
}
+ // Get project cla Group records
+ log.WithFields(f).Debugf("Getting SalesForce Projects for claGroup: %s ", claGroupID)
+ projectCLAGroups, getErr := s.projectsCLAGroupRepository.GetProjectsIdsForClaGroup(claGroupID)
+ if getErr != nil {
+ msg := fmt.Sprintf("Error getting SF projects for claGroup: %s ", claGroupID)
+ log.Debug(msg)
+ }
+
+ if len(projectCLAGroups) == 0 {
+ msg := fmt.Sprintf("Error getting SF projects for claGroup: %s ", claGroupID)
+ return errors.New(msg)
+ }
+
+ signedAtFoundation, signedErr := s.projectService.SignedAtFoundationLevel(ctx, projectCLAGroups[0].FoundationSFID)
+ if signedErr != nil {
+ msg := fmt.Sprintf("Problem checking project: %s , error: %+v", claGroupID, signedErr)
+ log.WithFields(f).Warn(msg)
+ return signedErr
+ }
+
+ var projectSFIDs []string
+ foundationSFID := projectCLAGroups[0].FoundationSFID
+
+ if signedAtFoundation {
+ // Get salesforce project by FoundationID
+ log.WithFields(f).Debugf("querying project service for project details...")
+ projectSFIDs = append(projectSFIDs, foundationSFID)
+ } else {
+ for _, pcg := range projectCLAGroups {
+ log.WithFields(f).Debugf("Getting salesforce project by SFID: %s ", pcg.ProjectSFID)
+ projectSFIDs = append(projectSFIDs, pcg.ProjectSFID)
+ }
+ }
+
// Send the email
- sendRequestApprovedEmailToRecipient(companyModel, claGroupModel, requestModel.UserName, requestModel.UserEmails[0])
+ s.sendRequestApprovedEmailToRecipient(ctx, s.projectService, s.projectsCLAGroupRepository, *claUser, companyModel, claGroupModel,
+ requestModel.UserName, requestModel.UserEmails[0], projectSFIDs)
return nil
}
@@ -345,40 +393,50 @@ func (s service) sendRequestRejectedEmailToRecipient(companyModel *models.Compan
}
}
-func requestApprovedEmailToRecipientContent(companyModel *models.Company, claGroupModel *models.ClaGroup, recipientName, recipientAddress string) (string, string, []string) {
- companyName := companyModel.CompanyName
+func (s service) sendRequestApprovedEmailToRecipient(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, claUser user.CLAUser, companyModel *models.Company, claGroupModel *models.ClaGroup, recipientName, recipientAddress string, projectSFIDs []string) {
+
+ f := logrus.Fields{
+ "functionName": "sendRequestApprovedEmailToRecipient",
+ utils.XREQUESTID: ctx.Value((utils.XREQUESTID)),
+ "claGroupName": claGroupModel.ProjectName,
+ "claGroupID": claGroupModel.ProjectID,
+ "companyName": companyModel.CompanyName,
+ "recipientName": recipientName,
+ "recipientAddress": recipientAddress,
+ }
+ companyName := companyModel.CompanyName
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: Approved List Request Accepted for %s", companyName)
recipients := []string{recipientAddress}
- body := fmt.Sprintf(`
-
Hello %s,
-
This is a notification email from EasyCLA regarding the company %s.
-
You have now been added to the approval list for %s.
-
To get started, please navigate back to GitHub or Gerrit and start the authorization process. Once you select the
-authorization link, you will be directed to the EasyCLA Contributor Console. GitHub users will need to authorize the
-tool to see your GitHub user name and email. Gerrit users will first need to log in with their LF Account. On the
-console landing page, select the corporate agreement option. To finish, search and select your company to acknowledge
-your association with your company. This will complete the authorization process. For GitHub users, your pull request
-will refresh and confirm that you are authorized. For Gerrit users, please log out of the UI and back in to complete the
-authorization.
-%s
-%s`,
- recipientName, companyName,
- companyName,
- utils.GetEmailHelpContent(claGroupModel.Version == utils.V2),
- utils.GetEmailSignOffContent())
-
- return subject, body, recipients
-}
-// sendRequestApprovedEmailToRecipient generates and sends an email to the specified recipient
-func sendRequestApprovedEmailToRecipient(companyModel *models.Company, claGroupModel *models.ClaGroup, recipientName, recipientAddress string) {
- subject, body, recipients := requestApprovedEmailToRecipientContent(companyModel, claGroupModel, recipientName, recipientAddress)
- err := utils.SendEmail(subject, body, recipients)
+ approver := ""
+ if claUser.LFUsername != "" {
+ approver = claUser.LFUsername
+ } else if claUser.LFEmail != "" {
+ approver = claUser.LFEmail
+ } else if claUser.Emails != nil {
+ approver = claUser.Emails[0]
+ }
+
+ body, err := emails.RenderApprovalListTemplate(
+ repository, projectService, projectSFIDs, emails.ApprovalListApprovedTemplateParams{
+ ApprovalTemplateParams: emails.ApprovalTemplateParams{
+ RecipientName: recipientName,
+ CompanyName: companyName,
+ CLAGroupName: claGroupModel.ProjectName,
+ Approver: approver,
+ },
+ },
+ )
if err != nil {
- log.Warnf("problem sending email with subject: %s to recipients: %+v, error: %+v", subject, recipients, err)
+ log.WithFields(f).Warnf("rendering email failed for : %s : %v", emails.ApprovalListApprovedTemplateName, err)
+ return
+ }
+ err = utils.SendEmail(subject, body, recipients)
+ if err != nil {
+ log.WithFields(f).Warnf("problem sending email with subject: %s to recipients: %+v, error: %+v", subject, recipients, err)
} else {
- log.Debugf("sent email with subject: %s to recipients: %+v", subject, recipients)
+ log.WithFields(f).Debugf("sent email with subject: %s to recipients: %+v", subject, recipients)
}
}
diff --git a/cla-backend-go/approval_list/service_test.go b/cla-backend-go/approval_list/service_test.go
deleted file mode 100644
index ebc053a70..000000000
--- a/cla-backend-go/approval_list/service_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-package approval_list
-
-import (
- "testing"
-
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/stretchr/testify/assert"
-)
-
-func TestRequestApprovedEmailToRecipientContent(t *testing.T) {
- subject, body, recipients := requestApprovedEmailToRecipientContent(
- &models.Company{
- CompanyName: "gardenerLtd"},
- &models.ClaGroup{Version: "v2"},
- "john",
- "john@john.com")
-
- assert.Equal(t, "EasyCLA: Approved List Request Accepted for gardenerLtd", subject)
- assert.Equal(t, []string{"john@john.com"}, recipients)
- assert.Contains(t, body, "Hello john,")
- assert.Contains(t, body, "This is a notification email from EasyCLA regarding the company gardenerLtd")
- assert.Contains(t, body, "You have now been added to the approval list for gardenerLtd")
-}
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 82cea5da4..0f2d13ce6 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -270,7 +270,7 @@ func server(localMode bool) http.Handler {
v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
v2ClaManagerService := v2ClaManager.NewService(v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
- v1ApprovalListService := approval_list.NewService(approvalListRepo, projectClaGroupRepo, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleV2URL, http.DefaultClient)
+ v1ApprovalListService := approval_list.NewService(approvalListRepo, projectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
diff --git a/cla-backend-go/emails/approval_list_templates.go b/cla-backend-go/emails/approval_list_templates.go
index 478cf4cac..4b0dc1613 100644
--- a/cla-backend-go/emails/approval_list_templates.go
+++ b/cla-backend-go/emails/approval_list_templates.go
@@ -3,7 +3,13 @@
package emails
-import "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+import (
+ "strings"
+
+ "github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+)
// ApprovalListRejectedTemplateParams is email params for ApprovalListRejectedTemplate
type ApprovalListRejectedTemplateParams struct {
@@ -28,6 +34,24 @@ If you have further questions about this denial, please contact one of the exist
`
)
+// ApprovalListApprovedTemplateParams is email params for Approval
+type ApprovalListApprovedTemplateParams struct {
+ ApprovalTemplateParams
+}
+
+const (
+ // ApprovalListApprovedTemplateName is email template name for ApprovalListRejectedTemplate
+ ApprovalListApprovedTemplateName = "ApprovalListApprovedTemplate"
+ // ApprovalListApprovedTemplate is email template for
+ ApprovalListApprovedTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.
+
You have been added to the Approval list of {{.CompanyName}} for {{.CLAGroupName}} by CLA Manager {{.Approver}}.
+
This means that you are authorized to contribute to the any of the following project(s) associated with the CLA Group {{.CLAGroupName}}: {{.GetProjects}}
+
If you had previously submitted a pull request to any any the above project(s) that had failed, you can now go back to it and follow the link to verify with your organization.
+ `
+)
+
// RequestToAuthorizeTemplateParams is email params for RequestToAuthorizeTemplate
type RequestToAuthorizeTemplateParams struct {
CLAManagerTemplateParams
@@ -72,3 +96,31 @@ func RenderRequestToAuthorizeTemplate(repository projects_cla_groups.Repository,
)
}
+
+// GetProjects returns the single Project or comma separated projects if more than one
+func (p ApprovalListApprovedTemplateParams) GetProjects() string {
+ if len(p.Projects) == 1 {
+ return p.Projects[0].ExternalProjectName
+ }
+
+ var projectNames []string
+ for _, p := range p.Projects {
+ projectNames = append(projectNames, p.ExternalProjectName)
+ }
+
+ return strings.Join(projectNames, ", ")
+}
+
+// RenderApprovalListTemplate renders RenderApprovalListTemplate
+func RenderApprovalListTemplate(repository projects_cla_groups.Repository, projectService project.Service, projectSFIDs []string, params ApprovalListApprovedTemplateParams) (string, error) {
+ // prefill the projects data
+ projects, err := PrefillCLAProjectParams(repository, projectService, projectSFIDs, "")
+ if err != nil {
+ return "", err
+ }
+
+ params.Projects = projects
+
+ return RenderTemplate(utils.V2, ApprovalListApprovedTemplateName,
+ ApprovalListApprovedTemplate, params)
+}
diff --git a/cla-backend-go/emails/approval_list_templates_test.go b/cla-backend-go/emails/approval_list_templates_test.go
index f2096da8f..f7c2524a7 100644
--- a/cla-backend-go/emails/approval_list_templates_test.go
+++ b/cla-backend-go/emails/approval_list_templates_test.go
@@ -31,6 +31,29 @@ func TestApprovalListRejectedTemplate(t *testing.T) {
assert.Contains(t, result, "
LFUserName LFEmail
")
}
+func TestApprovalListApprovedTemplate(t *testing.T) {
+ params := ApprovalListApprovedTemplateParams{
+ ApprovalTemplateParams: ApprovalTemplateParams{
+ RecipientName: "Recipient",
+ CLAGroupName: "CLAGroupFoo",
+ CompanyName: "CompanyFoo",
+ Approver: "LFUsername",
+ Projects: []CLAProjectParams{
+ {ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
+ {ExternalProjectName: "Project2", ProjectSFID: "ProjectSFID2", FoundationSFID: "FoundationSFID2", CorporateConsole: "http://CorporateConsole.com"},
+ },
+ },
+ }
+
+ result, err := RenderTemplate(utils.V2, ApprovalListApprovedTemplateName, ApprovalListApprovedTemplate, params)
+
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello Recipient")
+ assert.Contains(t, result, "regarding the CLA Group CLAGroupFoo")
+ assert.Contains(t, result, "You have been added to the Approval list of CompanyFoo for CLAGroupFoo by CLA Manager LFUsername.")
+ assert.Contains(t, result, "This means that you are authorized to contribute to the any of the following project(s) associated with the CLA Group CLAGroupFoo: Project1, Project2")
+}
+
func TestRequestToAuthorizeTemplate(t *testing.T) {
params := RequestToAuthorizeTemplateParams{
CLAManagerTemplateParams: CLAManagerTemplateParams{
diff --git a/cla-backend-go/emails/params.go b/cla-backend-go/emails/params.go
index a54884c87..fca576309 100644
--- a/cla-backend-go/emails/params.go
+++ b/cla-backend-go/emails/params.go
@@ -69,6 +69,15 @@ type CLAManagerTemplateParams struct {
ChildProjectCount int
}
+// ApprovalTemplateParams details approval fields for contributor
+type ApprovalTemplateParams struct {
+ RecipientName string
+ CompanyName string
+ CLAGroupName string
+ Approver string
+ Projects []CLAProjectParams
+}
+
// GetProjectNameOrFoundation returns if the foundationName is set it gets back
// the foundation Name otherwise the ProjectName is returned
func (claParams CLAManagerTemplateParams) GetProjectNameOrFoundation() string {
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 8d20d06ed..3457c3d1f 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -186,16 +186,14 @@ const (
V2ToCLAManagerDesigneeTemplate = `
Hello {{.RecipientName}},
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
-
We received a request from {{.ContributorName}} ({{.ContributorEmail}}): to contribute to the above projects on behalf of your organization
-
Before the user contribution can be accepted, your organization must sign a Corporate CLA (CCLA).The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute
+
We received a request from {{.ContributorName}} ({{.ContributorEmail}}) to contribute to the above projects on behalf of your organization.
+
Before the user contribution can be accepted, your organization must sign a Corporate CLA (CCLA).The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute.
Please complete the following steps:
After login, you will be redirected to the portal {{.CorporateConsole}} where you can either sign the CLA for any of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}, or send it to an authorized signatory for your company.
After signing the CLA, you will need to add this contributor to the approved list in the CLA Manager console.
After adding the contributor, please notify them so that they can complete the contribution process.
-
Kindly login to this portal {{.CorporateConsole}} and sign the CLA for one of the project(s): {{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}}.
-
After signing the CLA, you will need to add this contributor to the approved list. Please notify the contributor once they are added, so that they can complete the contribution process.
`
)
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index decf5ec26..33999a9bf 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -149,9 +149,8 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1, Project2")
- assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue):")
- assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, `CLA for one of the project(s): Project1,Project2`)
+ assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, `CLA for any of the project(s): Project1,Project2`)
params.Projects = []emails.CLAProjectParams{
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
@@ -161,9 +160,8 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1")
- assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue):")
- assert.Contains(t, result, "Kindly login to this portal http://CorporateConsole.com")
- assert.Contains(t, result, `CLA for one of the project(s): Project1`)
+ assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, `CLA for any of the project(s): Project1`)
}
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index e400f01d3..18dce0f9e 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -234,6 +234,8 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, project
"projectNames": strings.Join(projectNames, ","),
"role": role,
"corporateConsoleV2URL": corporateConsoleV2URL,
+ "requesterUsername": requesterUsername,
+ "requesterEmail": requesterEmail,
}
subject := "EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager"
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index fb9706796..2b1ec453f 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -832,7 +832,7 @@ func (s *service) ValidateInviteCompanyAdminCheck(ctx context.Context, f logrus.
return nil
}
-func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, LfxPortalURL, CorporateConsoleV2URL string) ([]*models.ClaManagerDesignee, error) {
+func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User, LfxPortalURL, CorporateConsoleV2URL string) ([]*models.ClaManagerDesignee, error) { //nolint
f := logrus.Fields{
"functionName": "cla_manager.service.InviteCompanyAdmin",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -976,10 +976,19 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
// Get suggested CLA Manager user details
user, userErr := userService.SearchUserByEmail(userEmail)
if userErr != nil || (user != nil && user.Username == "") {
+ contributorEmail, contributorUsername := "", ""
msg := fmt.Sprintf("UserEmail: %s has no LF Login and has been sent an invite email to create an account , error: %+v", userEmail, userErr)
log.Warn(msg)
- contibutorEmail := GetNonNoReplyUserEmail(contributor.UserEmails)
- sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectService, s.projectCGRepo, contributor.UserName, contibutorEmail, name, userEmail, organization.Name, organization.ID, projectSFs, projectSFIDs, foundationSFID, "cla-manager-designee", LfxPortalURL)
+
+ // Get username and useremail details for contributor
+ if contributor.LFEmail != "" && contributor.UserName != "" {
+ contributorEmail = contributor.LFEmail
+ contributorUsername = contributor.UserName
+ } else {
+ contributorUsername, contributorEmail = getContributorPublicEmail(contributor)
+ }
+
+ sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectService, s.projectCGRepo, contributorUsername, contributorEmail, name, userEmail, organization.Name, organization.ID, projectSFs, projectSFIDs, foundationSFID, "cla-manager-designee", LfxPortalURL)
if sendErr != nil {
msg := fmt.Sprintf("Problem sending email to user: %s , error: %+v", userEmail, sendErr)
log.Warn(msg)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 8a0f9c0c2..8c9aca376 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -2205,7 +2205,7 @@ def document_signed_email_content(icla: bool, project: Project, signature: Signa
else:
recipient_name = "CLA Manager"
- subject = f'EasyCLA: CLA Signature Signed for {project.get_project_name()}'
+ subject = f'EasyCLA: CLA Signed for {project.get_project_name()}'
body = f'''
Hello {recipient_name},
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
diff --git a/cla-backend/cla/tests/unit/test_docusign_models.py b/cla-backend/cla/tests/unit/test_docusign_models.py
index 1c0f6437c..f07b856c6 100644
--- a/cla-backend/cla/tests/unit/test_docusign_models.py
+++ b/cla-backend/cla/tests/unit/test_docusign_models.py
@@ -822,7 +822,7 @@ def test_document_signed_email_content():
assert subject is not None
assert body is not None
- assert "Signature Signed for JohnsProject" in subject
+ assert "Signed for JohnsProject" in subject
assert "Hello john" in body
assert "EasyCLA regarding the project JohnsProject" in body
assert "The CLA has now been signed." in body
From 53b136ab39e6359b113cbd7a7e204ef285d0f7f8 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 2 Mar 2021 02:28:12 +0300
Subject: [PATCH 0123/1276] [#3136] Bug/Corporate Console Admin Email (#2724)
- Resolved email sending to company admin on corporate console
Signed-off-by: wanyaland
---
cla-backend-go/v2/cla_manager/service.go | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 2b1ec453f..f6ce5a327 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -661,6 +661,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
}
orgService := v2OrgService.GetClient()
+ userService := v2UserService.GetClient()
log.WithFields(f).Debugf("loading company by external ID...")
// Search for salesForce Company aka external Company
@@ -720,7 +721,14 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
for _, admin := range scopes.Userroles {
log.WithFields(f).Debugf("sending email to organization admin: %+v", admin)
- s.SendEmailToOrgAdmin(ctx, s.projectCGRepo, s.projectService, admin.Contact.EmailAddress, admin.Contact.Name, v1CompanyModel.CompanyName, projectSF.Name, projectSF.ID, authUser.Email, authUser.UserName, LfxPortalURL)
+
+ adminUser, adminErr := userService.GetUser(admin.Contact.ID)
+ if adminErr != nil {
+ msg := fmt.Sprintf("Failed to get user for ID: %s ", admin.Contact.ID)
+ log.Warn(msg)
+ return nil, adminErr
+ }
+ s.SendEmailToOrgAdmin(ctx, s.projectCGRepo, s.projectService, userService.GetPrimaryEmail(adminUser), admin.Contact.Name, v1CompanyModel.CompanyName, projectSF.Name, projectSF.ID, authUser.Email, authUser.UserName, LfxPortalURL)
// Make a note in the event log
s.eventService.LogEvent(&events.LogEventArgs{
EventType: events.ContributorNotifyCompanyAdminType,
@@ -738,7 +746,6 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
}
log.WithFields(f).Debug("not sending admin email...")
- userService := v2UserService.GetClient()
log.WithFields(f).Debug("searching user in user service...")
// This routine is taking 24-29 seconds when running locally -> User service in DEV
//lfxUser, userErr := userService.SearchUserByEmail(userEmail)
From 69c0b2a0eaaca663d34d361b68e2f7ef948e51c1 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 2 Mar 2021 02:32:27 +0300
Subject: [PATCH 0124/1276] Feature/Python hug logging (#2722)
- Added elapsed time metrics to Python APIs
Signed-off-by: wanyaland
---
cla-backend/cla/middleware.py | 29 +++++++++++++++++++++++++++++
cla-backend/cla/routes.py | 3 ++-
cla-backend/cla/utils.py | 5 +++++
3 files changed, 36 insertions(+), 1 deletion(-)
create mode 100644 cla-backend/cla/middleware.py
diff --git a/cla-backend/cla/middleware.py b/cla-backend/cla/middleware.py
new file mode 100644
index 000000000..7c5b072e9
--- /dev/null
+++ b/cla-backend/cla/middleware.py
@@ -0,0 +1,29 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+from hug.middleware import LogMiddleware
+from datetime import datetime
+from timeit import default_timer
+
+class CLALogMiddleware(LogMiddleware):
+ """CLA log middleware"""
+
+ def __init__(self, logger=None):
+ super().__init__(logger=logger)
+ self.elapsed_time = 0
+ self.start_time = None
+ self.end_time = None
+
+ def process_request(self, request, response):
+ """Logs CLA request """
+ self.logger.info(f'BEGIN {request.method} {request.path}')
+ self.start_time = datetime.utcnow()
+ super().process_request(request, response)
+
+ def process_response(self, request, response, resource, req_succeeded):
+ """Logs data returned by CLA API """
+ if self.start_time:
+ self.elapsed_time = datetime.utcnow() - self.start_time
+ super().process_response(request, response, resource, req_succeeded)
+ self.logger.info(f'END {request.method} {request.path} - elapsed_time : {self.elapsed_time.seconds} secs')
+
diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py
index 1b4eb787d..5c17879f2 100755
--- a/cla-backend/cla/routes.py
+++ b/cla-backend/cla/routes.py
@@ -34,6 +34,7 @@
get_supported_repository_providers,
get_supported_document_content_types,
get_session_middleware,
+ get_log_middleware
)
@@ -1786,4 +1787,4 @@ def create_event(
# Session Middleware
__hug__.http.add_middleware(get_session_middleware())
-__hug__.http.add_middleware(LogMiddleware(logger=cla.log))
+__hug__.http.add_middleware(get_log_middleware())
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 2c8d0a5c7..9a4826325 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -17,6 +17,8 @@
import falcon
import requests
from hug.middleware import SessionMiddleware
+from hug.middleware import LogMiddleware
+from cla.middleware import CLALogMiddleware
from requests_oauthlib import OAuth2Session
import cla
@@ -38,6 +40,9 @@ def get_cla_path():
cla_root_dir = os.path.dirname(cla_folder_dir)
return cla_root_dir
+def get_log_middleware():
+ """Prepare the hug middleware to manage logging. """
+ return CLALogMiddleware(logger=cla.log)
def get_session_middleware():
"""Prepares the hug middleware to manage key-value session data."""
From c5da947061f44b37d3a044b8ee1b9154898925b4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Mar 2021 15:46:08 -0800
Subject: [PATCH 0125/1276] Resolved 2716 Activity Log for Enroll/Unenroll
(#2725)
- Added new events for enroll/unenroll
- Refactored event log to ensure CLA Group and Project ID were distinct. Updated load info logic.
- Added utility method to help differentiate between internal ID's and SFID's
Signed-off-by: David Deal
---
cla-backend-go/approval_list/handlers.go | 6 +-
cla-backend-go/cla_manager/handlers.go | 8 +-
cla-backend-go/cla_manager/service.go | 10 +-
cla-backend-go/company/handlers.go | 8 +-
cla-backend-go/events/event_data.go | 250 ++++++++++------
cla-backend-go/events/event_types.go | 8 +-
cla-backend-go/events/repository.go | 19 +-
cla-backend-go/events/service.go | 175 ++++++++---
cla-backend-go/gerrits/handlers.go | 4 +-
.../github_organizations/handlers.go | 6 +-
cla-backend-go/project/handlers.go | 18 +-
cla-backend-go/repositories/handlers.go | 4 +-
cla-backend-go/signatures/handlers.go | 4 +-
cla-backend-go/signatures/service.go | 37 +--
cla-backend-go/swagger/common/event.yaml | 21 +-
cla-backend-go/tests/utils_test.go | 24 ++
cla-backend-go/utils/utils.go | 6 +
cla-backend-go/v2/cla_groups/handlers.go | 157 ++++------
cla-backend-go/v2/cla_groups/helpers.go | 84 ++++--
cla-backend-go/v2/cla_groups/models.go | 40 +++
cla-backend-go/v2/cla_groups/service.go | 110 ++++---
.../v2/dynamo_events/projects_cla_groups.go | 279 +++++++++---------
cla-backend-go/v2/dynamo_events/service.go | 5 +-
23 files changed, 781 insertions(+), 502 deletions(-)
create mode 100644 cla-backend-go/v2/cla_groups/models.go
diff --git a/cla-backend-go/approval_list/handlers.go b/cla-backend-go/approval_list/handlers.go
index c362d5407..0a6b59bec 100644
--- a/cla-backend-go/approval_list/handlers.go
+++ b/cla-backend-go/approval_list/handlers.go
@@ -32,7 +32,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
return company.NewAddCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CCLAApprovalListRequestCreated,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
@@ -52,7 +52,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
return company.NewApproveCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CCLAApprovalListRequestApproved,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
@@ -72,7 +72,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
return company.NewRejectCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CCLAApprovalListRequestRejected,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
diff --git a/cla-backend-go/cla_manager/handlers.go b/cla-backend-go/cla_manager/handlers.go
index 47e687b2e..a4f9752a9 100644
--- a/cla-backend-go/cla_manager/handlers.go
+++ b/cla-backend-go/cla_manager/handlers.go
@@ -165,7 +165,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Send an event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerAccessRequestCreated,
ProjectID: params.ProjectID,
ClaGroupModel: claGroupModel,
@@ -339,7 +339,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Send an event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerAccessRequestApproved,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
@@ -441,7 +441,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Send an event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerAccessRequestDenied,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
@@ -569,7 +569,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Send an event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerAccessRequestDeleted,
ProjectID: params.ProjectID,
CompanyID: params.CompanyID,
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index cb6e8b4fd..7ed134f6e 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -239,9 +239,11 @@ func (s service) AddClaManager(ctx context.Context, companyID string, claGroupID
sendClaManagerAddedEmailToUser(companyModel, claGroupModel, userModel.Username, userModel.LfEmail, projectSFName)
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerCreated,
- ProjectID: claGroupID,
+ ProjectID: claGroupModel.ProjectExternalID,
+ CLAGroupID: claGroupID,
+ CLAGroupName: claGroupModel.ProjectName,
ClaGroupModel: claGroupModel,
CompanyID: companyID,
CompanyModel: companyModel,
@@ -341,7 +343,9 @@ func (s service) RemoveClaManager(ctx context.Context, companyID string, claGrou
// Send an event
s.eventsService.LogEvent(&events.LogEventArgs{
EventType: events.ClaManagerDeleted,
- ProjectID: claGroupID,
+ ProjectID: claGroupModel.ProjectExternalID,
+ CLAGroupID: claGroupID,
+ CLAGroupName: claGroupModel.ProjectName,
ClaGroupModel: claGroupModel,
CompanyID: companyID,
CompanyModel: companyModel,
diff --git a/cla-backend-go/company/handlers.go b/cla-backend-go/company/handlers.go
index 0c7cebbf5..f4fc7243a 100644
--- a/cla-backend-go/company/handlers.go
+++ b/cla-backend-go/company/handlers.go
@@ -257,7 +257,7 @@ func Configure(api *operations.ClaAPI, service IService, usersService users.Serv
return company.NewAddUsertoCompanyAccessListBadRequest().WithXRequestID(reqID)
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CompanyACLUserAdded,
CompanyID: params.CompanyID,
UserID: claUser.UserID,
@@ -280,7 +280,7 @@ func Configure(api *operations.ClaAPI, service IService, usersService users.Serv
}
// Add an event to the log
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CompanyACLRequestAdded,
CompanyID: params.CompanyID,
UserID: claUser.UserID,
@@ -305,7 +305,7 @@ func Configure(api *operations.ClaAPI, service IService, usersService users.Serv
}
// Add an event to the log
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CompanyACLRequestApproved,
CompanyID: params.CompanyID,
UserID: claUser.UserID,
@@ -330,7 +330,7 @@ func Configure(api *operations.ClaAPI, service IService, usersService users.Serv
}
// Add an event to the log
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CompanyACLRequestDenied,
CompanyID: params.CompanyID,
UserID: claUser.UserID,
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 097f70186..0e48bbb00 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -13,6 +13,18 @@ type EventData interface {
GetEventSummaryString(args *LogEventArgs) (eventData string, containsPII bool)
}
+type CLAGroupEnrolledProjectData struct {
+}
+
+type CLAGroupUnenrolledProjectData struct {
+}
+
+type ProjectServiceCLAEnabledData struct {
+}
+
+type ProjectServiceCLADisabledData struct {
+}
+
// RepositoryAddedEventData . . .
type RepositoryAddedEventData struct {
RepositoryName string
@@ -344,90 +356,114 @@ type ClaManagerRoleDeletedData struct {
UserEmail string
}
+// GetEventDetailsString . . .
+func (ed *CLAGroupEnrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s (%s/%s) enabled the the project %s (%s) from the CLA Group %s (%s).",
+ args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.ClaGroupModel.ProjectName, args.ClaGroupModel.ProjectID), false
+}
+
+// GetEventDetailsString . . .
+func (ed *CLAGroupUnenrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s (%s/%s) unenrolled the the project %s (%s) from the CLA Group %s (%s).",
+ args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.ClaGroupModel.ProjectName, args.ClaGroupModel.ProjectID), false
+}
+
+// GetEventDetailsString . . .
+func (ed *ProjectServiceCLAEnabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s (%s/%s) enabled the CLA Service for the project %s (%s)",
+ args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+}
+
+// GetEventDetailsString . . .
+func (ed *ProjectServiceCLADisabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s (%s/%s) disabled the CLA Service for the project %s (%s)",
+ args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+}
+
// GetEventDetailsString . . .
func (ed *RepositoryAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository: %s was added for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository: %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was updated for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *UserCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s added. User Details: %+v.", args.userName, args.UserModel)
+ data := fmt.Sprintf("User: %s added. User Details: %+v.", args.UserName, args.UserModel)
return data, true
}
// GetEventDetailsString . . .
func (ed *UserUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("User: %s updated. User Details: %+v.", args.userName, *args.UserModel), true
+ return fmt.Sprintf("User: %s updated. User Details: %+v.", args.UserName, *args.UserModel), true
}
// GetEventDetailsString . . .
func (ed *UserDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s deleted. User ID: %s.", args.userName, ed.DeletedUserID)
+ data := fmt.Sprintf("User: %s deleted. User ID: %s.", args.UserName, ed.DeletedUserID)
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLRequestAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s added pending invite with ID: %s and Email: %s for Company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.companyName)
+ ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Access Aproved for User: %s, ID: %s, Email: %s Company Group: %s.",
- ed.UserName, args.companyName, ed.UserID, ed.UserEmail)
+ ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLRequestDeniedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Access Denied for User: %s, ID: %s, Email: %s Company Group: %s.",
- ed.UserName, args.companyName, ed.UserID, ed.UserEmail)
+ ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User with LF Username: %s added to the ACL for Company: %s by: %s.",
- ed.UserLFID, args.companyName, args.userName)
+ ed.UserLFID, args.CompanyName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLATemplateCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("PDF Templates created for Project: %s by: %s.", args.userName, args.projectName)
+ data := fmt.Sprintf("PDF Templates created for Project: %s by: %s.", args.UserName, args.ProjectName)
return data, true
}
@@ -438,14 +474,14 @@ func (ed *GitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEvent
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.userName)
+ data = data + fmt.Sprintf(" by: %s.", args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *GitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization: %s was deleted by: %s.",
- ed.GitHubOrganizationName, args.userName)
+ ed.GitHubOrganizationName, args.UserName)
return data, true
}
@@ -456,21 +492,21 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEve
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf("by: %s.", args.userName)
+ data = data + fmt.Sprintf("by: %s.", args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s approved a CCLA Approval Request for Project: %s and Company: %s with Request ID: %s.",
- args.userName, args.projectName, args.companyName, ed.RequestID)
+ args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
return data, true
}
// GetEventDetailsString . . .
func (ed *CCLAApprovalListRequestRejectedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s rejected a CCLA Approval Request for Project: %s, Company: %s with Request ID: %s.",
- args.userName, args.projectName, args.companyName, ed.RequestID)
+ args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
return data, true
}
@@ -519,161 +555,161 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventDetailsString(args *LogEven
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Email: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Email: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Domain: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Domain %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.companyName, args.projectName)
+ ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s created a CCLA Approval Request for Project: %s, Company: %s with Request ID: %s.",
- args.userName, args.projectName, args.companyName, ed.RequestID)
+ args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
return data, true
}
// GetEventDetailsString . . .
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the whitelist for Company %s, Project: %s.",
- args.userName, ed.GitHubOrganizationName, args.companyName, args.projectName)
+ args.UserName, ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the whitelist for Company: %s, Project: %s.",
- args.userName, ed.GitHubOrganizationName, args.companyName, args.projectName)
+ args.UserName, ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *ClaManagerAccessRequestAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s has requested to be CLA Manager for Company %s, Project: %s.",
- args.userName, ed.CompanyName, ed.ProjectName)
+ args.UserName, ed.CompanyName, ed.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *ClaManagerAccessRequestDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s has deleted CLA Manager Request with ID: %s.",
- args.userName, ed.RequestID)
+ args.UserName, ed.RequestID)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Group ID: %s, Name: %s was created by: %s.",
- args.ProjectID, args.projectName, args.userName)
+ args.ProjectID, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Group ID: %s was updated by: %s with Name: %s, Description: %s.",
- args.ProjectID, args.userName, ed.ClaGroupName, ed.ClaGroupDescription)
+ args.ProjectID, args.UserName, ed.ClaGroupName, ed.ClaGroupDescription)
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Group ID: %s was deleted by: %s.",
- args.ProjectID, args.userName)
+ args.ProjectID, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Gerrit Repositories were deleted due to CLA Group/Project: %s deletion.",
- ed.DeletedCount, args.projectName)
+ ed.DeletedCount, args.ProjectName)
return data, false
}
// GetEventDetailsString . . .
func (ed *GerritAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Gerrit Repository: %s was added by: %s.", ed.GerritRepositoryName, args.userName)
+ data := fmt.Sprintf("Gerrit Repository: %s was added by: %s.", ed.GerritRepositoryName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Gerrit Repository: %s was deleted by: %s.", ed.GerritRepositoryName, args.userName)
+ data := fmt.Sprintf("Gerrit Repository: %s was deleted by: %s.", ed.GerritRepositoryName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *GitHubProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub Repositories were deleted due to CLA Group/Project: [%s] deletion.",
- ed.DeletedCount, args.projectName)
+ ed.DeletedCount, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *SignatureProjectInvalidatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Signatures were invalidated (approved set to false) due to CLA Group/Project: %s deletion.",
- ed.InvalidatedCount, args.projectName)
+ ed.InvalidatedCount, args.ProjectName)
return data, true
}
// GetEventDetailsString . . .
func (ed *ContributorNotifyCompanyAdminData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s notified Company Admin: %s by Email: %s for Company ID: %s, Name: %s.",
- args.userName, ed.AdminName, ed.AdminEmail, args.companyName, args.CompanyID)
+ args.UserName, ed.AdminName, ed.AdminEmail, args.CompanyName, args.CompanyID)
return data, true
}
// GetEventDetailsString . . .
func (ed *ContributorNotifyCLADesignee) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s notified CLA Designee: %s by Email: %s for Project Name : %s, ID: %s and Company Name: %s, ID: %s.",
- args.userName, ed.DesigneeName, ed.DesigneeEmail,
- args.projectName, args.ExternalProjectID,
- args.companyName, args.CompanyID)
+ args.UserName, ed.DesigneeName, ed.DesigneeEmail,
+ args.ProjectName, args.ExternalProjectID,
+ args.CompanyName, args.CompanyID)
return data, true
}
@@ -681,8 +717,8 @@ func (ed *ContributorNotifyCLADesignee) GetEventDetailsString(args *LogEventArgs
func (ed *ContributorAssignCLADesignee) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User Name: %s, Email: %s was assigned as CLA Manager Designee for project Name: %s, ID: %s and Company Name: %s, ID: %s by: %s.",
ed.DesigneeName, ed.DesigneeEmail,
- args.projectName, args.ExternalProjectID,
- args.companyName, args.CompanyID, args.userName)
+ args.ProjectName, args.ExternalProjectID,
+ args.CompanyName, args.CompanyID, args.UserName)
return data, true
}
@@ -703,102 +739,124 @@ func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string
// GetEventDetailsString . . .
func (ed *ClaManagerRoleCreatedData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s, Email: %s was added to Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.userName)
+ data := fmt.Sprintf("User: %s, Email: %s was added to Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.UserName)
return data, false
}
// GetEventDetailsString . . .
func (ed *ClaManagerRoleDeletedData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s, Email: %s was removed from Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.userName)
+ data := fmt.Sprintf("User: %s, Email: %s was removed from Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.UserName)
return data, false
}
// Event Summary started
+// GetEventDetailsString . . .
+func (ed *CLAGroupEnrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s enabled the the project %s from the CLA Group %s.",
+ args.UserName, args.ProjectName, args.ClaGroupModel.ProjectName), false
+}
+
+// GetEventDetailsString . . .
+func (ed *CLAGroupUnenrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s unenrolled the the project %s from the CLA Group %s.",
+ args.UserName, args.ProjectName, args.ClaGroupModel.ProjectName), false
+}
+
+// GetEventDetailsString . . .
+func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s enabled the CLA Service for the project %s.", args.UserName, args.ProjectName), false
+}
+
+// GetEventDetailsString . . .
+func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ return fmt.Sprintf("%s disabled the CLA Service for the project %s.", args.UserName, args.ProjectName), false
+}
+
// GetEventSummaryString . . .
func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was added to Project: %s by: %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("GitHub Repository: %s was added to Project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was deleted from Project: %s by: %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("GitHub Repository: %s was deleted from Project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was updated for the project project: %s by: %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("GitHub Repository: %s was updated for the project project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.projectName, args.userName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *UserCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s was added, User Details: %+v.", args.userName, args.UserModel)
+ data := fmt.Sprintf("User: %s was added, User Details: %+v.", args.UserName, args.UserModel)
return data, true
}
// GetEventSummaryString . . .
func (ed *UserUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("User: %s was updated, User Details: %+v.", args.userName, *args.UserModel), true
+ return fmt.Sprintf("User: %s was updated, User Details: %+v.", args.UserName, *args.UserModel), true
}
// GetEventSummaryString . . .
func (ed *UserDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User ID : %s was deleted by: %s.", ed.DeletedUserID, args.userName)
+ data := fmt.Sprintf("User ID : %s was deleted by: %s.", ed.DeletedUserID, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s with ID: %s, Email: %s requested Company Invite for Company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.companyName)
+ ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Company Invite was approved access for User: %s with ID: %s, Email: %s for company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.companyName)
+ ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Company Invite was denied access for User: %s with ID: %s, Email %s for Company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.companyName)
+ ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User with LF Username %s was added to the ACL for Company: %s by: %s.",
- ed.UserLFID, args.companyName, args.userName)
+ ed.UserLFID, args.CompanyName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLATemplateCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("PDF templates were created for Project %s by: %s.", args.projectName, args.userName)
+ data := fmt.Sprintf("PDF templates were created for Project %s by: %s.", args.ProjectName, args.UserName)
return data, true
}
@@ -809,14 +867,14 @@ func (ed *GitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEvent
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.userName)
+ data = data + fmt.Sprintf(" by: %s.", args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *GitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization: %s was deleted by: %s.",
- ed.GitHubOrganizationName, args.userName)
+ ed.GitHubOrganizationName, args.UserName)
return data, true
}
@@ -827,21 +885,21 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEve
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.userName)
+ data = data + fmt.Sprintf(" by: %s.", args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s approved a CCLA Approval Request for Project: %s, Company: %s.",
- args.userName, args.projectName, args.companyName)
+ args.UserName, args.ProjectName, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestRejectedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s rejected a CCLA Approval Request for Project: %s, Company: %s.",
- args.userName, args.projectName, args.companyName)
+ args.UserName, args.ProjectName, args.CompanyName)
return data, true
}
@@ -890,159 +948,159 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEven
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added Email: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListEmail, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed Email: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListEmail, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added Domain: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListDomain, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed Domain: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListDomain, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubUsername, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubUsername, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubOrg, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubOrg, args.companyName, args.projectName)
+ ed.UserName, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s created a CCLA Approval Request for Project: %s, Company: %s.",
- args.userName, args.projectName, args.companyName)
+ args.UserName, args.ProjectName, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the whitelist for Project: %s, Company: %s.",
- args.userName, ed.GitHubOrganizationName, args.projectName, args.companyName)
+ args.UserName, ed.GitHubOrganizationName, args.ProjectName, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the whitelist for Project: %s, Company: %s.",
- args.userName, ed.GitHubOrganizationName, args.projectName, args.companyName)
+ args.UserName, ed.GitHubOrganizationName, args.ProjectName, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerAccessRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s has requested to be CLA Manager for the project %s, the company %s.",
- args.userName, ed.ProjectName, ed.CompanyName)
+ args.UserName, ed.ProjectName, ed.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerAccessRequestDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s has deleted a request to be CLA Manager.",
- args.userName)
+ args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Group %s was created by the user %s.",
- args.projectName, args.userName)
+ args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was updated by the user %s.", args.projectName, args.userName)
+ data := fmt.Sprintf("The CLA Group %s was updated by the user %s.", args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Group %s was deleted by the user %s.",
- args.projectName, args.userName)
+ args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *GerritProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Gerrit repositories were deleted due to CLA Group/Project %s deletion.",
- ed.DeletedCount, args.projectName)
+ ed.DeletedCount, args.ProjectName)
return data, false
}
// GetEventSummaryString . . .
func (ed *GerritAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was added by: %s.", ed.GerritRepositoryName, args.userName)
+ data := fmt.Sprintf("The Gerrit repository %s was added by: %s.", ed.GerritRepositoryName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was deleted by %s.", ed.GerritRepositoryName, args.userName)
+ data := fmt.Sprintf("The Gerrit repository %s was deleted by %s.", ed.GerritRepositoryName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub repositories were deleted due to CLA Group/project %s deletion.",
- ed.DeletedCount, args.projectName)
+ ed.DeletedCount, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d signatures were invalidated (approved set to false) due to CLA Group/Project %s deletion.",
- ed.InvalidatedCount, args.projectName)
+ ed.InvalidatedCount, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ContributorNotifyCompanyAdminData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s notified the company admin %s by the email address %s for the company %s.",
- args.userName, ed.AdminName, ed.AdminEmail, args.companyName)
+ args.UserName, ed.AdminName, ed.AdminEmail, args.CompanyName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ContributorNotifyCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s notified the CLA Designee %s by email %s for the project: %s and the company %s.",
- args.userName, ed.DesigneeName, ed.DesigneeEmail,
- args.projectName, args.companyName)
+ args.UserName, ed.DesigneeName, ed.DesigneeEmail,
+ args.ProjectName, args.CompanyName)
return data, true
}
@@ -1050,31 +1108,31 @@ func (ed *ContributorNotifyCLADesignee) GetEventSummaryString(args *LogEventArgs
func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was assigned as CLA Manager Designee for the project: %s for the company %s by the user %s.",
ed.DesigneeName,
- args.projectName, args.companyName, args.userName)
+ args.ProjectName, args.CompanyName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was converted to a contact for the project %s.",
- args.LfUsername, args.projectName)
+ args.LfUsername, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s for project: %s.", args.LfUsername, ed.Role, args.projectName)
+ data := fmt.Sprintf("The user %s was added to the role %s for project: %s.", args.LfUsername, ed.Role, args.ProjectName)
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added with to the role %s by user %s.", ed.UserName, ed.Role, args.userName)
+ data := fmt.Sprintf("The user %s was added with to the role %s by user %s.", ed.UserName, ed.Role, args.UserName)
return data, false
}
// GetEventSummaryString . . .
func (ed *ClaManagerRoleDeletedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was removed from the role %s by user %s.", ed.UserName, ed.Role, args.userName)
+ data := fmt.Sprintf("The user %s was removed from the role %s by user %s.", ed.UserName, ed.Role, args.UserName)
return data, false
}
diff --git a/cla-backend-go/events/event_types.go b/cla-backend-go/events/event_types.go
index ab32212d6..d9753be8d 100644
--- a/cla-backend-go/events/event_types.go
+++ b/cla-backend-go/events/event_types.go
@@ -69,9 +69,11 @@ const (
ClaManagerRoleCreated = "cla_manager.added"
ClaManagerRoleDeleted = "cla_manager.deleted"
- CLAGroupCreated = "cla_group.created"
- CLAGroupUpdated = "cla_group.updated"
- CLAGroupDeleted = "cla_group.deleted"
+ CLAGroupCreated = "cla_group.created"
+ CLAGroupUpdated = "cla_group.updated"
+ CLAGroupDeleted = "cla_group.deleted"
+ CLAGroupEnrolledProject = "cla_group.enrolled.project"
+ CLAGroupUnenrolledProject = "cla_group.unenrolled.project"
InvalidatedSignature = "signature.invalidated"
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 89853230f..880b03cd3 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -104,6 +104,7 @@ func (repo *repository) CreateEvent(event *models.Event) error {
Item: map[string]*dynamodb.AttributeValue{},
TableName: aws.String(fmt.Sprintf("cla-%s-events", repo.stage)),
}
+
eventDateAndContainsPII := fmt.Sprintf("%s#%t", toDateFormat(currentTime), event.ContainsPII)
addAttribute(input.Item, "event_id", eventID.String())
addAttribute(input.Item, "event_type", event.EventType)
@@ -111,23 +112,33 @@ func (repo *repository) CreateEvent(event *models.Event) error {
addAttribute(input.Item, "event_user_name", event.UserName)
addAttribute(input.Item, "event_lf_username", event.LfUsername)
addAttribute(input.Item, "event_user_name_lower", strings.ToLower(event.UserName))
+
addAttribute(input.Item, "event_time", currentTimeString)
+ addAttribute(input.Item, "event_date", toDateFormat(currentTime))
addAttribute(input.Item, "event_data", event.EventData)
addAttribute(input.Item, "event_summary", event.EventSummary)
+
addAttribute(input.Item, "event_company_id", event.EventCompanyID)
+ addAttribute(input.Item, "event_company_sfid", event.EventCompanySFID)
addAttribute(input.Item, "event_company_name", event.EventCompanyName)
addAttribute(input.Item, "event_company_name_lower", strings.ToLower(event.EventCompanyName))
+
+ addAttribute(input.Item, "event_cla_group_id", event.EventCLAGroupID)
+ addAttribute(input.Item, "event_cla_group_name", event.EventCLAGroupName)
+ addAttribute(input.Item, "event_cla_group_name_lower", strings.ToLower(event.EventCLAGroupName))
+
addAttribute(input.Item, "event_project_id", event.EventProjectID)
+ addAttribute(input.Item, "event_project_external_id", event.EventProjectExternalID)
addAttribute(input.Item, "event_project_name", event.EventProjectName)
addAttribute(input.Item, "event_project_name_lower", strings.ToLower(event.EventProjectName))
- addAttribute(input.Item, "event_date", toDateFormat(currentTime))
- addAttribute(input.Item, "event_project_external_id", event.EventProjectExternalID)
+
addAttribute(input.Item, "event_date_and_contains_pii", eventDateAndContainsPII)
+
input.Item["contains_pii"] = &dynamodb.AttributeValue{BOOL: &event.ContainsPII}
input.Item["event_time_epoch"] = &dynamodb.AttributeValue{N: aws.String(strconv.FormatInt(currentTime.Unix(), 10))}
if event.EventCompanyID != "" && event.EventProjectExternalID != "" {
- companyIDexternalProjectID := fmt.Sprintf("%s#%s", event.EventCompanyID, event.EventProjectExternalID)
- addAttribute(input.Item, "company_id_external_project_id", companyIDexternalProjectID)
+ companyIDExternalProjectID := fmt.Sprintf("%s#%s", event.EventCompanyID, event.EventProjectExternalID)
+ addAttribute(input.Item, "company_id_external_project_id", companyIDExternalProjectID)
}
_, err = repo.dynamoDBClient.PutItem(input)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 34d0439d7..317ea5bf4 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -8,6 +8,10 @@ import (
"errors"
"fmt"
+ project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+
+ "github.com/sirupsen/logrus"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
@@ -25,6 +29,7 @@ const (
// Service interface defines methods of event service
type Service interface {
LogEvent(args *LogEventArgs)
+ LogEventWithContext(ctx context.Context, args *LogEventArgs)
SearchEvents(params *eventOps.SearchEventsParams) (*models.EventList, error)
GetRecentEvents(paramPageSize *int64) (*models.EventList, error)
@@ -108,65 +113,128 @@ func (s *service) GetCompanyEvents(companyID, eventType string, nextKey *string,
// EventType, EventData are compulsory.
// One of LfUsername, UserID must be present
type LogEventArgs struct {
- EventType string
- ProjectID string
- ClaGroupModel *models.ClaGroup
- CompanyID string
- CompanyModel *models.Company
- LfUsername string
- UserID string
- UserModel *models.User
+ EventType string
+
ExternalProjectID string
- EventData EventData
- userName string
- projectName string
- companyName string
+ ProjectName string
+ ProjectSFID string
+
+ ProjectID string // Should just use CLA GroupID
+ CLAGroupID string
+ CLAGroupName string
+ ClaGroupModel *models.ClaGroup
+
+ CompanyModel *models.Company
+ CompanyID string
+ CompanyName string
+ CompanySFID string
+
+ LfUsername string
+ UserName string
+ UserID string
+ UserModel *models.User
+
+ EventData EventData
}
func (s *service) loadCompany(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "loadCompany",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
if args.CompanyModel != nil {
- args.companyName = args.CompanyModel.CompanyName
+ args.CompanyName = args.CompanyModel.CompanyName
args.CompanyID = args.CompanyModel.CompanyID
+ args.CompanySFID = args.CompanyModel.CompanyExternalID
return nil
- }
- if args.CompanyID != "" {
+ } else if args.CompanyID != "" {
companyModel, err := s.combinedRepo.GetCompany(ctx, args.CompanyID)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("failed to load company record ID: %s", args.CompanyID)
return err
}
args.CompanyModel = companyModel
- args.companyName = companyModel.CompanyName
+ args.CompanyName = companyModel.CompanyName
+ args.CompanySFID = companyModel.CompanyExternalID
}
+
return nil
}
-func (s *service) loadProject(ctx context.Context, args *LogEventArgs) error {
+func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "loadCLAGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
if args.ClaGroupModel != nil {
- args.ProjectID = args.ClaGroupModel.ProjectID
- args.projectName = args.ClaGroupModel.ProjectName
+ args.CLAGroupID = args.ClaGroupModel.ProjectID
+ args.ProjectName = args.ClaGroupModel.ProjectName
args.ExternalProjectID = args.ClaGroupModel.ProjectExternalID
return nil
}
- if args.ProjectID != "" {
- claGroupModel, err := s.combinedRepo.GetCLAGroupByID(ctx, args.ProjectID, DontLoadRepoDetails)
+
+ claGroupID := ""
+ if args.CLAGroupID != "" {
+ claGroupID = args.CLAGroupID
+ } else if args.ProjectID != "" && utils.IsUUIDv4(args.ProjectID) { // legacy parameter
+ claGroupID = args.ProjectID
+ }
+
+ if claGroupID != "" {
+ claGroupModel, err := s.combinedRepo.GetCLAGroupByID(ctx, claGroupID, DontLoadRepoDetails)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("failed to load CLA Group by ID: %s", claGroupID)
return err
}
args.ClaGroupModel = claGroupModel
- args.projectName = claGroupModel.ProjectName
+ args.ProjectName = claGroupModel.ProjectName
args.ExternalProjectID = claGroupModel.ProjectExternalID
+ return nil
}
+
return nil
}
-func (s *service) loadUser(args *LogEventArgs) error {
+func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "loadSFProject",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if utils.IsUUIDv4(args.ProjectID) { // internal CLA Group ID
+ return s.loadCLAGroup(ctx, args)
+ } else if utils.IsSalesForceID(args.ProjectID) { // external SF project ID
+ args.ProjectSFID = args.ProjectID
+ args.ExternalProjectID = args.ProjectID
+ // Check if project exists in platform project service
+ project, projectErr := project_service.GetClient().GetProject(args.ProjectSFID)
+ if projectErr != nil || project == nil {
+ log.WithFields(f).Warnf("failed to load salesforce project by ID: %s", args.ProjectSFID)
+ return nil
+ }
+ args.ProjectName = project.Name
+ return nil
+ }
+
+ return nil
+}
+
+func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "loadUser",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
if args.UserModel != nil {
- args.userName = args.UserModel.Username
+ args.UserName = args.UserModel.Username
args.UserID = args.UserModel.UserID
args.LfUsername = args.UserModel.LfUsername
return nil
}
if args.UserID == "" && args.LfUsername == "" {
+ log.WithFields(f).Warn("failed to load user for event log - user ID and username were not set")
return errors.New("require userID or LfUsername")
}
var userModel *models.User
@@ -174,77 +242,106 @@ func (s *service) loadUser(args *LogEventArgs) error {
if args.LfUsername != "" {
userModel, err = s.combinedRepo.GetUserByUserName(args.LfUsername, true)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("failed to load user by username: %s", args.LfUsername)
return err
}
}
if args.UserID != "" {
userModel, err = s.combinedRepo.GetUser(args.UserID)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("failed to load user by ID: %s", args.UserID)
return err
}
}
if userModel != nil {
args.UserModel = userModel
- args.userName = userModel.Username
+ args.UserName = userModel.Username
args.UserID = userModel.UserID
args.LfUsername = userModel.LfUsername
+ } else {
+ log.WithFields(f).Warnf("unable to set user information for event log entry")
}
return nil
}
+// loadDetails fetches and sets additional information into the data model required to fill out the event log entry
func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
err := s.loadCompany(ctx, args)
if err != nil {
return err
}
- err = s.loadProject(ctx, args)
+
+ err = s.loadCLAGroup(ctx, args)
if err != nil {
return err
}
- err = s.loadUser(args)
+
+ err = s.loadSFProject(ctx, args)
if err != nil {
return err
}
+
+ err = s.loadUser(ctx, args)
+ if err != nil {
+ return err
+ }
+
return nil
}
-// LogEvent logs the event in database
-func (s *service) LogEvent(args *LogEventArgs) {
- ctx := utils.NewContext()
+// LogEventWithContext logs the event in database
+func (s *service) LogEventWithContext(ctx context.Context, args *LogEventArgs) {
+ f := logrus.Fields{
+ "functionName": "events.service.LogEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
defer func() {
if r := recover(); r != nil {
- log.Error("panic occurred in CreateEvent", fmt.Errorf("%v", r))
+ log.WithFields(f).Error("panic occurred in CreateEvent", fmt.Errorf("%v", r))
}
}()
+
if args == nil || args.EventType == "" || args.EventData == nil || (args.UserID == "" && args.LfUsername == "") {
- log.Warnf("invalid arguments to LogEvent, missing one or more required values. args %#v", args)
+ log.WithFields(f).Warnf("invalid arguments to LogEvent, missing one or more required values. args %#v", args)
return
}
+
err := s.loadDetails(ctx, args)
if err != nil {
- log.Error("unable to load details for event", err)
+ log.WithFields(f).Error("unable to load details for event", err)
return
}
+
eventData, containsPII := args.EventData.GetEventDetailsString(args)
eventSummary, _ := args.EventData.GetEventSummaryString(args)
event := models.Event{
ContainsPII: containsPII,
+ EventCLAGroupID: args.CLAGroupID,
+ EventCLAGroupName: args.ProjectName,
EventCompanyID: args.CompanyID,
- EventCompanyName: args.companyName,
+ EventCompanySFID: args.CompanySFID,
+ EventCompanyName: args.CompanyName,
EventData: eventData,
- EventSummary: eventSummary,
EventProjectExternalID: args.ExternalProjectID,
EventProjectID: args.ProjectID,
- EventProjectName: args.projectName,
+ EventProjectName: args.ProjectName,
+ EventProjectSFID: args.ProjectSFID,
+ EventSummary: eventSummary,
EventType: args.EventType,
- UserID: args.UserID,
- UserName: args.userName,
LfUsername: args.LfUsername,
+ UserID: args.UserID,
+ UserName: args.UserName,
}
err = s.repo.CreateEvent(&event)
if err != nil {
- log.Error(fmt.Sprintf("unable to create event for args %#v", args), err)
+ log.WithFields(f).Error(fmt.Sprintf("unable to create event for args %#v", args), err)
}
}
+
+// LogEvent logs the event in database
+func (s *service) LogEvent(args *LogEventArgs) {
+ s.LogEventWithContext(utils.NewContext(), args)
+}
diff --git a/cla-backend-go/gerrits/handlers.go b/cla-backend-go/gerrits/handlers.go
index d82043782..f34cfa2a4 100644
--- a/cla-backend-go/gerrits/handlers.go
+++ b/cla-backend-go/gerrits/handlers.go
@@ -52,7 +52,7 @@ func Configure(api *operations.ClaAPI, service Service, projectService ProjectSe
return gerrits.NewDeleteGerritBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
// record the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GerritRepositoryDeleted,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
@@ -92,7 +92,7 @@ func Configure(api *operations.ClaAPI, service Service, projectService ProjectSe
return gerrits.NewAddGerritBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
// record the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GerritRepositoryAdded,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
diff --git a/cla-backend-go/github_organizations/handlers.go b/cla-backend-go/github_organizations/handlers.go
index 1e0e87bcb..7d494cdf0 100644
--- a/cla-backend-go/github_organizations/handlers.go
+++ b/cla-backend-go/github_organizations/handlers.go
@@ -89,7 +89,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if params.Body.BranchProtectionEnabled != nil {
branchProtectionEnabled = *params.Body.BranchProtectionEnabled
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
UserID: claUser.UserID,
EventType: events.GitHubOrganizationAdded,
ExternalProjectID: params.ProjectSFID,
@@ -125,7 +125,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
return github_organizations.NewDeleteProjectGithubOrganizationBadRequest().WithPayload(errorResponse(err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
UserID: claUser.UserID,
EventType: events.GitHubOrganizationDeleted,
ExternalProjectID: params.ProjectSFID,
@@ -163,7 +163,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(errorResponse(err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
UserID: claUser.UserID,
EventType: events.GitHubOrganizationUpdated,
ExternalProjectID: params.ProjectSFID,
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index 74b5321e7..d0973c947 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -76,8 +76,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
return project.NewCreateProjectBadRequest().WithPayload(errorResponse(err))
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CLAGroupCreated,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -200,8 +201,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
// Log gerrit event
if howMany > 0 {
log.WithFields(f).Debugf("Deleted %d gerrit groups", howMany)
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GerritRepositoryDeleted,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -221,8 +223,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
log.WithFields(f).Debugf("Deleted %d github repositories", howMany)
// Log github delete event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -241,8 +244,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
if howMany > 0 {
log.WithFields(f).Debugf("Invalidated %d signatures", howMany)
// Log invalidate signatures
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.InvalidatedSignature,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -259,8 +263,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
}
return project.NewDeleteProjectByIDBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CLAGroupDeleted,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -304,8 +309,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
}
// Log an event
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.CLAGroupUpdated,
+ ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
diff --git a/cla-backend-go/repositories/handlers.go b/cla-backend-go/repositories/handlers.go
index 0ccc68d13..6a327007d 100644
--- a/cla-backend-go/repositories/handlers.go
+++ b/cla-backend-go/repositories/handlers.go
@@ -52,7 +52,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if err != nil {
return github_repositories.NewAddProjectGithubRepositoryBadRequest().WithPayload(errorResponse(err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryAdded,
ProjectID: utils.StringValue(params.GithubRepositoryInput.RepositoryProjectID),
ExternalProjectID: params.ProjectSFID,
@@ -90,7 +90,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if err != nil {
return github_repositories.NewDeleteProjectGithubRepositoryBadRequest().WithPayload(errorResponse(err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
ExternalProjectID: params.ProjectSFID,
ProjectID: ghRepo.RepositoryProjectID,
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index e541edb55..50dfa8ebf 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -211,7 +211,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
projectID = signatureModel.ProjectID
companyID = signatureModel.SignatureReferenceID
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ApprovalListGitHubOrganizationAdded,
ProjectID: projectID,
CompanyID: companyID,
@@ -262,7 +262,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
companyID = signatureModel.SignatureReferenceID
}
- eventsService.LogEvent(&events.LogEventArgs{
+ eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ApprovalListGitHubOrganizationDeleted,
ProjectID: projectID,
CompanyID: companyID,
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index b418da046..ce6d2a9f1 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -430,7 +430,7 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
}
// Log Events
- s.createEventLogEntries(companyModel, claGroupModel, userModel, params)
+ s.createEventLogEntries(ctx, companyModel, claGroupModel, userModel, params)
// Send an email to the CLA Managers
for _, claManager := range claManagers {
@@ -638,12 +638,12 @@ func (s service) sendRequestAccessEmailToContributors(authUser *auth.User, compa
}
}
-func (s service) createEventLogEntries(companyModel *models.Company, claGroupModel *models.ClaGroup, userModel *models.User, approvalList *models.ApprovalList) {
+func (s service) createEventLogEntries(ctx context.Context, companyModel *models.Company, claGroupModel *models.ClaGroup, userModel *models.User, approvalList *models.ApprovalList) {
for _, value := range approvalList.AddEmailApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -661,9 +661,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.RemoveEmailApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -681,9 +681,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.AddDomainApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -701,9 +701,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.RemoveDomainApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -721,9 +721,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.AddGithubUsernameApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -741,9 +741,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.RemoveGithubUsernameApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -761,9 +761,9 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.AddGithubOrgApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
@@ -781,9 +781,10 @@ func (s service) createEventLogEntries(companyModel *models.Company, claGroupMod
}
for _, value := range approvalList.RemoveGithubOrgApprovalList {
// Send an event
- s.eventsService.LogEvent(&events.LogEventArgs{
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaApprovalListUpdated,
- ProjectID: claGroupModel.ProjectID,
+ CLAGroupID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
CompanyID: companyModel.CompanyID,
CompanyModel: companyModel,
diff --git a/cla-backend-go/swagger/common/event.yaml b/cla-backend-go/swagger/common/event.yaml
index cb8a03854..a39ab12a5 100644
--- a/cla-backend-go/swagger/common/event.yaml
+++ b/cla-backend-go/swagger/common/event.yaml
@@ -4,31 +4,32 @@
type: object
properties:
EventID:
- type: string
description: unique id of the event
+ $ref: './common/properties/internal-id.yaml'
EventType:
type: string
description: type of the event
UserID:
- type: string
description: id of the user who created this event
+ $ref: './common/properties/internal-id.yaml'
UserName:
- type: string
- description: name of the user
+ $ref: './common/properties/user-name.yaml'
LfUsername:
type: string
description: name of the user
+ EventCLAGroupID:
+ description: the CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
+ EventCLAGroupName:
+ description: the CLA Group name
+ $ref: './common/properties/cla-group-name.yaml'
EventProjectID:
type: string
description: id of the SFID project
EventProjectName:
- type: string
- description: name of the project
+ $ref: './common/properties/project-name.yaml'
EventCompanyName:
- type: string
- description: name of the company
- pattern: '^([\w\p{L}][\w\s\p{L}()\[\]+\-/%!@#$]*){2,255}$'
- example: "Linux Foundation"
+ $ref: './common/properties/company-name.yaml'
EventCompanyID:
type: string
description: id of the organization/company
diff --git a/cla-backend-go/tests/utils_test.go b/cla-backend-go/tests/utils_test.go
index 7b62da95f..53abcb9ac 100644
--- a/cla-backend-go/tests/utils_test.go
+++ b/cla-backend-go/tests/utils_test.go
@@ -390,3 +390,27 @@ func TestIsUUIDv4LikeSFID(t *testing.T) {
sfid := "0014100000TdznWAAR"
assert.False(t, utils.IsUUIDv4(sfid), fmt.Sprintf("%s is not v4 UUID", sfid))
}
+
+func TestIsSalesForceID(t *testing.T) {
+ trueTestData := []string{
+ "00117000015vpjX",
+ "00117000015vpjXAAQ",
+ }
+ falseTestData := []string{
+ "",
+ "00117",
+ "-00117",
+ "00117000015vpj-",
+ "0011700001-vpjXAAQ",
+ "0011700001?vpjXAAQ",
+ "0011700001&vpjXAAQ",
+ "0011700001_vpjXAAQ",
+ }
+
+ for i := range trueTestData {
+ assert.True(t, utils.IsSalesForceID(trueTestData[i]))
+ }
+ for i := range falseTestData {
+ assert.False(t, utils.IsSalesForceID(falseTestData[i]))
+ }
+}
diff --git a/cla-backend-go/utils/utils.go b/cla-backend-go/utils/utils.go
index eb7a98be0..41dec29cf 100644
--- a/cla-backend-go/utils/utils.go
+++ b/cla-backend-go/utils/utils.go
@@ -297,3 +297,9 @@ func IsUUIDv4(id string) bool {
return value.Version() == uuid.V4
}
+
+// IsSalesForceID returns true if the specified ID is a SalesForce formatted ID, otherwise returns false
+func IsSalesForceID(id string) bool {
+ regExp := regexp.MustCompile("^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$")
+ return regExp.MatchString(strings.TrimSpace(id))
+}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 7de4461b8..eb89bfb2f 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -40,7 +40,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupCreateClaGroupHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupCreateClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupName": utils.StringValue(params.ClaGroupInput.ClaGroupName),
"foundationSFID": utils.StringValue(params.ClaGroupInput.FoundationSfid),
@@ -60,7 +60,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
return cla_group.NewCreateClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- claGroup, err := service.CreateCLAGroup(ctx, params.ClaGroupInput, utils.StringValue(params.XUSERNAME))
+ claGroup, err := service.CreateCLAGroup(ctx, authUser, params.ClaGroupInput, utils.StringValue(params.XUSERNAME))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to create the CLA Group")
return cla_group.NewCreateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
@@ -86,7 +86,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupUpdateClaGroupHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupUpdateClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -135,7 +135,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("unable to update the CLA Group Name or Description - values are the same for CLA Group ID: %s", params.ClaGroupID)))
}
- claGroup, err := service.UpdateCLAGroup(ctx, claGroupModel, params.Body, utils.StringValue(params.XUSERNAME))
+ claGroup, err := service.UpdateCLAGroup(ctx, authUser, claGroupModel, params.Body, utils.StringValue(params.XUSERNAME))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to update the CLA Group Name and/or Description - update failed")
return cla_group.NewUpdateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(
@@ -161,7 +161,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupDeleteClaGroupHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupDeleteClaGroupHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -204,12 +204,8 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
err = service.DeleteCLAGroup(ctx, claGroupModel, authUser)
if err != nil {
log.WithFields(f).Warn(err)
- return cla_group.NewDeleteClaGroupInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error - error deleting CLA Group %s, error: %+v",
- params.ClaGroupID, err),
- XRequestID: reqID,
- })
+ return cla_group.NewDeleteClaGroupInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseInternalServerErrorWithError(reqID, fmt.Sprintf("error deleting CLA Group by ID: %s", params.ClaGroupID), err))
}
eventsService.LogEvent(&events.LogEventArgs{
@@ -227,7 +223,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupEnrollProjectsHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupEnrollProjectsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ClaGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -235,28 +231,18 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
"projectSFIDList": strings.Join(params.ProjectSFIDList, ","),
}
- cg, err := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
- if err != nil {
- if err, ok := err.(*utils.CLAGroupNotFound); ok {
- return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("EasyCLA - 404 Not Found - %s", err.Error()),
- XRequestID: reqID,
- })
+ cg, getCLAGroupErr := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ if getCLAGroupErr != nil {
+ if _, ok := getCLAGroupErr.(*utils.CLAGroupNotFound); ok {
+ return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("problem loading CLA Group by ID: %s", params.ClaGroupID), getCLAGroupErr))
}
- if err == v1Project.ErrProjectDoesNotExist {
- return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("EasyCLA - 404 Not Found - cla_group %s not found",
- params.ClaGroupID),
- XRequestID: reqID,
- })
+ if getCLAGroupErr == v1Project.ErrProjectDoesNotExist {
+ return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("problem loading CLA Group by ID: %s", params.ClaGroupID), getCLAGroupErr))
}
- return cla_group.NewEnrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error - error = %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewEnrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseInternalServerErrorWithError(reqID, fmt.Sprintf("problem loading CLA Group by ID: %s", params.ClaGroupID), getCLAGroupErr))
}
// Check permissions
@@ -275,12 +261,11 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
msg := fmt.Sprintf("Failed to get salesforce project: %s", projectSFID)
log.WithFields(f).Warn(msg)
if _, ok := projectErr.(*v2ProjectServiceClient.GetProjectNotFound); ok {
- return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("project not found with given ID. [%s]", projectSFID),
- })
+ return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("project not found with ID: [%s]", projectSFID), projectErr))
}
- return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, projectErr))
+ return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, msg, projectErr))
}
var parentProject *v2ProjectServiceModels.ProjectOutputDetailed
// Handle the ONAP edge case
@@ -289,10 +274,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
if parentProject == nil || projectErr != nil {
msg := fmt.Sprintf("Failed to get parent: %s", project.Parent)
log.WithFields(f).Warnf(msg)
- return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload((&models.ErrorResponse{
- Code: "400",
- Message: msg,
- }))
+ return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
}
if (project.Parent != "" && !utils.IsProjectCategory(project, parentProject)) || (utils.IsProjectHasRootParent(project) && project.ProjectType == utils.ProjectTypeProjectGroup) {
@@ -303,32 +285,22 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
- err = service.EnrollProjectsInClaGroup(ctx, params.ClaGroupID, cg.FoundationSFID, params.ProjectSFIDList)
- if err != nil {
- if strings.Contains(err.Error(), "bad request") {
- return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: fmt.Sprintf("EasyCLA - 400 Bad Request - %s", err.Error()),
- XRequestID: reqID,
- })
+ // Enroll the project(s) into the CLA Group
+ enrollCLAGroupErr := service.EnrollProjectsInClaGroup(ctx, &EnrollProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: params.ClaGroupID,
+ FoundationSFID: cg.FoundationSFID,
+ ProjectSFIDList: params.ProjectSFIDList,
+ })
+ if enrollCLAGroupErr != nil {
+ if strings.Contains(enrollCLAGroupErr.Error(), "bad request") {
+ return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", enrollCLAGroupErr))
}
- return cla_group.NewEnrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error - error = %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewEnrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseInternalServerErrorWithError(reqID, "unable to enroll projects in CLA Group", enrollCLAGroupErr))
}
- eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ClaGroupModel: cg,
- LfUsername: authUser.UserName,
- EventData: &events.CLAGroupUpdatedEventData{
- ClaGroupName: cg.ProjectName,
- ClaGroupDescription: cg.ProjectDescription,
- },
- })
-
return cla_group.NewEnrollProjectsOK().WithXRequestID(reqID)
})
@@ -337,7 +309,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupUnenrollProjectsHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupUnenrollProjectsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ClaGroupID": params.ClaGroupID,
"authUsername": params.XUSERNAME,
@@ -348,24 +320,15 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
cg, err := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
if err, ok := err.(*utils.CLAGroupNotFound); ok {
- return cla_group.NewUnenrollProjectsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("EasyCLA - 404 Not Found - %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewUnenrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("unable to locate CLA Group by ID: %s", params.ClaGroupID), err))
}
if err == v1Project.ErrProjectDoesNotExist {
- return cla_group.NewUnenrollProjectsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("EasyCLA - 404 Not Found - cla_group %s not found", params.ClaGroupID),
- XRequestID: reqID,
- })
+ return cla_group.NewUnenrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("unable to locate CLA Group by ID: %s", params.ClaGroupID), err))
}
- return cla_group.NewUnenrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error - error = %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewUnenrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("problem locating CLA Group by ID: %s", params.ClaGroupID), err))
}
// Check permissions
@@ -375,20 +338,19 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
return cla_group.NewUnenrollProjectsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- err = service.UnenrollProjectsInClaGroup(ctx, params.ClaGroupID, cg.FoundationSFID, params.ProjectSFIDList)
+ err = service.UnenrollProjectsInClaGroup(ctx, &UnenrollProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: params.ClaGroupID,
+ FoundationSFID: cg.FoundationSFID,
+ ProjectSFIDList: params.ProjectSFIDList,
+ })
if err != nil {
if strings.Contains(err.Error(), "bad request") {
- return cla_group.NewUnenrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: fmt.Sprintf("EasyCLA - 400 Bad Request - %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewUnenrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to unenroll projects for CLA Group ID: %s", params.ClaGroupID), err))
}
- return cla_group.NewUnenrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: fmt.Sprintf("EasyCLA - 500 Internal server error - error = %s", err.Error()),
- XRequestID: reqID,
- })
+ return cla_group.NewUnenrollProjectsInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseInternalServerErrorWithError(reqID, fmt.Sprintf("unable to unenroll projects for CLA Group ID: %s", params.ClaGroupID), err))
}
eventsService.LogEvent(&events.LogEventArgs{
@@ -409,7 +371,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "cla_groups.handlers.ClaGroupListClaGroupsUnderFoundationHandler",
+ "functionName": "v2.cla_groups.handlers.ClaGroupListClaGroupsUnderFoundationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"authUsername": params.XUSERNAME,
@@ -423,12 +385,11 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
msg := fmt.Sprintf("Failed to get salesforce project: %s", params.ProjectSFID)
log.WithFields(f).Warn(msg)
if _, ok := projectErr.(*v2ProjectServiceClient.GetProjectNotFound); ok {
- return cla_group.NewListClaGroupsUnderFoundationNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("project not found with given ID. [%s]", params.ProjectSFID),
- })
+ return cla_group.NewListClaGroupsUnderFoundationNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("project not found with ID: %s", params.ProjectSFID), projectErr))
}
- return cla_group.NewListClaGroupsUnderFoundationBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, projectErr))
+ return cla_group.NewListClaGroupsUnderFoundationBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, msg, projectErr))
}
log.WithFields(f).Debug("found project - evaluating parent...")
@@ -518,7 +479,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// isUserHaveAccessToCLAProject is a helper function to determine if the user has access to the specified project
func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, parentProjectSFID string, projectSFIDs []string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
f := logrus.Fields{
- "functionName": "cla_groups.handlers.isUserHaveAccessToCLAProject",
+ "functionName": "v2.cla_groups.handlers.isUserHaveAccessToCLAProject",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFIDs": strings.Join(projectSFIDs, ","),
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 7122b6f34..7a0dd68f9 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -10,6 +10,9 @@ import (
"strings"
"sync"
+ "github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -220,14 +223,6 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
}
- // Comment out the below as we want to support stand-alone projects
- /*
- if len(foundationProjectDetails.Projects) == 0 {
- log.WithFields(f).Warn("validation failure - project does not have any subprojects")
- return fmt.Errorf("bad request: invalid input to enroll projects. project does not have any subprojects")
- }
- */
-
// Check to see if all the provided enrolled projects are part of this foundation
foundationProjectIDList := utils.NewStringSet()
for _, pr := range foundationProjectDetails.Projects {
@@ -389,35 +384,46 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return nil
}
-func (s *service) AssociateCLAGroupWithProjects(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+func (s *service) AssociateCLAGroupWithProjects(ctx context.Context, request *AssociateCLAGroupWithProjectsModel) error {
f := logrus.Fields{
"functionName": "AssociateCLAGroupWithProjects",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "foundationSFID": foundationSFID,
- "projectSFIDList": strings.Join(projectSFIDList, ","),
+ "authUserName": request.AuthUser.UserName,
+ "authUserEmail": request.AuthUser.Email,
+ "claGroupID": request.CLAGroupID,
+ "foundationSFID": request.FoundationSFID,
+ "projectSFIDList": strings.Join(request.ProjectSFIDList, ","),
}
// Associate the CLA Group with the project list in a go routine
var errorList []error
var wg sync.WaitGroup
- wg.Add(len(projectSFIDList))
+ wg.Add(len(request.ProjectSFIDList))
- for _, projectSFID := range projectSFIDList {
+ for _, projectSFID := range request.ProjectSFIDList {
// Invoke the go routine - any errors will be handled below
go func(sfid string) {
defer wg.Done()
log.WithFields(f).Debugf("associating cla_group with project: %s", sfid)
- err := s.projectsClaGroupsRepo.AssociateClaGroupWithProject(claGroupID, sfid, foundationSFID)
+ err := s.projectsClaGroupsRepo.AssociateClaGroupWithProject(request.CLAGroupID, sfid, request.FoundationSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("associating cla_group with project: %s failed", sfid)
log.WithFields(f).Debug("deleting stale entries from cla_group project association")
- deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(claGroupID, projectSFIDList, false)
+ deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(request.CLAGroupID, request.ProjectSFIDList, false)
if deleteErr != nil {
log.WithFields(f).WithError(deleteErr).Warn("deleting stale entries from cla_group project association failed")
}
// Add the error to the error list
errorList = append(errorList, err)
}
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.CLAGroupEnrolledProject,
+ ProjectSFID: sfid,
+ CLAGroupID: request.CLAGroupID,
+ LfUsername: request.AuthUser.UserName,
+ EventData: &events.CLAGroupEnrolledProjectData{},
+ })
}(projectSFID)
}
@@ -427,36 +433,52 @@ func (s *service) AssociateCLAGroupWithProjects(ctx context.Context, claGroupID
// If any errors while associating - return the first one
if len(errorList) > 0 {
- log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when associating %d projects with the CLA Group", len(errorList), len(projectSFIDList))
+ log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when associating %d projects with the CLA Group", len(errorList), len(request.ProjectSFIDList))
return errorList[0]
}
return nil
}
-func (s *service) UnassociateCLAGroupWithProjects(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+func (s *service) UnassociateCLAGroupWithProjects(ctx context.Context, request *UnassociateCLAGroupWithProjectsModel) error {
f := logrus.Fields{
"functionName": "UnassociateCLAGroupWithProjects",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "claGroupID": claGroupID,
- "foundationSFID": foundationSFID,
- "projectSFIDList": strings.Join(projectSFIDList, ","),
+ "authUserName": request.AuthUser.UserName,
+ "authUserEmail": request.AuthUser.Email,
+ "claGroupID": request.CLAGroupID,
+ "foundationSFID": request.FoundationSFID,
+ "projectSFIDList": strings.Join(request.ProjectSFIDList, ","),
}
- deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(claGroupID, projectSFIDList, false)
+ deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(request.CLAGroupID, request.ProjectSFIDList, false)
if deleteErr != nil {
log.WithFields(f).Warnf("problem disassociating projects with CLA Group, error: %+v", deleteErr)
return deleteErr
}
+ // If this is slow, we may want to run these in a go routine
+ for _, projectSFID := range request.ProjectSFIDList {
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.CLAGroupUnenrolledProject,
+ ProjectSFID: projectSFID,
+ CLAGroupID: request.CLAGroupID,
+ LfUsername: request.AuthUser.UserName,
+ EventData: &events.CLAGroupUnenrolledProjectData{},
+ })
+ }
+
return nil
}
// EnableCLAService enable CLA service attribute in the project service for the specified project list
-func (s *service) EnableCLAService(ctx context.Context, projectSFIDList []string) error {
+func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, projectSFIDList []string) error {
f := logrus.Fields{
"functionName": "EnableCLAService",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
"projectSFIDList": strings.Join(projectSFIDList, ","),
}
@@ -478,6 +500,13 @@ func (s *service) EnableCLAService(ctx context.Context, projectSFIDList []string
errorList = append(errorList, enableProjectErr)
} else {
log.WithFields(f).Debugf("enabled CLA service for project: %s", sfid)
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ProjectServiceCLAEnabled,
+ ProjectID: sfid,
+ LfUsername: authUser.UserName,
+ EventData: &events.ProjectServiceCLAEnabledData{},
+ })
}
}(psc, projectSFID)
}
@@ -494,10 +523,12 @@ func (s *service) EnableCLAService(ctx context.Context, projectSFIDList []string
}
// DisableCLAService disable CLA service attribute in the project service for the specified project list
-func (s *service) DisableCLAService(ctx context.Context, projectSFIDList []string) error {
+func (s *service) DisableCLAService(ctx context.Context, authUser *auth.User, projectSFIDList []string) error {
f := logrus.Fields{
"functionName": "DisableCLAService",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
"projectSFIDList": strings.Join(projectSFIDList, ","),
}
@@ -519,6 +550,13 @@ func (s *service) DisableCLAService(ctx context.Context, projectSFIDList []strin
errorList = append(errorList, disableProjectErr)
} else {
log.WithFields(f).Debugf("disabled CLA service for project: %s", sfid)
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ProjectServiceCLADisabled,
+ ProjectID: sfid,
+ LfUsername: authUser.UserName,
+ EventData: &events.ProjectServiceCLADisabledData{},
+ })
}
}(psc, projectSFID)
}
diff --git a/cla-backend-go/v2/cla_groups/models.go b/cla-backend-go/v2/cla_groups/models.go
new file mode 100644
index 000000000..1473ea896
--- /dev/null
+++ b/cla-backend-go/v2/cla_groups/models.go
@@ -0,0 +1,40 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package cla_groups
+
+import (
+ "github.com/LF-Engineering/lfx-kit/auth"
+)
+
+// EnrollProjectsModel model to encapsulate the enroll projects request
+type EnrollProjectsModel struct {
+ AuthUser *auth.User
+ CLAGroupID string
+ FoundationSFID string
+ ProjectSFIDList []string
+}
+
+// UnenrollProjectsModel model to encapsulate the unenroll projects request
+type UnenrollProjectsModel struct {
+ AuthUser *auth.User
+ CLAGroupID string
+ FoundationSFID string
+ ProjectSFIDList []string
+}
+
+// AssociateCLAGroupWithProjectsModel to encapsulate the associate request
+type AssociateCLAGroupWithProjectsModel struct {
+ AuthUser *auth.User
+ CLAGroupID string
+ FoundationSFID string
+ ProjectSFIDList []string
+}
+
+// UnassociateCLAGroupWithProjectsModel to encapsulate the unassociate request
+type UnassociateCLAGroupWithProjectsModel struct {
+ AuthUser *auth.User
+ CLAGroupID string
+ FoundationSFID string
+ ProjectSFIDList []string
+}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index c21486fd6..cd056d9f6 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -54,17 +54,17 @@ type service struct {
// Service interface
type Service interface {
- CreateCLAGroup(ctx context.Context, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
- UpdateCLAGroup(ctx context.Context, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
+ CreateCLAGroup(ctx context.Context, authUser *auth.User, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
+ UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
ListClaGroupsForFoundationOrProject(ctx context.Context, foundationSFID string) (*models.ClaGroupListSummary, error)
ListAllFoundationClaGroups(ctx context.Context, foundationID *string) (*models.FoundationMappingList, error)
DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.ClaGroup, authUser *auth.User) error
- EnrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error
- UnenrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error
- AssociateCLAGroupWithProjects(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error
- UnassociateCLAGroupWithProjects(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error
- EnableCLAService(ctx context.Context, projectSFIDList []string) error
- DisableCLAService(ctx context.Context, projectSFIDList []string) error
+ EnrollProjectsInClaGroup(ctx context.Context, request *EnrollProjectsModel) error
+ UnenrollProjectsInClaGroup(ctx context.Context, request *UnenrollProjectsModel) error
+ AssociateCLAGroupWithProjects(ctx context.Context, request *AssociateCLAGroupWithProjectsModel) error
+ UnassociateCLAGroupWithProjects(ctx context.Context, request *UnassociateCLAGroupWithProjectsModel) error
+ EnableCLAService(ctx context.Context, authUser *auth.User, projectSFIDList []string) error
+ DisableCLAService(ctx context.Context, authUser *auth.User, projectSFIDList []string) error
ValidateCLAGroup(ctx context.Context, input *models.ClaGroupValidationRequest) (bool, []string)
}
@@ -83,7 +83,7 @@ func NewService(projectService v1Project.Service, templateService v1Template.Ser
}
}
-func (s *service) CreateCLAGroup(ctx context.Context, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
+func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
// Validate the input
log.WithField("input", input).Debugf("validating create cla group input")
if input.IclaEnabled == nil ||
@@ -97,6 +97,8 @@ func (s *service) CreateCLAGroup(ctx context.Context, input *models.CreateClaGro
f := logrus.Fields{
"functionName": "CreateCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
"ClaGroupName": aws.StringValue(input.ClaGroupName),
"ClaGroupDescription": input.ClaGroupDescription,
"FoundationSfid": aws.StringValue(input.FoundationSfid),
@@ -175,15 +177,20 @@ func (s *service) CreateCLAGroup(ctx context.Context, input *models.CreateClaGro
log.WithFields(f).Debug("cla_group_template attached", pdfUrls)
// Associate the specified projects with our new CLA Group
- err = s.EnrollProjectsInClaGroup(ctx, claGroup.ProjectID, *input.FoundationSfid, input.ProjectSfidList)
- if err != nil {
+ enrollErr := s.EnrollProjectsInClaGroup(ctx, &EnrollProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: claGroup.ProjectID,
+ FoundationSFID: *input.FoundationSfid,
+ ProjectSFIDList: input.ProjectSfidList,
+ })
+ if enrollErr != nil {
// Oops, roll back logic
- log.WithFields(f).Debug("enroll projects in CLA Group failure - deleting created cla group")
+ log.WithFields(f).WithError(enrollErr).Debug("enroll projects in CLA Group failure - deleting created cla group")
deleteErr := s.v1ProjectService.DeleteCLAGroup(ctx, claGroup.ProjectID)
if deleteErr != nil {
log.WithFields(f).Error("deleting created cla group failed - manual cleanup required.", deleteErr)
}
- return nil, err
+ return nil, enrollErr
}
// Build the response model
@@ -222,11 +229,13 @@ func (s *service) CreateCLAGroup(ctx context.Context, input *models.CreateClaGro
}, nil
}
-func (s *service) UpdateCLAGroup(ctx context.Context, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
+func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
// Validate the input
f := logrus.Fields{
"functionName": "UpdateCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
"claGroupID": claGroupModel.ProjectID,
"ClaGroupName": input.ClaGroupName,
"ClaGroupDescription": input.ClaGroupDescription,
@@ -689,6 +698,7 @@ func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.Cl
s.eventsService.LogEvent(&events.LogEventArgs{
EventType: events.GerritRepositoryDeleted,
ClaGroupModel: claGroup,
+ CLAGroupID: claGroup.ProjectID,
LfUsername: authUser.UserName,
EventData: &events.GerritProjectDeletedEventData{
DeletedCount: numDeleted,
@@ -855,8 +865,13 @@ func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.Cl
}
}
- // Associate the specified projects with our new CLA Group
- err := s.UnenrollProjectsInClaGroup(ctx, claGroupModel.ProjectID, foundationSFID, projectIDList.List())
+ // Unenroll the specified projects with the CLA Group
+ err := s.UnenrollProjectsInClaGroup(ctx, &UnenrollProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: claGroupModel.ProjectID,
+ FoundationSFID: foundationSFID,
+ ProjectSFIDList: projectIDList.List(),
+ })
if err != nil {
log.WithFields(f).WithError(err).Warn("unenrolling projects in CLA Group failed - manual cleanup required.")
}
@@ -873,17 +888,19 @@ func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.Cl
}
// EnrollProjectsInClaGroup enrolls the specified project list in the CLA Group
-func (s *service) EnrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollProjectsModel) error {
f := logrus.Fields{
"functionName": "EnrollProjectsInClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "claGroupID": claGroupID,
- "foundationSFID": foundationSFID,
- "projectSFIDList": strings.Join(projectSFIDList, ","),
+ "authUserName": request.AuthUser.UserName,
+ "authUserEmail": request.AuthUser.Email,
+ "claGroupID": request.CLAGroupID,
+ "foundationSFID": request.FoundationSFID,
+ "projectSFIDList": strings.Join(request.ProjectSFIDList, ","),
}
log.WithFields(f).Debug("validating enroll project input")
- err := s.validateEnrollProjectsInput(ctx, foundationSFID, projectSFIDList)
+ err := s.validateEnrollProjectsInput(ctx, request.FoundationSFID, request.ProjectSFIDList)
if err != nil {
log.WithFields(f).Warnf("validating enroll project input failed. error = %s", err)
return err
@@ -895,49 +912,57 @@ func (s *service) EnrollProjectsInClaGroup(ctx context.Context, claGroupID strin
wg.Add(2)
// Separate go routine for enrolling projects
- go func(c context.Context, claGrID string, fSFID string, projSFIDList []string) {
+ go func(c context.Context, authUser *auth.User, claGroupID string, foundationSFID string, projSFIDList []string) {
defer wg.Done()
log.WithFields(f).Debug("enrolling projects in CLA Group")
- enrollErr := s.AssociateCLAGroupWithProjects(c, claGrID, fSFID, projSFIDList)
+ enrollErr := s.AssociateCLAGroupWithProjects(c, &AssociateCLAGroupWithProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: claGroupID,
+ FoundationSFID: foundationSFID,
+ ProjectSFIDList: projSFIDList,
+ })
if enrollErr != nil {
log.WithFields(f).WithError(enrollErr).Warn("enrolling projects in CLA Group failed")
errorList = append(errorList, enrollErr)
}
-
- }(ctx, claGroupID, foundationSFID, projectSFIDList)
+ }(ctx, request.AuthUser, request.CLAGroupID, request.FoundationSFID, request.ProjectSFIDList)
// Separate go routine for enabling the CLA Service in the project service
go func(c context.Context, projSFIDList []string) {
defer wg.Done()
log.WithFields(f).Debug("enabling CLA service in platform project service")
- errEnableCLA := s.EnableCLAService(c, projSFIDList)
+ // Note: log entry will be created by enable CLA Service call
+ errEnableCLA := s.EnableCLAService(c, request.AuthUser, projSFIDList)
if errEnableCLA != nil {
log.WithFields(f).WithError(errEnableCLA).Warn("enabling CLA service in platform project service failed")
errorList = append(errorList, errEnableCLA)
}
- }(ctx, projectSFIDList)
+ }(ctx, request.ProjectSFIDList)
// Wait until all go routines are done
wg.Wait()
if len(errorList) > 0 {
- log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when enrolling and enabling CLA service for %d projects", len(errorList), len(projectSFIDList))
+ log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when enrolling and enabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList))
return errorList[0]
}
return nil
}
-func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+//func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *UnenrollProjectsModel) error {
f := logrus.Fields{
"functionName": "UnenrollProjectsInClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "claGroupID": claGroupID,
- "foundationSFID": foundationSFID,
- "projectSFIDList": strings.Join(projectSFIDList, ","),
+ "authUserName": request.AuthUser.UserName,
+ "authUserEmail": request.AuthUser.Email,
+ "claGroupID": request.CLAGroupID,
+ "foundationSFID": request.FoundationSFID,
+ "projectSFIDList": strings.Join(request.ProjectSFIDList, ","),
}
log.WithFields(f).Debug("validating unenroll project input")
- err := s.validateUnenrollProjectsInput(ctx, foundationSFID, projectSFIDList)
+ err := s.validateUnenrollProjectsInput(ctx, request.FoundationSFID, request.ProjectSFIDList)
if err != nil {
log.WithFields(f).Warnf("validating unenroll project input failed. error = %s", err)
return err
@@ -949,32 +974,37 @@ func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, claGroupID str
wg.Add(2)
// Separate go routine for unenrolling projects
- go func(c context.Context, claGrID string, fSFID string, projSFIDList []string) {
+ go func(c context.Context, authUser *auth.User, claGroupID string, foundationSFID string, projSFIDList []string) {
defer wg.Done()
log.WithFields(f).Debug("unenrolling projects in CLA Group")
- unenrollErr := s.UnassociateCLAGroupWithProjects(c, claGrID, fSFID, projSFIDList)
+ unenrollErr := s.UnassociateCLAGroupWithProjects(c, &UnassociateCLAGroupWithProjectsModel{
+ AuthUser: authUser,
+ CLAGroupID: claGroupID,
+ FoundationSFID: foundationSFID,
+ ProjectSFIDList: projSFIDList,
+ })
if unenrollErr != nil {
log.WithFields(f).WithError(unenrollErr).Warn("unenrolling projects in CLA Group failed")
errorList = append(errorList, unenrollErr)
}
-
- }(ctx, claGroupID, foundationSFID, projectSFIDList)
+ }(ctx, request.AuthUser, request.CLAGroupID, request.FoundationSFID, request.ProjectSFIDList)
// Separate go routine for disabling the CLA Service in the project service
go func(c context.Context, projSFIDList []string) {
defer wg.Done()
log.WithFields(f).Debug("disabling CLA service in platform project service")
- errDisableCLA := s.DisableCLAService(c, projSFIDList)
+ // Note: log entry will be created by disable CLA Service call
+ errDisableCLA := s.DisableCLAService(c, request.AuthUser, projSFIDList)
if errDisableCLA != nil {
log.WithFields(f).WithError(errDisableCLA).Warn("disabling CLA service in platform project service failed")
errorList = append(errorList, errDisableCLA)
}
- }(ctx, projectSFIDList)
+ }(ctx, request.ProjectSFIDList)
// Wait until all go routines are done
wg.Wait()
if len(errorList) > 0 {
- log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when unenrolling and disabling CLA service for %d projects", len(errorList), len(projectSFIDList))
+ log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when unenrolling and disabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList))
return errorList[0]
}
diff --git a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
index 0bde09763..e571f17a1 100644
--- a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
+++ b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
@@ -8,7 +8,6 @@ import (
"fmt"
"strings"
"sync"
- "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
@@ -34,145 +33,145 @@ type ProjectClaGroup struct {
RepositoriesCount int64 `json:"repositories_count"`
}
-// ProjectServiceEnableCLAServiceHandler handles enabling the CLA Service attribute from the project service
-func (s *service) ProjectServiceEnableCLAServiceHandler(event events.DynamoDBEventRecord) error {
- ctx := utils.NewContext()
- f := logrus.Fields{
- "functionName": "dynamo_events.projects_cla_groups.ProjectServiceEnableCLAServiceHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "eventID": event.EventID,
- "eventName": event.EventName,
- "eventSource": event.EventSource,
- }
-
- log.WithFields(f).Debug("processing request")
- var newProject ProjectClaGroup
- err := unmarshalStreamImage(event.Change.NewImage, &newProject)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("project decoding add event")
- return err
- }
-
- f["projectSFID"] = newProject.ProjectSFID
- f["claGroupID"] = newProject.ClaGroupID
- f["foundationSFID"] = newProject.FoundationSFID
-
- psc := v2ProjectService.GetClient()
- log.WithFields(f).Debug("looking up project by SFID...")
- projectDetails, prjerr := psc.GetProject(newProject.ProjectSFID)
- if prjerr != nil {
- log.WithError(err).Warnf("unable to get project details from SFID: %s", newProject.ProjectSFID)
- }
- projectName := newProject.ProjectSFID
- if projectDetails != nil {
- projectName = projectDetails.Name
- f["projectName"] = projectName
- }
-
- start, _ := utils.CurrentTime()
- log.WithFields(f).Debugf("enabling CLA service for project %s with ID: %s", projectName, newProject.ProjectSFID)
- err = psc.EnableCLA(newProject.ProjectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("enabling CLA service failed")
- return err
- }
- finish, _ := utils.CurrentTime()
- log.WithFields(f).Debugf("enabled CLA service for project %s with ID: %s", projectName, newProject.ProjectSFID)
- log.WithFields(f).Debugf("enabling CLA service completed - took: %s", finish.Sub(start).String())
-
- // Log the event
- eventErr := s.eventsRepo.CreateEvent(&models.Event{
- ContainsPII: false,
- EventData: fmt.Sprintf("enabled CLA service for project: %s with ID: %s", projectName, newProject.ProjectSFID),
- EventFoundationSFID: newProject.FoundationSFID,
- EventProjectExternalID: newProject.ProjectSFID,
- EventProjectID: newProject.ClaGroupID,
- EventProjectName: projectName,
- EventProjectSFID: newProject.ProjectSFID,
- EventProjectSFName: projectName,
- EventSummary: fmt.Sprintf("enabled CLA service for project: %s", projectName),
- EventType: claEvents.ProjectServiceCLAEnabled,
- LfUsername: "easycla system",
- UserID: "easycla system",
- UserName: "easycla system",
- })
- if eventErr != nil {
- log.WithFields(f).WithError(eventErr).Warn("problem logging event for enabling CLA service")
- // Ok - don't fail for now
- }
-
- return nil
-}
-
-// ProjectServiceDisableCLAServiceHandler handles disabling/removing the CLA Service attribute from the project service
-func (s *service) ProjectServiceDisableCLAServiceHandler(event events.DynamoDBEventRecord) error {
- ctx := utils.NewContext()
- f := logrus.Fields{
- "functionName": "dynamo_events.projects_cla_groups.ProjectServiceDisableCLAServiceHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "eventID": event.EventID,
- "eventName": event.EventName,
- "eventSource": event.EventSource,
- }
-
- log.WithFields(f).Debug("processing request")
- var oldProject ProjectClaGroup
- err := unmarshalStreamImage(event.Change.OldImage, &oldProject)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("problem unmarshalling stream image")
- return err
- }
-
- // Add more fields for the logger
- f["ProjectSFID"] = oldProject.ProjectSFID
- f["ClaGroupID"] = oldProject.ClaGroupID
- f["FoundationSFID"] = oldProject.FoundationSFID
-
- psc := v2ProjectService.GetClient()
- log.WithFields(f).Debug("looking up project by SFID...")
- projectDetails, prjerr := psc.GetProject(oldProject.ProjectSFID)
- if prjerr != nil {
- log.WithError(err).Warnf("unable to get project details from SFID: %s", oldProject.ProjectSFID)
- }
- projectName := oldProject.ProjectSFID
- if projectDetails != nil {
- projectName = projectDetails.Name
- f["projectName"] = projectName
- }
-
- // Gathering metrics - grab the time before the API call
- before, _ := utils.CurrentTime()
- log.WithFields(f).Debugf("disabling CLA service for project %s with ID: %s", projectName, oldProject.ProjectSFID)
- err = psc.DisableCLA(oldProject.ProjectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("disabling CLA service failed")
- return err
- }
- log.WithFields(f).Debugf("disabled CLA service for project %s with ID: %s", projectName, oldProject.ProjectSFID)
- log.WithFields(f).Debugf("disabling CLA service completed - took %s", time.Since(before).String())
-
- // Log the event
- eventErr := s.eventsRepo.CreateEvent(&models.Event{
- ContainsPII: false,
- EventData: fmt.Sprintf("disabled CLA service for project: %s with ID: %s", projectName, oldProject.ProjectSFID),
- EventFoundationSFID: oldProject.FoundationSFID,
- EventProjectExternalID: oldProject.ProjectSFID,
- EventProjectID: oldProject.ClaGroupID,
- EventProjectName: projectName,
- EventProjectSFID: oldProject.ProjectSFID,
- EventSummary: fmt.Sprintf("disabled CLA service for project: %s", projectName),
- EventType: claEvents.ProjectServiceCLADisabled,
- LfUsername: "easycla system",
- UserID: "easycla system",
- UserName: "easycla system",
- })
- if eventErr != nil {
- log.WithFields(f).WithError(eventErr).Warn("problem logging event for disabling CLA service")
- // Ok - don't fail for now
- }
-
- return nil
-}
+//// ProjectServiceEnableCLAServiceHandler handles enabling the CLA Service attribute from the project service
+//func (s *service) ProjectServiceEnableCLAServiceHandler(event events.DynamoDBEventRecord) error {
+// ctx := utils.NewContext()
+// f := logrus.Fields{
+// "functionName": "dynamo_events.projects_cla_groups.ProjectServiceEnableCLAServiceHandler",
+// utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+// "eventID": event.EventID,
+// "eventName": event.EventName,
+// "eventSource": event.EventSource,
+// }
+//
+// log.WithFields(f).Debug("processing request")
+// var newProject ProjectClaGroup
+// err := unmarshalStreamImage(event.Change.NewImage, &newProject)
+// if err != nil {
+// log.WithFields(f).WithError(err).Warn("project decoding add event")
+// return err
+// }
+//
+// f["projectSFID"] = newProject.ProjectSFID
+// f["claGroupID"] = newProject.ClaGroupID
+// f["foundationSFID"] = newProject.FoundationSFID
+//
+// psc := v2ProjectService.GetClient()
+// log.WithFields(f).Debug("looking up project by SFID...")
+// projectDetails, prjerr := psc.GetProject(newProject.ProjectSFID)
+// if prjerr != nil {
+// log.WithError(err).Warnf("unable to get project details from SFID: %s", newProject.ProjectSFID)
+// }
+// projectName := newProject.ProjectSFID
+// if projectDetails != nil {
+// projectName = projectDetails.Name
+// f["projectName"] = projectName
+// }
+//
+// start, _ := utils.CurrentTime()
+// log.WithFields(f).Debugf("enabling CLA service for project %s with ID: %s", projectName, newProject.ProjectSFID)
+// err = psc.EnableCLA(newProject.ProjectSFID)
+// if err != nil {
+// log.WithFields(f).WithError(err).Warn("enabling CLA service failed")
+// return err
+// }
+// finish, _ := utils.CurrentTime()
+// log.WithFields(f).Debugf("enabled CLA service for project %s with ID: %s", projectName, newProject.ProjectSFID)
+// log.WithFields(f).Debugf("enabling CLA service completed - took: %s", finish.Sub(start).String())
+//
+// // Log the event
+// eventErr := s.eventsRepo.CreateEvent(&models.Event{
+// ContainsPII: false,
+// EventData: fmt.Sprintf("enabled CLA service for project: %s with ID: %s", projectName, newProject.ProjectSFID),
+// EventFoundationSFID: newProject.FoundationSFID,
+// EventProjectExternalID: newProject.ProjectSFID,
+// EventProjectID: newProject.ClaGroupID,
+// EventProjectName: projectName,
+// EventProjectSFID: newProject.ProjectSFID,
+// EventProjectSFName: projectName,
+// EventSummary: fmt.Sprintf("enabled CLA service for project: %s", projectName),
+// EventType: claEvents.ProjectServiceCLAEnabled,
+// LfUsername: "easycla system",
+// UserID: "easycla system",
+// UserName: "easycla system",
+// })
+// if eventErr != nil {
+// log.WithFields(f).WithError(eventErr).Warn("problem logging event for enabling CLA service")
+// // Ok - don't fail for now
+// }
+//
+// return nil
+//}
+//
+//// ProjectServiceDisableCLAServiceHandler handles disabling/removing the CLA Service attribute from the project service
+//func (s *service) ProjectServiceDisableCLAServiceHandler(event events.DynamoDBEventRecord) error {
+// ctx := utils.NewContext()
+// f := logrus.Fields{
+// "functionName": "dynamo_events.projects_cla_groups.ProjectServiceDisableCLAServiceHandler",
+// utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+// "eventID": event.EventID,
+// "eventName": event.EventName,
+// "eventSource": event.EventSource,
+// }
+//
+// log.WithFields(f).Debug("processing request")
+// var oldProject ProjectClaGroup
+// err := unmarshalStreamImage(event.Change.OldImage, &oldProject)
+// if err != nil {
+// log.WithFields(f).WithError(err).Warn("problem unmarshalling stream image")
+// return err
+// }
+//
+// // Add more fields for the logger
+// f["ProjectSFID"] = oldProject.ProjectSFID
+// f["ClaGroupID"] = oldProject.ClaGroupID
+// f["FoundationSFID"] = oldProject.FoundationSFID
+//
+// psc := v2ProjectService.GetClient()
+// log.WithFields(f).Debug("looking up project by SFID...")
+// projectDetails, prjerr := psc.GetProject(oldProject.ProjectSFID)
+// if prjerr != nil {
+// log.WithError(err).Warnf("unable to get project details from SFID: %s", oldProject.ProjectSFID)
+// }
+// projectName := oldProject.ProjectSFID
+// if projectDetails != nil {
+// projectName = projectDetails.Name
+// f["projectName"] = projectName
+// }
+//
+// // Gathering metrics - grab the time before the API call
+// before, _ := utils.CurrentTime()
+// log.WithFields(f).Debugf("disabling CLA service for project %s with ID: %s", projectName, oldProject.ProjectSFID)
+// err = psc.DisableCLA(oldProject.ProjectSFID)
+// if err != nil {
+// log.WithFields(f).WithError(err).Warn("disabling CLA service failed")
+// return err
+// }
+// log.WithFields(f).Debugf("disabled CLA service for project %s with ID: %s", projectName, oldProject.ProjectSFID)
+// log.WithFields(f).Debugf("disabling CLA service completed - took %s", time.Since(before).String())
+//
+// // Log the event
+// eventErr := s.eventsRepo.CreateEvent(&models.Event{
+// ContainsPII: false,
+// EventData: fmt.Sprintf("disabled CLA service for project: %s with ID: %s", projectName, oldProject.ProjectSFID),
+// EventFoundationSFID: oldProject.FoundationSFID,
+// EventProjectExternalID: oldProject.ProjectSFID,
+// EventProjectID: oldProject.ClaGroupID,
+// EventProjectName: projectName,
+// EventProjectSFID: oldProject.ProjectSFID,
+// EventSummary: fmt.Sprintf("disabled CLA service for project: %s", projectName),
+// EventType: claEvents.ProjectServiceCLADisabled,
+// LfUsername: "easycla system",
+// UserID: "easycla system",
+// UserName: "easycla system",
+// })
+// if eventErr != nil {
+// log.WithFields(f).WithError(eventErr).Warn("problem logging event for disabling CLA service")
+// // Ok - don't fail for now
+// }
+//
+// return nil
+//}
func (s *service) ProjectUnenrolledDisableRepositoryHandler(event events.DynamoDBEventRecord) error {
ctx := utils.NewContext()
diff --git a/cla-backend-go/v2/dynamo_events/service.go b/cla-backend-go/v2/dynamo_events/service.go
index 378a4edc9..7fcef6ef3 100644
--- a/cla-backend-go/v2/dynamo_events/service.go
+++ b/cla-backend-go/v2/dynamo_events/service.go
@@ -122,8 +122,9 @@ func NewService(stage string,
s.registerCallback(eventsTable, Insert, s.EventAddedEvent)
// Enable or Disable the CLA Service Enabled/Disabled flag/attribute in the platform Project Service
- s.registerCallback(projectsCLAGroupsTable, Insert, s.ProjectServiceEnableCLAServiceHandler)
- s.registerCallback(projectsCLAGroupsTable, Remove, s.ProjectServiceDisableCLAServiceHandler)
+ // These are called by the API via the service layer - includes the user who did it
+ //s.registerCallback(projectsCLAGroupsTable, Insert, s.ProjectServiceEnableCLAServiceHandler)
+ //s.registerCallback(projectsCLAGroupsTable, Remove, s.ProjectServiceDisableCLAServiceHandler)
s.registerCallback(projectsCLAGroupsTable, Remove, s.ProjectUnenrolledDisableRepositoryHandler)
// Add or Remove any CLA Permissions for the specified project
From 5b9633adb6f3a5966a090341ac35971ffb4cf9ee Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 1 Mar 2021 23:39:45 -0800
Subject: [PATCH 0126/1276] Search CLA Group Events by CLA Group ID (#2726)
- Added new key/index for searching events by CLA Group ID
- Updated query logic
- Updated python data models
- Cleaned up event message english
- Updated serverless config
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 6 +-
cla-backend-go/events/repository.go | 5 +-
cla-backend-go/repositories/handlers.go | 5 +-
cla-backend-go/serverless.yml | 1 +
cla-backend-go/v2/cla_groups/service.go | 14 +--
cla-backend-go/v2/events/handlers.go | 2 +
cla-backend/cla/models/dynamo_models.py | 137 +++++++++++++++++-------
cla-backend/serverless.yml | 1 +
8 files changed, 121 insertions(+), 50 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 0e48bbb00..28d74b035 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -775,19 +775,19 @@ func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArg
// GetEventSummaryString . . .
func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was added to Project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was added to the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was deleted from Project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was deleted from the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Repository: %s was updated for the project project: %s by: %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was updated for the project project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
return data, true
}
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 880b03cd3..f99780f1f 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -44,6 +44,7 @@ const (
CompanyIDEventTypeIndex = "company-id-event-type-index"
EventFoundationSFIDEpochIndex = "event-foundation-sfid-event-time-epoch-index"
EventProjectIDEpochIndex = "event-project-id-event-time-epoch-index"
+ EventCLAGroupIDEpochIndex = "event-cla-group-id-event-time-epoch-index"
)
// constants
@@ -516,8 +517,8 @@ func (repo *repository) GetFoundationEvents(foundationSFID string, nextKey *stri
// GetClaGroupEvents returns the list of cla-group events
func (repo *repository) GetClaGroupEvents(claGroupID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
- keyCondition := expression.Key("event_project_id").Equal(expression.Value(claGroupID))
- return repo.queryEventsTable(EventProjectIDEpochIndex, keyCondition, nil, nextKey, paramPageSize, all, searchTerm)
+ keyCondition := expression.Key("event_cla_group_id").Equal(expression.Value(claGroupID))
+ return repo.queryEventsTable(EventCLAGroupIDEpochIndex, keyCondition, nil, nextKey, paramPageSize, all, searchTerm)
}
// toString encodes the map as a string
diff --git a/cla-backend-go/repositories/handlers.go b/cla-backend-go/repositories/handlers.go
index 6a327007d..8bd5a6a67 100644
--- a/cla-backend-go/repositories/handlers.go
+++ b/cla-backend-go/repositories/handlers.go
@@ -54,7 +54,8 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryAdded,
- ProjectID: utils.StringValue(params.GithubRepositoryInput.RepositoryProjectID),
+ CLAGroupID: utils.StringValue(params.GithubRepositoryInput.RepositoryProjectID),
+ ProjectID: params.ProjectSFID,
ExternalProjectID: params.ProjectSFID,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
@@ -92,8 +93,8 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
+ ProjectID: params.ProjectSFID,
ExternalProjectID: params.ProjectSFID,
- ProjectID: ghRepo.RepositoryProjectID,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
EventData: &events.RepositoryDisabledEventData{
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index 3188aaac3..aac6235f3 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -181,6 +181,7 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/user-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-id-external-project-id-event-epoch-time-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-project-id-event-time-epoch-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-cla-group-id-event-time-epoch-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-date-and-contains-pii-event-time-epoch-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-sfid-foundation-sfid-event-time-epoch-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-sfid-project-id-event-time-epoch-index"
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index cd056d9f6..3499a5d9d 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -95,7 +95,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
}
f := logrus.Fields{
- "functionName": "CreateCLAGroup",
+ "functionName": "v2.cla_groups.service.CreateCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
@@ -232,7 +232,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
// Validate the input
f := logrus.Fields{
- "functionName": "UpdateCLAGroup",
+ "functionName": "v2.cla_groups.service.UpdateCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
@@ -352,7 +352,7 @@ func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGr
// ListClaGroupsForFoundationOrProject returns the CLA Group list for the specified foundation ID
func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, projectOrFoundationSFID string) (*models.ClaGroupListSummary, error) { // nolint
f := logrus.Fields{
- "functionName": "ListClaGroupsForFoundationOrProject",
+ "functionName": "v2.cla_groups.service.ListClaGroupsForFoundationOrProject",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectOrFoundationSFID": projectOrFoundationSFID,
}
@@ -611,7 +611,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
func (s *service) ListAllFoundationClaGroups(ctx context.Context, foundationID *string) (*models.FoundationMappingList, error) {
f := logrus.Fields{
- "functionName": "ListAllFoundationClaGroups",
+ "functionName": "v2.cla_groups.service.ListAllFoundationClaGroups",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"foundationID": foundationID,
}
@@ -632,7 +632,7 @@ func (s *service) ListAllFoundationClaGroups(ctx context.Context, foundationID *
// DeleteCLAGroup handles deleting and invalidating the CLA group, removing permissions, cleaning up pending requests, etc.
func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.ClaGroup, authUser *auth.User) error {
f := logrus.Fields{
- "functionName": "DeleteCLAGroup",
+ "functionName": "v2.cla_groups.service.DeleteCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupModel.ProjectID,
"claGroupExternalID": claGroupModel.ProjectExternalID,
@@ -890,7 +890,7 @@ func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.Cl
// EnrollProjectsInClaGroup enrolls the specified project list in the CLA Group
func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollProjectsModel) error {
f := logrus.Fields{
- "functionName": "EnrollProjectsInClaGroup",
+ "functionName": "v2.cla_groups.service.EnrollProjectsInClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": request.AuthUser.UserName,
"authUserEmail": request.AuthUser.Email,
@@ -952,7 +952,7 @@ func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollP
//func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *UnenrollProjectsModel) error {
f := logrus.Fields{
- "functionName": "UnenrollProjectsInClaGroup",
+ "functionName": "v2.cla_groups.service.UnenrollProjectsInClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": request.AuthUser.UserName,
"authUserEmail": request.AuthUser.Email,
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index 365bad29f..5d45ad2bb 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -224,6 +224,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
// Lookup the CLA Group associated with this Project SFID...
+ log.WithFields(f).Debugf("loading CLA Group for projectSFID: %s", params.ProjectSFID)
pm, err := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
if err != nil {
msg := fmt.Sprintf("problem loading CLA Group from Project SFID:: %s", params.ProjectSFID)
@@ -241,6 +242,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
}
// Lookup any events for this CLA Group....
+ log.WithFields(f).Debugf("loading CLA Group %s events using ID: %s", pm.ClaGroupName, pm.ClaGroupID)
result, err := service.GetClaGroupEvents(pm.ClaGroupID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents), params.SearchTerm)
if err != nil {
msg := fmt.Sprintf("problem loading events for CLA Group: %s with ID: %s error: %v", pm.ClaGroupName, pm.ClaGroupID, err.Error())
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 123ec1b79..920308633 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -4142,20 +4142,31 @@ class Meta:
event_id = UnicodeAttribute(hash_key=True)
event_user_id = UnicodeAttribute(null=True)
event_type = UnicodeAttribute(null=True)
+
+ event_cla_group_id = UnicodeAttribute(null=True)
+ event_cla_group_name = UnicodeAttribute(null=True)
+ event_cla_group_name_lower = UnicodeAttribute(null=True)
+
event_project_id = UnicodeAttribute(null=True)
+ event_project_name = UnicodeAttribute(null=True)
+ event_project_name_lower = UnicodeAttribute(null=True)
+ event_project_external_id = UnicodeAttribute(null=True)
+
event_company_id = UnicodeAttribute(null=True)
+ event_company_sfid = UnicodeAttribute(null=True)
event_company_name = UnicodeAttribute(null=True)
event_company_name_lower = UnicodeAttribute(null=True)
- event_project_name = UnicodeAttribute(null=True)
- event_project_name_lower = UnicodeAttribute(null=True)
+
event_user_name = UnicodeAttribute(null=True)
event_user_name_lower = UnicodeAttribute(null=True)
+
event_time = UTCDateTimeAttribute(default=datetime.datetime.utcnow())
event_time_epoch = NumberAttribute(default=int(time.time()))
+ event_date = UnicodeAttribute(null=True)
+
event_data = UnicodeAttribute(null=True)
event_summary = UnicodeAttribute(null=True)
- event_date = UnicodeAttribute(null=True)
- event_project_external_id = UnicodeAttribute(null=True)
+
event_date_and_contains_pii = UnicodeAttribute(null=True)
company_id_external_project_id = UnicodeAttribute(null=True)
contains_pii = BooleanAttribute(null=True)
@@ -4173,8 +4184,11 @@ def __init__(
event_id=None,
event_type=None,
user_id=None,
+ event_cla_group_id=None,
+ event_cla_group_name=None,
event_project_id=None,
event_company_id=None,
+ event_company_sfid=None,
event_data=None,
event_summary=None,
event_company_name=None,
@@ -4187,38 +4201,57 @@ def __init__(
self.model = EventModel()
self.model.event_id = event_id
self.model.event_type = event_type
+
self.model.event_user_id = user_id
- self.model.event_project_id = event_project_id
- self.model.event_company_id = event_company_id
- self.model.event_data = event_data
- self.model.event_summary = event_summary
- self.model.event_company_name = event_company_name
- self.model.contains_pii = contains_pii
- if self.model.event_company_name:
- self.model.event_company_name_lower = self.model.event_company_name.lower()
self.model.event_user_name = event_user_name
if self.model.event_user_name:
self.model.event_user_name_lower = self.model.event_user_name.lower()
+
+ self.model.event_cla_group_id = event_cla_group_id
+ self.model.event_cla_group_name = event_cla_group_name
+ if self.model.event_cla_group_name:
+ self.model.event_cla_group_name_lower = self.model.event_cla_group_name.lower()
+
+ self.model.event_project_id = event_project_id
self.model.event_project_name = event_project_name
if self.model.event_project_name:
self.model.event_project_name_lower = self.model.event_project_name.lower()
+ self.model.event_company_id = event_company_id
+ self.model.event_company_sfid = event_company_sfid
+ self.model.event_company_name = event_company_name
+ if self.model.event_company_name:
+ self.model.event_company_name_lower = self.model.event_company_name.lower()
+
+ self.model.event_data = event_data
+ self.model.event_summary = event_summary
+ self.model.contains_pii = contains_pii
+
def __str__(self):
return (
f"id:{self.model.event_id}, "
f"event type:{self.model.event_type}, "
+
f"event_user id:{self.model.event_user_id}, "
+ f"event user name: {self.model.event_user_name},"
+
+ f"event cla group id:{self.model.event_cla_group_id}, "
+ f"event cla group name:{self.model.event_cla_group_name}, "
+
f"event project id:{self.model.event_project_id}, "
+ f"event project name: {self.model.event_project_name}, "
+ f"event project external id: {self.model.event_project_external_id},"
+
f"event company id: {self.model.event_company_id}, "
+ f"event company sfid: {self.model.event_company_sfid}, "
+ f"event company name: {self.model.event_company_name}, "
+
f"event time: {self.model.event_time}, "
f"event time epoch: {self.model.event_time_epoch}, "
+ f"event date: {self.model.event_date},"
+
f"event data: {self.model.event_data}, "
f"event summary: {self.model.event_summary}, "
- f"event company name: {self.model.event_company_name}, "
- f"event project name: {self.model.event_project_name}, "
- f"event user name: {self.model.event_user_name},"
- f"event date: {self.model.event_date},"
- f"event project external id: {self.model.event_project_external_id},"
f"contains pii: {self.model.contains_pii}"
)
@@ -4236,12 +4269,6 @@ def load(self, event_id):
raise cla.models.DoesNotExist("Event not found")
self.model = event
- def get_event_company_id(self):
- return self.model.event_company_id
-
- def get_event_company_name(self):
- return self.model.event_company_name
-
def get_event_user_id(self):
return self.model.event_user_id
@@ -4257,9 +4284,21 @@ def get_event_date(self):
def get_event_id(self):
return self.model.event_id
+ def get_event_cla_group_id(self):
+ return self.model.event_cla_group_id
+
+ def get_event_cla_group_name(self):
+ return self.model.event_cla_group_name
+
+ def get_event_cla_group_name_lower(self):
+ return self.model.event_cla_group_name_lower
+
def get_event_project_id(self):
return self.model.event_project_id
+ def get_event_project_external_id(self):
+ return self.model.event_project_external_id
+
def get_event_project_name(self):
return self.model.event_project_name
@@ -4275,6 +4314,15 @@ def get_event_time(self):
def get_event_time_epoch(self):
return self.model.event_time_epoch
+ def get_event_company_id(self):
+ return self.model.event_company_id
+
+ def get_event_company_sfid(self):
+ return self.model.event_company_sfid
+
+ def get_event_company_name(self):
+ return self.model.event_company_name
+
def get_event_company_name_lower(self):
return self.model.event_company_name_lower
@@ -4284,9 +4332,6 @@ def get_event_user_name(self):
def get_event_user_name_lower(self):
return self.model.event_user_name_lower
- def get_event_project_external_id(self):
- return self.model.event_project_external_id
-
def get_company_id_external_project_id(self):
return self.model.company_id_external_project_id
@@ -4302,13 +4347,14 @@ def all(self, ids=None):
ret.append(ev)
return ret
- def set_event_company_id(self, company_id):
- self.model.event_company_id = company_id
-
- def set_event_company_name(self, company_name):
- self.model.event_company_name = company_name
- if company_name:
- self.model.event_company_name_lower = company_name.lower()
+ def all_limit(self, limit: Optional[int] = None, last_evaluated_key: Optional[str] = None):
+ result_iterator = self.model.scan(limit=limit, last_evaluated_key=last_evaluated_key)
+ ret = []
+ for signature in result_iterator:
+ evt = Event()
+ evt.model = signature
+ ret.append(evt)
+ return ret, result_iterator.last_evaluated_key, result_iterator.total_count
def set_event_data(self, event_data):
self.model.event_data = event_data
@@ -4319,12 +4365,34 @@ def set_event_summary(self, event_summary):
def set_event_id(self, event_id):
self.model.event_id = event_id
+ def set_event_company_id(self, company_id):
+ self.model.event_company_id = company_id
+
+ def set_event_company_sfid(self, company_sfid):
+ self.model.event_company_sfid = company_sfid
+
+ def set_event_company_name(self, company_name):
+ self.model.event_company_name = company_name
+ if company_name:
+ self.model.event_company_name_lower = company_name.lower()
+
def set_event_user_id(self, user_id):
self.model.event_user_id = user_id
+ def set_event_cla_group_id(self, event_cla_group_id):
+ self.model.event_cla_group_id = event_cla_group_id
+
+ def set_event_cla_group_name(self, event_cla_group_name):
+ self.model.event_cla_group_name = event_cla_group_name
+ if event_cla_group_name:
+ self.model.event_cla_group_name_lower = event_cla_group_name.lower()
+
def set_event_project_id(self, event_project_id):
self.model.event_project_id = event_project_id
+ def set_event_project_external_id(self, event_project_external_id):
+ self.model.event_project_external_id = event_project_external_id
+
def set_event_project_name(self, event_project_name):
self.model.event_project_name = event_project_name
if event_project_name:
@@ -4337,9 +4405,6 @@ def set_event_user_name(self, event_user_name):
self.model.event_user_name = event_user_name
self.model.event_user_name_lower = event_user_name.lower()
- def set_event_project_external_id(self, event_project_external_id):
- self.model.event_project_external_id = event_project_external_id
-
def set_event_date_and_contains_pii(self, contains_pii=False):
dateDDMMYYYY = datetime.date.today().strftime("%d-%m-%Y")
self.model.contains_pii = contains_pii
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index 9044a474e..b216dcdc0 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -271,6 +271,7 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/user-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-id-external-project-id-event-epoch-time-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-project-id-event-time-epoch-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-cla-group-id-event-time-epoch-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/event-date-and-contains-pii-event-time-epoch-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-sfid-foundation-sfid-event-time-epoch-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-events/index/company-sfid-project-id-event-time-epoch-index"
From d7722c3c536a9b19c11ae48736a5b9bc42094041 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Tue, 2 Mar 2021 09:40:14 +0200
Subject: [PATCH 0127/1276] change email content for cla signatory (#2719)
Signed-off-by: makkalot
---
cla-backend/cla/models/docusign_models.py | 63 +++++++++++++++----
.../cla/tests/unit/test_docusign_models.py | 24 ++++++-
2 files changed, 73 insertions(+), 14 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 8c9aca376..17a57d8bf 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -14,10 +14,11 @@
import urllib.request
import uuid
import xml.etree.ElementTree as ET
-from typing import Dict, Any, Optional
+from typing import Dict, Any, Optional, List
from urllib.parse import urlparse
import pydocusign # type: ignore
+from attr import dataclass
from pydocusign.exceptions import DocuSignException # type: ignore
import cla
@@ -25,11 +26,12 @@
from cla.models import signing_service_interface, DoesNotExist
from cla.models.dynamo_models import Signature, User, \
Project, Company, Gerrit, \
- Document, Event
+ Document, Event, ProjectCLAGroupModel
from cla.models.event_types import EventType
from cla.models.s3_storage import S3Storage
from cla.user_service import UserService
-from cla.utils import get_email_help_content, append_email_help_sign_off_content, get_corporate_url
+from cla.utils import get_email_help_content, append_email_help_sign_off_content, get_corporate_url, \
+ get_project_cla_group_instance
api_base_url = os.environ.get('CLA_API_BASE', '')
root_url = os.environ.get('DOCUSIGN_ROOT_URL', '')
@@ -44,8 +46,6 @@
lf_group = LFGroup(lf_group_client_url, lf_group_client_id, lf_group_client_secret, lf_group_refresh_token)
-
-
class ProjectDoesNotExist(Exception):
pass
@@ -1270,18 +1270,27 @@ def populate_sign_url(self, signature, callback_url=None,
# Not assigning a clientUserId sends an email.
project_name = project.get_project_name()
+ cla_group_name = project_name
company_name = company.get_company_name()
+ project_cla_group = get_project_cla_group_instance()
+ project_cla_groups = project_cla_group.get_by_cla_group_id(project.get_project_id())
+ project_names = [p.get_project_name() for p in project_cla_groups]
+ if not project_names:
+ project_names = [project_name]
cla.log.debug(f'{fn} - {sig_type} - sending document as email with '
f'name: {signatory_name}, email: {signatory_email} '
f'project name: {project_name}, company: {company_name}')
- email_subject = f'EasyCLA: CLA Signature Request for {project_name}'
- email_body = f'
Hello {signatory_name},
'
- email_body += f'
This is a notification email from EasyCLA regarding the project {project_name}. {cla_manager_name} has designated you as being an authorized signatory for {company_name}. In order for employees of your company to contribute to the open source project {project_name}, they must do so under a Contributor License Agreement signed by someone with authority to sign on behalf of your company.
'
- email_body += f'
After you sign, {cla_manager_name} (as the initial CLA Manager for your company) will be able to maintain the list of specific employees authorized to contribute to the project under this signed CLA.
'
- email_body += f'
If you are authorized to sign on your company’s behalf, and if you approve {cla_manager_name} as your initial CLA Manager for {project_name}, please review the document and sign the CLA.If you have questions, or if you are not an authorized signatory of this company, please contact the requester at {cla_manager_email}.
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
The CLA has now been signed. You can download the signed CLA as a PDF
@@ -2216,3 +2225,31 @@ def document_signed_email_content(icla: bool, project: Project, signature: Signa
'''
body = append_email_help_sign_off_content(body, project.get_version())
return subject, body
+
+
+@dataclass
+class ClaSignatoryEmailParams:
+ cla_group_name: str
+ signatory_name: str
+ cla_manager_name: str
+ cla_manager_email: str
+ company_name: str
+ project_version: str
+ project_names: List[str]
+
+
+def cla_signatory_email_content(params: ClaSignatoryEmailParams) -> (str, str):
+ """
+ cla_signatory_email_content prepares the content for cla signatory
+ :param params: ClaSignatoryEmailParams
+ :return:
+ """
+ project_names_list = ", ".join(params.project_names)
+
+ email_subject = f'EasyCLA: CLA Signature Request for {params.cla_group_name}'
+ email_body = f'
Hello {params.signatory_name},
'
+ email_body += f'
This is a notification email from EasyCLA regarding the project(s) {project_names_list} associated with the CLA Group {params.cla_group_name}. {params.cla_manager_name} has designated you as being an authorized signatory for the organization {params.company_name}. In order for employees of your company to contribute to any of the above project(s), they must do so under a Contributor License Agreement signed by someone with authority n behalf of your company.
'
+ email_body += f'
After you sign, {params.cla_manager_name} (as the initial CLA Manager for your company) will be able to maintain the list of specific employees authorized to contribute to the project(s) under this signed CLA.
'
+ email_body += f'
If you are authorized to sign on your company’s behalf, and if you approve {params.cla_manager_name} as your initial CLA Manager, please review the document and sign the CLA. If you have questions, or if you are not an authorized signatory of this company, please contact the requester at {params.cla_manager_email}.
" in email_body
+ assert "EasyCLA regarding the project(s) project1, project2 associated" in email_body
+ assert "with the CLA Group cla_group_name_value" in email_body
+ assert "john has designated you as being an authorized signatory" in email_body
+ assert "signatory for the organization IBM" in email_body
+ assert "
After you sign, john (as the initial CLA Manager for your company)" in email_body
+ assert "and if you approve john as your initial CLA Manager" in email_body
+ assert "contact the requester at john@example.com" in email_body
From af79079e3ca05937c8111293913adef4997aafb2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 2 Mar 2021 14:12:22 -0800
Subject: [PATCH 0128/1276] Updated Event Log Message Formatting (#2727)
- Updated event log summary messages using a common format
- Use LogEventWithContext when possible, added more logging
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 654 +++++++++++++++---
cla-backend-go/v2/cla_manager/service.go | 8 +-
cla-backend-go/v2/company/service.go | 16 +-
cla-backend-go/v2/gerrits/handlers.go | 4 +-
cla-backend-go/v2/github_activity/service.go | 62 +-
.../v2/github_organizations/handlers.go | 6 +-
cla-backend-go/v2/repositories/handlers.go | 6 +-
7 files changed, 624 insertions(+), 132 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 28d74b035..bf0f750e8 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -753,128 +753,292 @@ func (ed *ClaManagerRoleDeletedData) GetEventDetailsString(args *LogEventArgs) (
// GetEventDetailsString . . .
func (ed *CLAGroupEnrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s enabled the the project %s from the CLA Group %s.",
+ return fmt.Sprintf("The user %s enabled the the project %s from the CLA Group %s.",
args.UserName, args.ProjectName, args.ClaGroupModel.ProjectName), false
}
// GetEventDetailsString . . .
func (ed *CLAGroupUnenrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s unenrolled the the project %s from the CLA Group %s.",
+ return fmt.Sprintf("The user %s unenrolled the the project %s from the CLA Group %s.",
args.UserName, args.ProjectName, args.ClaGroupModel.ProjectName), false
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s enabled the CLA Service for the project %s.", args.UserName, args.ProjectName), false
+ data := fmt.Sprintf("The user %s enabled the CLA Service", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, false
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s disabled the CLA Service for the project %s.", args.UserName, args.ProjectName), false
+ data := fmt.Sprintf("The user %s disabled the CLA Service", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, false
}
// GetEventSummaryString . . .
func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was added to the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was added", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was deleted from the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was deleted", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was updated for the project project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was updated", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was added", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was disabled", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was updated", ed.RepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *UserCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s was added, User Details: %+v.", args.UserName, args.UserModel)
+ data := fmt.Sprintf("The user %s was added with the user details: %+v.", args.UserName, args.UserModel)
return data, true
}
// GetEventSummaryString . . .
func (ed *UserUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("User: %s was updated, User Details: %+v.", args.UserName, *args.UserModel), true
+ return fmt.Sprintf("The user %s was updated with the user details: %+v.", args.UserName, *args.UserModel), true
}
// GetEventSummaryString . . .
func (ed *UserDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User ID : %s was deleted by: %s.", ed.DeletedUserID, args.UserName)
+ data := fmt.Sprintf("The user ID %s was deleted by the user %s.", ed.DeletedUserID, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s with ID: %s, Email: %s requested Company Invite for Company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
+ data := fmt.Sprintf("The user %s with ID %s and with the email %s requested a company invitation",
+ ed.UserName, ed.UserID, ed.UserEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Company Invite was approved access for User: %s with ID: %s, Email: %s for company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
+ data := fmt.Sprintf("A company invite was approved for the user %s with the ID of %s with the email %s",
+ ed.UserName, ed.UserID, ed.UserEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Company Invite was denied access for User: %s with ID: %s, Email %s for Company: %s.",
- ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
+ data := fmt.Sprintf("A company invite was denied for the user %s with the ID of %s with the email %s",
+ ed.UserName, ed.UserID, ed.UserEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CompanyACLUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User with LF Username %s was added to the ACL for Company: %s by: %s.",
+ data := fmt.Sprintf("The user with LF username %s was added to the access list for the company %s by the user %s.",
ed.UserLFID, args.CompanyName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *CLATemplateCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("PDF templates were created for Project %s by: %s.", args.ProjectName, args.UserName)
+ data := "The PDF templates were created"
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *GitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization: %s was added with auto-enabled: %t, branch protection enabled: %t",
+ data := fmt.Sprintf("The GitHub organization %s was added with auto-enabled set to %t with branch protection enabled set to %t",
ed.GitHubOrganizationName, ed.AutoEnabled, ed.BranchProtectionEnabled)
if ed.AutoEnabledClaGroupID != "" {
- data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group set to %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *GitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization: %s was deleted by: %s.",
- ed.GitHubOrganizationName, args.UserName)
+ data := fmt.Sprintf("The GitHub organization %s was deleted", ed.GitHubOrganizationName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -885,161 +1049,367 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEve
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s approved a CCLA Approval Request for Project: %s, Company: %s.",
- args.UserName, args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The user %s approved a CCLA approval request", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestRejectedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s rejected a CCLA Approval Request for Project: %s, Company: %s.",
- args.UserName, args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The user %s rejected a CCLA approval request", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s added CLA Manager Request: %s for Company: %s, Project: %s.",
- ed.UserName, ed.RequestID, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The user %s added a CLA Manager request", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s was added as CLA Manager for Company: %s, Project: %s.",
- ed.UserName, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The user %s was added as CLA Manager", ed.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s was removed as CLA Manager for Company: %s, Project: %s.",
- ed.UserLFID, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The user %s was removed as CLA Manager", ed.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s for User: %s was approved by: %s for Company: %s, Project: %s.",
- ed.RequestID, ed.UserName, ed.ManagerName, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The CLA Manager request for the user %s was approved by the CLA Manager %s",
+ ed.UserName, ed.ManagerName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerRequestDeniedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s for User: %s was denied by: %s for Company: %s, Project: %s.",
- ed.RequestID, ed.UserName, ed.ManagerName, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The CLA Manager request for the user %s was denied by the CLA Manager %s",
+ ed.UserName, ed.ManagerName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s for User: %s was deleted by: %s for Company: %s, Project: %s.",
- ed.RequestID, ed.UserName, ed.ManagerName, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The CLA Manager request for the user %s was deleted by the CLA Manager %s",
+ ed.UserName, ed.ManagerName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added Email: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s added the email %s to the approval list", ed.UserName, ed.ApprovalListEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed Email: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s removed the email %s from the approval list", ed.UserName, ed.ApprovalListEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added Domain: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s added the domain %s to the approval list", ed.UserName, ed.ApprovalListDomain)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed Domain: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s removed the domain %s from the approval list", ed.UserName, ed.ApprovalListDomain)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s added the GitHub username %s to the approval list", ed.UserName, ed.ApprovalListGitHubUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s removed the GitHub username %s from the approval list", ed.UserName, ed.ApprovalListGitHubUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", ed.UserName, ed.ApprovalListGitHubOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s.",
- ed.UserName, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", ed.UserName, ed.ApprovalListGitHubOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s created a CCLA Approval Request for Project: %s, Company: %s.",
- args.UserName, args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The user %s created a CCLA Approval Request", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the whitelist for Project: %s, Company: %s.",
- args.UserName, ed.GitHubOrganizationName, args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", args.UserName, ed.GitHubOrganizationName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the whitelist for Project: %s, Company: %s.",
- args.UserName, ed.GitHubOrganizationName, args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", args.UserName, ed.GitHubOrganizationName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerAccessRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s has requested to be CLA Manager for the project %s, the company %s.",
- args.UserName, ed.ProjectName, ed.CompanyName)
+ data := fmt.Sprintf("The user %s has requested to be CLA Manager", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerAccessRequestDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s has deleted a request to be CLA Manager.",
- args.UserName)
+ data := fmt.Sprintf("The user %s has deleted a request to be CLA Manager", args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was created by the user %s.",
- args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The CLA Group %s was created by the user %s.", args.ProjectName, args.UserName)
return data, true
}
@@ -1051,34 +1421,60 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString . . .
func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was deleted by the user %s.",
- args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The CLA Group %s was deleted by the user %s.", args.ProjectName, args.UserName)
return data, true
}
// GetEventSummaryString . . .
func (ed *GerritProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d Gerrit repositories were deleted due to CLA Group/Project %s deletion.",
- ed.DeletedCount, args.ProjectName)
+ data := fmt.Sprintf("%d Gerrit repositories were deleted due to CLA Group/Project deletion", ed.DeletedCount)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ data = data + "."
return data, false
}
// GetEventSummaryString . . .
func (ed *GerritAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was added by: %s.", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("The Gerrit repository %s was added by the user %s", ed.GerritRepositoryName, args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was deleted by %s.", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("The Gerrit repository %s was deleted by the user %s", ed.GerritRepositoryName, args.UserName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d GitHub repositories were deleted due to CLA Group/project %s deletion.",
- ed.DeletedCount, args.ProjectName)
+ data := fmt.Sprintf("%d GitHub repositories were deleted due to CLA Group/project deletion",
+ ed.DeletedCount)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ data = data + "."
return data, true
}
@@ -1091,48 +1487,122 @@ func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogE
// GetEventSummaryString . . .
func (ed *ContributorNotifyCompanyAdminData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s notified the company admin %s by the email address %s for the company %s.",
- args.UserName, ed.AdminName, ed.AdminEmail, args.CompanyName)
+ data := fmt.Sprintf("The user %s notified the company admin %s by the email address %s",
+ args.UserName, ed.AdminName, ed.AdminEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ContributorNotifyCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s notified the CLA Designee %s by email %s for the project: %s and the company %s.",
- args.UserName, ed.DesigneeName, ed.DesigneeEmail,
- args.ProjectName, args.CompanyName)
+ data := fmt.Sprintf("The user %s notified the CLA Designee %s by email %s", args.UserName, ed.DesigneeName, ed.DesigneeEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was assigned as CLA Manager Designee for the project: %s for the company %s by the user %s.",
- ed.DesigneeName,
- args.ProjectName, args.CompanyName, args.UserName)
+ data := fmt.Sprintf("The user %s was assigned as CLA Manager Designee", ed.DesigneeName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was converted to a contact for the project %s.",
- args.LfUsername, args.ProjectName)
+ data := fmt.Sprintf("The user %s was converted to a contact", args.LfUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s for project: %s.", args.LfUsername, ed.Role, args.ProjectName)
+ data := fmt.Sprintf("The user %s was added to the role %s", args.LfUsername, ed.Role)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added with to the role %s by user %s.", ed.UserName, ed.Role, args.UserName)
+ data := fmt.Sprintf("The user %s was added to the role %s", ed.UserName, ed.Role)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, false
}
// GetEventSummaryString . . .
func (ed *ClaManagerRoleDeletedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was removed from the role %s by user %s.", ed.UserName, ed.Role, args.UserName)
+ data := fmt.Sprintf("The user %s was removed from the role %s", ed.UserName, ed.Role)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, false
}
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index f6ce5a327..6a95c6b95 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -396,7 +396,7 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companyID string
userEmail, utils.CLADesigneeRole, roleID, projectSFID, v1CompanyModel.CompanyExternalID)
// Log Event
- s.eventService.LogEvent(
+ s.eventService.LogEventWithContext(ctx,
&events.LogEventArgs{
EventType: events.AssignUserRoleScopeType,
LfUsername: lfxUser.Username,
@@ -730,7 +730,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
}
s.SendEmailToOrgAdmin(ctx, s.projectCGRepo, s.projectService, userService.GetPrimaryEmail(adminUser), admin.Contact.Name, v1CompanyModel.CompanyName, projectSF.Name, projectSF.ID, authUser.Email, authUser.UserName, LfxPortalURL)
// Make a note in the event log
- s.eventService.LogEvent(&events.LogEventArgs{
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ContributorNotifyCompanyAdminType,
LfUsername: authUser.UserName,
ExternalProjectID: projectID,
@@ -781,7 +781,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
log.WithFields(f).Debug("creating a contributor assigned CLA designee log event...")
// Make a note in the event log
- s.eventService.LogEvent(&events.LogEventArgs{
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ContributorAssignCLADesigneeType,
LfUsername: authUser.UserName,
ExternalProjectID: projectID,
@@ -798,7 +798,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
log.WithFields(f).Debug("creating a contributor notify CLA designee log event...")
// Make a note in the event log
- s.eventService.LogEvent(&events.LogEventArgs{
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ContributorNotifyCLADesigneeType,
LfUsername: authUser.UserName,
ExternalProjectID: projectID,
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 68cd5e6ff..a72c5d83a 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -36,7 +36,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/signatures"
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/utils"
- acs_service "github.com/communitybridge/easycla/cla-backend-go/v2/acs-service"
+ acsService "github.com/communitybridge/easycla/cla-backend-go/v2/acs-service"
orgModels "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/models"
orgService "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
@@ -74,10 +74,6 @@ const (
HugePageSize = int64(10000)
// LoadRepoDetails = true
DontLoadRepoDetails = false
- // FoundationType the SF foundation type string - previously was "Foundation", now "Project Group"
- FoundationType = "Project Group"
- // Lead representing type of user
- Lead = "lead"
//NoAccount
NoAccount = "Individual - No Account"
//OrgAssociated stating whether user has user association with another org
@@ -402,7 +398,7 @@ func (s *service) CreateCompany(ctx context.Context, companyName, signingEntityN
f["updatedSigningEntityName"] = org.Name
}
- acsClient := acs_service.GetClient()
+ acsClient := acsService.GetClient()
userClient := v2UserService.GetClient()
lfUser, lfErr := userClient.SearchUserByEmail(userEmail)
@@ -622,7 +618,7 @@ func (s *service) AssociateContributor(ctx context.Context, companySFID string,
return nil, userErr
}
- acsServiceClient := acs_service.GetClient()
+ acsServiceClient := acsService.GetClient()
log.WithFields(f).Info("Getting roleID for the contributor role")
roleID, roleErr := acsServiceClient.GetRoleID("contributor")
@@ -662,7 +658,7 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
}
// integrate user,acs,org and project services
userClient := v2UserService.GetClient()
- acServiceClient := acs_service.GetClient()
+ acServiceClient := acsService.GetClient()
orgClient := orgService.GetClient()
user, userErr := userClient.SearchUserByEmail(userEmail)
@@ -714,7 +710,7 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
}
// Log Event
- s.eventService.LogEvent(
+ s.eventService.LogEventWithContext(ctx,
&events.LogEventArgs{
EventType: events.AssignUserRoleScopeType,
LfUsername: user.Username,
@@ -722,6 +718,8 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
ExternalProjectID: projectID,
CompanyModel: v1CompanyModel,
ClaGroupModel: projectModel,
+ CLAGroupID: projectModel.ProjectID,
+ CLAGroupName: projectModel.ProjectName,
UserModel: &v1Models.User{LfUsername: user.Username, UserID: user.ID},
EventData: &events.AssignRoleScopeData{
Role: "contributor",
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index 1dc419ab2..364daef5e 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -65,7 +65,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
}
// record the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GerritRepositoryDeleted,
ProjectID: gerrit.ProjectID,
LfUsername: authUser.UserName,
@@ -126,7 +126,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
}
// record the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GerritRepositoryAdded,
ProjectID: params.ClaGroupID,
LfUsername: authUser.UserName,
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index d537b0fc4..f4682e6b4 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -9,6 +9,8 @@ import (
"fmt"
"strconv"
+ "github.com/sirupsen/logrus"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
@@ -47,24 +49,34 @@ func NewService(githubRepo repositories.Repository,
}
func (s *eventHandlerService) ProcessRepositoryEvent(event *github.RepositoryEvent) error {
+ ctx := utils.NewContext()
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.ProcessRepositoryEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
log.Debugf("ProcessRepositoryEvent called for action : %s", *event.Action)
if event.Action == nil {
return fmt.Errorf("no action found in event payload")
}
switch *event.Action {
case "created":
- return s.handleRepositoryAddedAction(event.Sender, event.Repo)
+ return s.handleRepositoryAddedAction(ctx, event.Sender, event.Repo)
case "deleted":
- return s.handleRepositoryRemovedAction(event.Sender, event.Repo)
+ return s.handleRepositoryRemovedAction(ctx, event.Sender, event.Repo)
default:
- log.Warnf("ProcessRepositoryEvent no handler for action : %s", *event.Action)
+ log.WithFields(f).Warnf("no handler for action : %s", *event.Action)
}
return nil
}
-func (s *eventHandlerService) handleRepositoryAddedAction(sender *github.User, repo *github.Repository) error {
+func (s *eventHandlerService) handleRepositoryAddedAction(ctx context.Context, sender *github.User, repo *github.Repository) error {
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.handleRepositoryAddedAction",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
if repo.ID == nil || *repo.ID == 0 {
return fmt.Errorf("missing repo id")
}
@@ -76,27 +88,28 @@ func (s *eventHandlerService) handleRepositoryAddedAction(sender *github.User, r
if repo.FullName == nil || *repo.FullName == "" {
return fmt.Errorf("repo full name missing")
}
+
repoModel, err := s.autoEnableService.CreateAutoEnabledRepository(repo)
if err != nil {
if errors.Is(err, dynamo_events.ErrAutoEnabledOff) {
- log.Warnf("autoEnable is off for this repo : %s can't continue", *repo.FullName)
+ log.WithFields(f).Warnf("autoEnable is off for this repo : %s can't continue", *repo.FullName)
return nil
}
return err
}
if err := s.autoEnableService.NotifyCLAManagerForRepos(repoModel.RepositoryProjectID, []*models.GithubRepository{repoModel}); err != nil {
- log.Warnf("notifyCLAManager for autoEnabled repo : %s for claGroup : %s failed : %v", repoModel.RepositoryName, repoModel.RepositoryProjectID, err)
+ log.WithFields(f).Warnf("notifyCLAManager for autoEnabled repo : %s for claGroup : %s failed : %v", repoModel.RepositoryName, repoModel.RepositoryProjectID, err)
}
if sender == nil || sender.Login == nil || *sender.Login == "" {
- log.Warnf("not able to send event empty sender")
+ log.WithFields(f).Warnf("not able to send event empty sender")
return nil
}
// sending the log event for the added repository
log.Debugf("handleRepositoryAddedAction sending RepositoryAdded Event for repo %s", *repo.FullName)
- s.eventService.LogEvent(&events.LogEventArgs{
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryAdded,
ProjectID: repoModel.RepositoryProjectID,
UserID: *sender.Login,
@@ -108,7 +121,12 @@ func (s *eventHandlerService) handleRepositoryAddedAction(sender *github.User, r
return nil
}
-func (s *eventHandlerService) handleRepositoryRemovedAction(sender *github.User, repo *github.Repository) error {
+func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context, sender *github.User, repo *github.Repository) error {
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.handleRepositoryRemovedAction",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
if repo.ID == nil || *repo.ID == 0 {
return fmt.Errorf("missing repo id")
}
@@ -116,19 +134,19 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(sender *github.User,
repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
- log.Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
+ log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
return nil
}
return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
}
if err := s.githubRepo.DisableRepository(context.Background(), repoModel.RepositoryID); err != nil {
- log.Warnf("disabling repo : %s failed : %v", *repo.FullName, err)
+ log.WithFields(f).Warnf("disabling repo : %s failed : %v", *repo.FullName, err)
return err
}
// sending event for the action
- s.eventService.LogEvent(&events.LogEventArgs{
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
ProjectID: repoModel.RepositoryProjectID,
UserID: *sender.Login,
@@ -141,6 +159,12 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(sender *github.User,
}
func (s *eventHandlerService) ProcessInstallationRepositoriesEvent(event *github.InstallationRepositoriesEvent) error {
+ ctx := utils.NewContext()
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.ProcessInstallationRepositoriesEvent",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
log.Debugf("ProcessInstallationRepositoriesEvent called for action : %s", *event.Action)
if event.Action == nil {
return fmt.Errorf("no action found in event payload")
@@ -148,28 +172,28 @@ func (s *eventHandlerService) ProcessInstallationRepositoriesEvent(event *github
switch *event.Action {
case "added":
if len(event.RepositoriesAdded) == 0 {
- log.Warnf("repositories list is empty nothing to add")
+ log.WithFields(f).Warnf("repositories list is empty nothing to add")
return nil
}
for _, r := range event.RepositoriesAdded {
- if err := s.handleRepositoryAddedAction(event.Sender, r); err != nil {
+ if err := s.handleRepositoryAddedAction(ctx, event.Sender, r); err != nil {
// we just log it don't want to stop the whole process at this stage
- log.Warnf("adding the repository : %s failed : %v", *r.FullName, err)
+ log.WithFields(f).Warnf("adding the repository : %s failed : %v", *r.FullName, err)
}
}
case "removed":
if len(event.RepositoriesRemoved) == 0 {
- log.Warnf("repositories list is empty nothing to remove")
+ log.WithFields(f).Warnf("repositories list is empty nothing to remove")
return nil
}
for _, r := range event.RepositoriesRemoved {
- if err := s.handleRepositoryRemovedAction(event.Sender, r); err != nil {
- log.Warnf("removing the repository : %s failed : %v", *r.FullName, err)
+ if err := s.handleRepositoryRemovedAction(ctx, event.Sender, r); err != nil {
+ log.WithFields(f).Warnf("removing the repository : %s failed : %v", *r.FullName, err)
}
}
default:
- log.Warnf("ProcessInstallationRepositoriesEvent no handler for action : %s", *event.Action)
+ log.WithFields(f).Warnf("ProcessInstallationRepositoriesEvent no handler for action : %s", *event.Action)
}
return nil
diff --git a/cla-backend-go/v2/github_organizations/handlers.go b/cla-backend-go/v2/github_organizations/handlers.go
index ac4f893c5..d3022a639 100644
--- a/cla-backend-go/v2/github_organizations/handlers.go
+++ b/cla-backend-go/v2/github_organizations/handlers.go
@@ -128,7 +128,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
// Log the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
EventType: events.GitHubOrganizationAdded,
ExternalProjectID: params.ProjectSFID,
@@ -173,7 +173,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
return github_organizations.NewDeleteProjectGithubOrganizationBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
EventType: events.GitHubOrganizationDeleted,
ExternalProjectID: params.ProjectSFID,
@@ -227,7 +227,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
// Log the event
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
EventType: events.GitHubOrganizationUpdated,
ExternalProjectID: params.ProjectSFID,
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 14aae468c..5bafd1a10 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -127,7 +127,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
// Log the events
for _, result := range results {
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryAdded,
ProjectID: utils.StringValue(params.GithubRepositoryInput.ClaGroupID),
ExternalProjectID: params.ProjectSFID,
@@ -202,7 +202,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
ExternalProjectID: params.ProjectSFID,
ProjectID: ghRepo.RepositoryProjectID,
@@ -338,7 +338,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
// We could extract the parameter values from the branch protection payload to determine if it was added/remove or simply updated
// For now, let's just set the updated event log
- eventService.LogEvent(&events.LogEventArgs{
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryBranchProtectionUpdated,
ExternalProjectID: params.ProjectSFID,
ProjectID: params.ProjectSFID,
From b819983d06c3516ea1d4fe18909f3adf6f50120d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 2 Mar 2021 15:28:32 -0800
Subject: [PATCH 0129/1276] =?UTF-8?q?Added=20Event=20Delete=20Python=20Fun?=
=?UTF-8?q?ction=20for=20Database=20Cleanup=20for=20DEV=20and=20S=E2=80=A6?=
=?UTF-8?q?=20(#2728)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: David Deal
---
cla-backend/cla/models/dynamo_models.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 920308633..564b8e981 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -4262,6 +4262,9 @@ def save(self) -> None:
self.model.date_modified = datetime.datetime.utcnow()
self.model.save()
+ def delete(self):
+ self.model.delete()
+
def load(self, event_id):
try:
event = self.model.get(str(event_id))
From bb920618bbbbb1082149723bc41c25470708df13 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 2 Mar 2021 15:49:26 -0800
Subject: [PATCH 0130/1276] Updated CodeQL Configuration (#2729)
- Resolved CodeQL warning: 1 issue was detected with this workflow: git
checkout HEAD^2 is no longer necessary. Please remove this step as Code
Scanning recommends analyzing the merge commit for best results.
Signed-off-by: David Deal
---
.github/workflows/codeql-analysis.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index c4d7d1cbf..272ef956d 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,8 +24,9 @@ jobs:
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- - run: git checkout HEAD^2
- if: ${{ github.event_name == 'pull_request' }}
+ # Note: git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.
+ #- run: git checkout HEAD^2
+ # if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
From 82919cacabd1a9f5d52e7ecc6f5259c068d658d6 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Wed, 3 Mar 2021 17:48:47 +0300
Subject: [PATCH 0131/1276] [#2730,#2731] Feature/Corporate Email updates
- Updated email content sent to Users with no lfid for invite flow
- Refactored utility functions for sending email
- Updated unit tests
Signed-off-by: wanyaland
---
cla-backend-go/approval_list/service.go | 2 +-
cla-backend-go/cla_manager/service.go | 5 ++--
.../emails/approval_list_templates.go | 4 +--
.../emails/cla_manager_templates.go | 9 ++++--
cla-backend-go/emails/uitls.go | 15 ++++++++--
.../emails/v2_cla_manager_templates.go | 29 ++++++++++++-------
.../tests/v2_cla_manager_templates_test.go | 20 ++++++++-----
cla-backend-go/v2/cla_manager/emails.go | 4 +--
cla-backend-go/v2/cla_manager/service.go | 4 +--
9 files changed, 60 insertions(+), 32 deletions(-)
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index cf8ad6731..9ba532bac 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -313,7 +313,7 @@ func (s service) sendRequestEmailToRecipient(projectClaGroupRepository projects_
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: Request to Authorize %s for %s", contributorName, projectName)
recipients := []string{recipientAddress}
- body, err := emails.RenderRequestToAuthorizeTemplate(projectClaGroupRepository, claGroupModel.Version, claGroupModel.ProjectExternalID,
+ body, err := emails.RenderRequestToAuthorizeTemplate(projectClaGroupRepository, s.projectService, claGroupModel.Version, claGroupModel.ProjectExternalID,
emails.RequestToAuthorizeTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: recipientName,
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index 7ed134f6e..de658b095 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -338,7 +338,7 @@ func (s service) RemoveClaManager(ctx context.Context, companyID string, claGrou
}
// Notify the removed manager
- sendRemovedClaManagerEmailToRecipient(s.projectClaRepository, companyModel, claGroupModel, userModel.LfUsername, userModel.LfEmail, claManagers)
+ s.sendRemovedClaManagerEmailToRecipient(s.projectClaRepository, companyModel, claGroupModel, userModel.LfUsername, userModel.LfEmail, claManagers)
// Send an event
s.eventsService.LogEvent(&events.LogEventArgs{
@@ -437,7 +437,7 @@ func sendClaManagerAddedEmailToCLAManagers(companyModel *models.Company, claGrou
}
// sendRequestRejectedEmailToRecipient generates and sends an email to the specified recipient
-func sendRemovedClaManagerEmailToRecipient(projectsClaGroupRepository projects_cla_groups.Repository, companyModel *models.Company, claGroupModel *models.ClaGroup, recipientName, recipientAddress string, claManagers []models.User) {
+func (s *service) sendRemovedClaManagerEmailToRecipient(projectsClaGroupRepository projects_cla_groups.Repository, companyModel *models.Company, claGroupModel *models.ClaGroup, recipientName, recipientAddress string, claManagers []models.User) {
companyName := companyModel.CompanyName
projectName := claGroupModel.ProjectName
@@ -488,6 +488,7 @@ func sendRemovedClaManagerEmailToRecipient(projectsClaGroupRepository projects_c
recipients := []string{recipientAddress}
body, err := emails.RenderRemovedCLAManagerTemplate(
projectsClaGroupRepository,
+ s.projectService,
claGroupModel.Version,
recipientName,
companyName,
diff --git a/cla-backend-go/emails/approval_list_templates.go b/cla-backend-go/emails/approval_list_templates.go
index 4b0dc1613..faaf50642 100644
--- a/cla-backend-go/emails/approval_list_templates.go
+++ b/cla-backend-go/emails/approval_list_templates.go
@@ -86,8 +86,8 @@ repository. This will permit them to begin contributing to {{.Project.ExternalPr
)
// RenderRequestToAuthorizeTemplate renders RequestToAuthorizeTemplate
-func RenderRequestToAuthorizeTemplate(repository projects_cla_groups.Repository, claGroupVersion string, projecSFID string, params RequestToAuthorizeTemplateParams) (string, error) {
- if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projecSFID, ¶ms.CLAManagerTemplateParams); err != nil {
+func RenderRequestToAuthorizeTemplate(repository projects_cla_groups.Repository, projectService project.Service, claGroupVersion string, projecSFID string, params RequestToAuthorizeTemplateParams) (string, error) {
+ if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectService, projecSFID, ¶ms.CLAManagerTemplateParams, nil); err != nil {
return "", err
}
diff --git a/cla-backend-go/emails/cla_manager_templates.go b/cla-backend-go/emails/cla_manager_templates.go
index b4d8f318c..ac47c6085 100644
--- a/cla-backend-go/emails/cla_manager_templates.go
+++ b/cla-backend-go/emails/cla_manager_templates.go
@@ -3,7 +3,10 @@
package emails
-import "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+import (
+ "github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+)
// RemovedCLAManagerTemplateParams is email params for RemovedCLAManagerTemplate
type RemovedCLAManagerTemplateParams struct {
@@ -29,14 +32,14 @@ const (
)
// RenderRemovedCLAManagerTemplate renders the RemovedCLAManagerTemplate
-func RenderRemovedCLAManagerTemplate(repository projects_cla_groups.Repository, claGroupModelVersion, recipientName, companyName, projectSFID string, claManagers []ClaManagerInfoParams) (string, error) {
+func RenderRemovedCLAManagerTemplate(repository projects_cla_groups.Repository, projectService project.Service, claGroupModelVersion, recipientName, companyName, projectSFID string, claManagers []ClaManagerInfoParams) (string, error) {
params := CLAManagerTemplateParams{
RecipientName: recipientName,
CompanyName: companyName,
CLAManagers: claManagers,
}
- err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectSFID, ¶ms)
+ err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectService, projectSFID, ¶ms, nil)
if err != nil {
return "", err
}
diff --git a/cla-backend-go/emails/uitls.go b/cla-backend-go/emails/uitls.go
index b1bf2af89..2a3f909cf 100644
--- a/cla-backend-go/emails/uitls.go
+++ b/cla-backend-go/emails/uitls.go
@@ -15,7 +15,7 @@ import (
// PrefillCLAManagerTemplateParamsFromClaGroup fetches data from projects_cla_groups.Repository to prefill some of the fields
// of CLAManagerTemplateParams, like childCount and FoundationName and etc
-func PrefillCLAManagerTemplateParamsFromClaGroup(repository projects_cla_groups.Repository, projectSFID string, params *CLAManagerTemplateParams) error {
+func PrefillCLAManagerTemplateParamsFromClaGroup(repository projects_cla_groups.Repository, projectService project.Service, projectSFID string, params *CLAManagerTemplateParams, corporateConsole *string) error {
projectCLAGroup, err := repository.GetClaGroupIDForProject(projectSFID)
if err != nil {
if errors.Is(err, projects_cla_groups.ErrProjectNotAssociatedWithClaGroup) {
@@ -24,6 +24,13 @@ func PrefillCLAManagerTemplateParamsFromClaGroup(repository projects_cla_groups.
}
return err
}
+ ctx := context.Background()
+
+ //Check if signed at foundationLevel
+ signedAtFoundationLevel, err := projectService.SignedAtFoundationLevel(ctx, projectCLAGroup.FoundationSFID)
+ if err != nil {
+ return err
+ }
params.CLAGroupName = projectCLAGroup.ClaGroupName
params.Project = CLAProjectParams{
@@ -31,8 +38,10 @@ func PrefillCLAManagerTemplateParamsFromClaGroup(repository projects_cla_groups.
ProjectSFID: projectSFID,
FoundationName: projectCLAGroup.FoundationName,
FoundationSFID: projectCLAGroup.FoundationSFID,
- SignedAtFoundationLevel: false,
- CorporateConsole: "",
+ SignedAtFoundationLevel: signedAtFoundationLevel,
+ }
+ if corporateConsole != nil {
+ params.Project.CorporateConsole = *corporateConsole
}
projects, err := repository.GetProjectsIdsForClaGroup(projectCLAGroup.ClaGroupID)
if err != nil {
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 3457c3d1f..1f26ee664 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -62,7 +62,7 @@ Either you or someone whom to designate from your company can login to this port
// RenderV2OrgAdminTemplate renders V2OrgAdminTemplate
func RenderV2OrgAdminTemplate(repository projects_cla_groups.Repository, projectService project.Service, projectSFID string, params V2OrgAdminTemplateParams) (string, error) {
- if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectSFID, ¶ms.CLAManagerTemplateParams); err != nil {
+ if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectService, projectSFID, ¶ms.CLAManagerTemplateParams, nil); err != nil {
return "", err
}
@@ -132,7 +132,7 @@ const (
{{.Project.ExternalProjectName}}
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom to designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.Project.GetProjectFullURL}}
+Either you or someone whom you designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.Project.GetProjectFullURL}}
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
Please notify the user once CLA setup is complete.
`
@@ -140,7 +140,7 @@ Either you or someone whom to designate from your company can login to this port
// RenderV2CLAManagerDesigneeCorporateTemplate renders V2CLAManagerDesigneeCorporateTemplate
func RenderV2CLAManagerDesigneeCorporateTemplate(repository projects_cla_groups.Repository, projectService project.Service, projectSFID string, params V2CLAManagerDesigneeCorporateTemplateParams) (string, error) {
- if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectSFID, ¶ms.CLAManagerTemplateParams); err != nil {
+ if err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectService, projectSFID, ¶ms.CLAManagerTemplateParams, nil); err != nil {
return "", err
}
@@ -245,6 +245,7 @@ type V2CLAManagerToUserWithNoLFIDTemplateParams struct {
CLAManagerTemplateParams
RequesterUserName string
RequesterEmail string
+ Projects []CLAProjectParams
}
const (
@@ -253,25 +254,33 @@ const (
// V2CLAManagerToUserWithNoLFIDTemplate is email template
V2CLAManagerToUserWithNoLFIDTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the Project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}}.
-
User {{.RequesterUserName}} ({{.RequesterEmail}}) was trying to add you as a CLA Manager for the Project {{.Project.ExternalProjectName}} but was unable to identify your account details in
-the EasyCLA system. In order to become a CLA Manager for the Project {{.Project.ExternalProjectName}}, you will need to accept the invite below.
-Once complete, notify the user {{.RequesterUserName}} and they will be able to add you as a CLA Manager.
This is a notification email from EasyCLA regarding the CLA setup and signing process for the organization {{.CompanyName}}.The user {{.RequesterUserName}} ({{.RequesterEmail}}) has identified you as a potential candidate to setup the Corporate CLA for the organization {{.CompanyName }} and the project {{.GetProjectNameOrFoundation}}
+
Before the user contribution can be accepted, your organization must sign a Corporate CLA(CCLA).
+
Please complete the following steps:
+
+
Please click on Accept Invite to create your LF Login.This is used to access the EasyCLA CLA Manager console.
+
After login, you will be redirected to the portal {{.Project.CorporateConsole}} where you can either sign the CLA for the project: {{.Project.GetProjectFullURL}}, or send it to an authorized signatory for your company.
+
After signing the CLA, you will need to add this contributor to the approved list in the CLA Manager console.
+
After adding the contributor, please notify them so that they can complete the contribution process.
+
+
`
)
// RenderV2CLAManagerToUserWithNoLFIDTemplate renders V2CLAManagerToUserWithNoLFIDTemplate
-func RenderV2CLAManagerToUserWithNoLFIDTemplate(repository projects_cla_groups.Repository, recipientName, projectName, projectSFID, requesterName, requesterEmail string) (string, error) {
+func RenderV2CLAManagerToUserWithNoLFIDTemplate(repository projects_cla_groups.Repository, projectService project.Service, recipientName, projectName, projectSFID, requesterName, requesterEmail, corporateConsole string) (string, error) {
params := V2CLAManagerToUserWithNoLFIDTemplateParams{
CLAManagerTemplateParams: CLAManagerTemplateParams{
RecipientName: recipientName,
+ Project: CLAProjectParams{
+ ExternalProjectName: projectName,
+ },
},
RequesterUserName: requesterName,
RequesterEmail: requesterEmail,
}
- err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectSFID, ¶ms.CLAManagerTemplateParams)
+ err := PrefillCLAManagerTemplateParamsFromClaGroup(repository, projectService, projectSFID, ¶ms.CLAManagerTemplateParams, &corporateConsole)
if err != nil {
return "", err
}
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 33999a9bf..10292337e 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -191,9 +191,14 @@ func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
params := emails.V2CLAManagerToUserWithNoLFIDTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
RecipientName: "JohnsClaManager",
- Project: emails.CLAProjectParams{ExternalProjectName: "JohnsProjectExternal"},
- CLAGroupName: "JohnsCLAGroupName",
- CompanyName: "JohnsCompany",
+ Project: emails.CLAProjectParams{ExternalProjectName: "JohnsProjectExternal",
+ CorporateConsole: "http://CorporateConsole.com",
+ SignedAtFoundationLevel: false,
+ ProjectSFID: "ProjectSFID",
+ FoundationSFID: "FoundationSFID",
+ },
+ CLAGroupName: "JohnsCLAGroupName",
+ CompanyName: "JohnsCompany",
},
RequesterUserName: "RequesterUserNameValue",
RequesterEmail: "RequesterEmailValue",
@@ -203,8 +208,9 @@ func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the Project JohnsProjectExternal and CLA Group JohnsCLAGroupName")
- assert.Contains(t, result, "User RequesterUserNameValue (RequesterEmailValue) was trying")
- assert.Contains(t, result, "CLA Manager for the Project JohnsProject")
- assert.Contains(t, result, "notify the user RequesterUserNameValue")
+ assert.Contains(t, result, "regarding the CLA setup and signing process for the organization JohnsCompany")
+ assert.Contains(t, result, "The user RequesterUserNameValue (RequesterEmailValue) has identified you as a potential candidate to setup the Corporate CLA for the organization JohnsCompany and the project JohnsProjectExternal")
+ assert.Contains(t, result, "After login, you will be redirected to the portal http://CorporateConsole.com")
+ assert.Contains(t, result, "After adding the contributor, please notify them")
+ assert.Contains(t, result, `where you can either sign the CLA for the project: JohnsProjectExternal`)
}
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index 18dce0f9e..e4b41deb9 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -275,7 +275,7 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, project
}
// sendEmailToUserWithNoLFID helper function to send email to a given user with no LFID
-func (s *service) SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error {
+func (s *service) SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role, corporateConsole string) error {
f := logrus.Fields{
"functionName": "cla_manager.service.SendEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -291,7 +291,7 @@ func (s *service) SendEmailToUserWithNoLFID(ctx context.Context, repository proj
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager with %s role", role)
- body, err := emails.RenderV2CLAManagerToUserWithNoLFIDTemplate(repository, userWithNoLFIDName, projectName, *projectID, requesterUsername, requesterEmail)
+ body, err := emails.RenderV2CLAManagerToUserWithNoLFIDTemplate(repository, projectService, userWithNoLFIDName, projectName, *projectID, requesterUsername, requesterEmail, corporateConsole)
if err != nil {
log.WithFields(f).WithError(err).Warnf("rendering email : %s failed : %v", emails.V2CLAManagerToUserWithNoLFIDTemplateName, err)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 6a95c6b95..b6596b0bd 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -97,7 +97,7 @@ type Service interface {
SendEmailToCLAManagerDesigneeCorporate(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectSFID string, projectName string, designeeEmail string, designeeName string, senderEmail string, senderName string)
SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorID string, contributorName string)
SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectIDs []string, foundationSFID, role string, corporateConsoleV2URL string) error
- SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role string) error
+ SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role, corporateConsole string) error
}
// NewService returns instance of CLA Manager service
@@ -755,7 +755,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
msg := fmt.Sprintf("User: %s does not have an LF Login", userEmail)
log.WithFields(f).Warn(msg)
// Send email
- sendEmailErr := s.SendEmailToUserWithNoLFID(ctx, s.projectCGRepo, projectSF.Name, authUser.UserName, authUser.Email, fullName, userEmail, v1CompanyModel.CompanyExternalID, &projectSF.ID, utils.CLADesigneeRole)
+ sendEmailErr := s.SendEmailToUserWithNoLFID(ctx, s.projectCGRepo, s.projectService, projectSF.Name, authUser.UserName, authUser.Email, fullName, userEmail, v1CompanyModel.CompanyExternalID, &projectSF.ID, utils.CLADesigneeRole, corporateConsole)
if sendEmailErr != nil {
log.WithFields(f).Warnf("Error sending email: %+v", sendEmailErr)
return nil, sendEmailErr
From 6bd986d6e42a6ba0912985f5617f36d9924ac9d9 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 3 Mar 2021 19:22:31 +0200
Subject: [PATCH 0132/1276] [#2734]use project name instead of cla group name
(#2735)
Signed-off-by: makkalot
---
cla-backend-go/cla_manager/service.go | 15 ++++++++++++++-
cla-backend-go/emails/cla_manager_templates.go | 2 +-
.../emails/cla_manager_templates_test.go | 8 ++++----
3 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index de658b095..a1e38b65d 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+
"github.com/communitybridge/easycla/cla-backend-go/emails"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -408,6 +410,16 @@ func sendClaManagerAddedEmailToCLAManagers(companyModel *models.Company, claGrou
companyName := companyModel.CompanyName
projectName := claGroupModel.ProjectName
+ // we want the Project Name in the email not the cla group name
+ ps := v2ProjectService.GetClient()
+ projectSF, projectErr := ps.GetProject(claGroupModel.ProjectExternalID)
+ if projectErr != nil {
+ msg := fmt.Sprintf("Project service lookup error for SFID: %s, error : %+v",
+ claGroupModel.ProjectExternalID, projectErr)
+ log.Warn(msg)
+ return
+ }
+
// subject string, body string, recipients []string
subject := fmt.Sprintf("EasyCLA: CLA Manager Added Notice for %s", projectName)
recipients := []string{recipientAddress}
@@ -415,8 +427,9 @@ func sendClaManagerAddedEmailToCLAManagers(companyModel *models.Company, claGrou
emails.ClaManagerAddedToCLAManagersTemplate,
emails.ClaManagerAddedToCLAManagersTemplateParams{
CLAManagerTemplateParams: emails.CLAManagerTemplateParams{
+ CLAGroupName: projectName,
RecipientName: recipientName,
- Project: emails.CLAProjectParams{ExternalProjectName: projectName},
+ Project: emails.CLAProjectParams{ExternalProjectName: projectSF.Name},
CompanyName: companyName,
},
Name: name,
diff --git a/cla-backend-go/emails/cla_manager_templates.go b/cla-backend-go/emails/cla_manager_templates.go
index ac47c6085..fddde4530 100644
--- a/cla-backend-go/emails/cla_manager_templates.go
+++ b/cla-backend-go/emails/cla_manager_templates.go
@@ -200,7 +200,7 @@ const (
// ClaManagerAddedToCLAManagersTemplate is email template for
ClaManagerAddedToCLAManagersTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}}.
+
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}} associated with the CLA Group {{.CLAGroupName}}.
The following user has been added as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that they can now
maintain the list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the
list of company’s CLA Managers for {{.Project.ExternalProjectName}}.
diff --git a/cla-backend-go/emails/cla_manager_templates_test.go b/cla-backend-go/emails/cla_manager_templates_test.go
index 913882052..c23001ee0 100644
--- a/cla-backend-go/emails/cla_manager_templates_test.go
+++ b/cla-backend-go/emails/cla_manager_templates_test.go
@@ -203,10 +203,10 @@ func TestClaManagerAddedToCLAManagersTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
- assert.Contains(t, result, "regarding the project JohnsProject")
- assert.Contains(t, result, "CLA Manager from JohnsCompany for the project JohnsProject")
- assert.Contains(t, result, "contribute to JohnsProject")
- assert.Contains(t, result, "CLA Managers for JohnsProject")
+ assert.Contains(t, result, "regarding the project JohnsProjectExternal associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "CLA Manager from JohnsCompany for the project JohnsProjectExternal")
+ assert.Contains(t, result, "contribute to JohnsProjectExternal")
+ assert.Contains(t, result, "CLA Managers for JohnsProjectExternal")
assert.Contains(t, result, "
This is a notification email from EasyCLA regarding the project %s.
-
You have been %s %s the Approval List of %s for %s by CLA Manager %s. This means that %s on behalf of %s.
+
You have been %s %s the Approval List of %s for %s by CLA Manager %s. This means that %s.
If you had previously submitted a pull request to EasyCLA Test Group that had failed,
you can now go back to it and follow the link to verify with your organization.
This is a notification email from EasyCLA regarding the project(s) {project_names_list} associated with the CLA Group {params.cla_group_name}. {params.cla_manager_name} has designated you as being an authorized signatory for the organization {params.company_name}. In order for employees of your company to contribute to any of the above project(s), they must do so under a Contributor License Agreement signed by someone with authority n behalf of your company.
'
+ email_body += f'
This is a notification email from EasyCLA regarding the project(s) {project_names_list} associated with the CLA Group {params.cla_group_name}. {params.cla_manager_name} has designated you as an authorized signatory for the organization {params.company_name}. In order for employees of your company to contribute to any of the above project(s), they must do so under a Contributor License Agreement signed by someone with authority n behalf of your company.
'
email_body += f'
After you sign, {params.cla_manager_name} (as the initial CLA Manager for your company) will be able to maintain the list of specific employees authorized to contribute to the project(s) under this signed CLA.
'
email_body += f'
If you are authorized to sign on your company’s behalf, and if you approve {params.cla_manager_name} as your initial CLA Manager, please review the document and sign the CLA. If you have questions, or if you are not an authorized signatory of this company, please contact the requester at {params.cla_manager_email}.
" in email_body
assert "EasyCLA regarding the project(s) project1, project2 associated" in email_body
assert "with the CLA Group cla_group_name_value" in email_body
- assert "john has designated you as being an authorized signatory" in email_body
+ assert "john has designated you as an authorized signatory" in email_body
assert "signatory for the organization IBM" in email_body
assert "
After you sign, john (as the initial CLA Manager for your company)" in email_body
assert "and if you approve john as your initial CLA Manager" in email_body
From fed1462ed23c3e4d2ed60fbff5b6905f2d3098ef Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 3 Mar 2021 13:23:23 -0800
Subject: [PATCH 0134/1276] Resolved CLA Group Project Document TimeFormat
Issue (#2738)
- resolves issue with OpenVDB ICLA/CCLA template documents not being loaded in the PCC
- Added additional time format parsing options, added/verified with unit tests
Signed-off-by: David Deal
---
cla-backend-go/project/helpers.go | 4 ++--
cla-backend-go/tests/utils_test.go | 2 ++
cla-backend-go/utils/utils.go | 2 ++
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/project/helpers.go b/cla-backend-go/project/helpers.go
index 0d1491860..06a63329a 100644
--- a/cla-backend-go/project/helpers.go
+++ b/cla-backend-go/project/helpers.go
@@ -69,7 +69,7 @@ func buildCLAGroupDocumentModels(dbDocumentModels []DBProjectDocumentModel) []mo
func (s service) fillRepoInfo(ctx context.Context, project *models.ClaGroup) {
f := logrus.Fields{
- "functionName": "fillRepoInfo",
+ "functionName": "v1.project.helpers.fillRepoInfo",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
@@ -108,7 +108,7 @@ func (s service) fillRepoInfo(ctx context.Context, project *models.ClaGroup) {
// GetCurrentDocument returns the current document based on the version and date/time
func GetCurrentDocument(ctx context.Context, docs []models.ClaGroupDocument) (models.ClaGroupDocument, error) {
f := logrus.Fields{
- "functionName": "GetCurrentDocument",
+ "functionName": "v1.project.helpers.GetCurrentDocument",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
var currentDoc models.ClaGroupDocument
diff --git a/cla-backend-go/tests/utils_test.go b/cla-backend-go/tests/utils_test.go
index 53abcb9ac..94a8c1578 100644
--- a/cla-backend-go/tests/utils_test.go
+++ b/cla-backend-go/tests/utils_test.go
@@ -89,6 +89,8 @@ func TestParseDateTimeMS(t *testing.T) {
"2020-03-27T15:04:05.000000+0000",
"2016-12-02T05:14:05.000000+0800",
"2006-08-31T10:24:05.000000-1000",
+ "2019-04-15T20:30:12.13589",
+ "2019-04-15T20:30:19.321645",
}
for _, dateTimeStr := range validInput {
diff --git a/cla-backend-go/utils/utils.go b/cla-backend-go/utils/utils.go
index 41dec29cf..b4f84a098 100644
--- a/cla-backend-go/utils/utils.go
+++ b/cla-backend-go/utils/utils.go
@@ -63,6 +63,8 @@ func ParseDateTime(dateTimeStr string) (time.Time, error) {
"2006-01-02T15:04:05Z-07:00",
"2006-01-02T15:04:05-07:00",
"2006-01-02T15:04:05.000000-0700",
+ "2006-01-02T15:04:05.00000",
+ "2006-01-02T15:04:05.000000",
time.RFC850,
time.RFC1123,
time.RFC1123Z,
From 5571873aa01806ebf8408c0aaff8958526099e2b Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 4 Mar 2021 19:11:14 +0300
Subject: [PATCH 0135/1276] [#2739] Feature/Email|Username Labels (#2741)
- Added labels to User emails speficying type (GH, LF|EasyCLA)
Signed-off-by: wanyaland
---
.../emails/v2_cla_manager_templates.go | 15 ++++--
.../tests/v2_cla_manager_templates_test.go | 34 +++++++++---
cla-backend-go/utils/constants.go | 12 +++++
cla-backend-go/v2/cla_manager/emails.go | 20 ++++---
cla-backend-go/v2/cla_manager/service.go | 52 ++++++++++++-------
5 files changed, 92 insertions(+), 41 deletions(-)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index 1f26ee664..fdabd7460 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -12,6 +12,14 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
+//Contributor representing GH user details
+type Contributor struct {
+ Email string
+ Username string
+ EmailLabel string
+ UsernameLabel string
+}
+
// V2ContributorApprovalRequestTemplateParams is email template params for V2ContributorApprovalRequestTemplate
type V2ContributorApprovalRequestTemplateParams struct {
CLAManagerTemplateParams
@@ -159,8 +167,7 @@ func RenderV2CLAManagerDesigneeCorporateTemplate(repository projects_cla_groups.
type V2ToCLAManagerDesigneeTemplateParams struct {
RecipientName string
Projects []CLAProjectParams
- ContributorEmail string
- ContributorName string
+ Contributor Contributor
CorporateConsole string
CompanyName string
}
@@ -186,7 +193,7 @@ const (
V2ToCLAManagerDesigneeTemplate = `
Hello {{.RecipientName}},
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
-
We received a request from {{.ContributorName}} ({{.ContributorEmail}}) to contribute to the above projects on behalf of your organization.
+
We received a request from {{.Contributor.UsernameLabel}}: {{.Contributor.Username}} ({{.Contributor.EmailLabel}}: {{.Contributor.Email}}) to contribute to the above projects on behalf of your organization.
Before the user contribution can be accepted, your organization must sign a Corporate CLA (CCLA).The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute.
This is a notification email from EasyCLA regarding the project(s): {{.GetProjectsOrProject}}.
-
We received a request from {{.ContributorName}} ({{.ContributorEmail}}) to contribute to any of the above projects on behalf of your
+
We received a request from {{.Contributor.UsernameLabel}}: {{.Contributor.Username}} ({{.Contributor.EmailLabel}}: {{.Contributor.Email}}) to contribute to any of the above projects on behalf of your
organization {{.CompanyName}}.
Before the user contribution can be accepted, your organization must sign a Corporate CLA(CCLA).
The requester has stated that you would be the initial CLA Manager for this CCLA, to coordinate the signing of the CCLA and then manage the list of employees who are authorized to contribute.
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 10292337e..8b1b39965 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -139,8 +139,12 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
{ExternalProjectName: "Project2", ProjectSFID: "ProjectSFID2", FoundationSFID: "FoundationSFID2", CorporateConsole: "http://CorporateConsole.com"},
},
- ContributorEmail: "ContributorIDValue",
- ContributorName: "ContributorNameValue",
+ Contributor: emails.Contributor{
+ Email: "ContributorEmailValue",
+ Username: "ContributorNameValue",
+ EmailLabel: utils.EmailLabel,
+ UsernameLabel: utils.UserLabel,
+ },
CorporateConsole: "http://CorporateConsole.com",
}
@@ -149,18 +153,21 @@ func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1, Project2")
- assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, "from Username: ContributorNameValue (Email Address: ContributorEmailValue)")
assert.Contains(t, result, `CLA for any of the project(s): Project1,Project2`)
params.Projects = []emails.CLAProjectParams{
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "http://CorporateConsole.com"},
}
+ params.Contributor.EmailLabel = utils.GitHubEmailLabel
+ params.Contributor.UsernameLabel = utils.GitHubUserLabel
+
result, err = emails.RenderTemplate(utils.V1, emails.V2ToCLAManagerDesigneeTemplateName, emails.V2ToCLAManagerDesigneeTemplate,
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager")
assert.Contains(t, result, "regarding the project(s): Project1")
- assert.Contains(t, result, "from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, "from GitHub Username: ContributorNameValue (GitHub Email Address: ContributorEmailValue)")
assert.Contains(t, result, `CLA for any of the project(s): Project1`)
}
@@ -172,8 +179,12 @@ func TestV2DesigneeToUserWithNoLFIDTemplate(t *testing.T) {
{ExternalProjectName: "Project1", ProjectSFID: "ProjectSFID1", FoundationSFID: "FoundationSFID1", CorporateConsole: "https://corporate.dev.lfcla.com"},
{ExternalProjectName: "Project2", ProjectSFID: "ProjectSFID2", FoundationSFID: "FoundationSFID2", CorporateConsole: "https://corporate.dev.lfcla.com"},
},
- ContributorEmail: "ContributorIDValue",
- ContributorName: "ContributorNameValue",
+ Contributor: emails.Contributor{
+ Email: "ContributorEmail",
+ Username: "ContributorUsername",
+ EmailLabel: utils.EmailLabel,
+ UsernameLabel: utils.UserLabel,
+ },
CorporateConsole: "https://corporate.dev.lfcla.com",
}
@@ -181,10 +192,19 @@ func TestV2DesigneeToUserWithNoLFIDTemplate(t *testing.T) {
params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello JohnsClaManager,")
- assert.Contains(t, result, "We received a request from ContributorNameValue (ContributorIDValue)")
+ assert.Contains(t, result, "We received a request from Username: ContributorUsername (Email Address: ContributorEmail)")
assert.Contains(t, result, "After login, you will be redirected to the portal https://corporate.dev.lfcla.com ")
assert.Contains(t, result, `where you can either sign the CLA for any of the project(s): Project1`)
assert.Contains(t, result, "or send it to an authorized signatory for your company.")
+
+ params.Contributor.EmailLabel = utils.GitHubEmailLabel
+ params.Contributor.UsernameLabel = utils.GitHubUserLabel
+
+ result, err = emails.RenderTemplate(utils.V1, emails.V2DesigneeToUserWithNoLFIDTemplateName, emails.V2DesigneeToUserWithNoLFIDTemplate,
+ params)
+
+ assert.NoError(t, err)
+ assert.Contains(t, result, "We received a request from GitHub Username: ContributorUsername (GitHub Email Address: ContributorEmail)")
}
func TestV2CLAManagerToUserWithNoLFIDTemplate(t *testing.T) {
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 98b23103b..c219dca8e 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -134,3 +134,15 @@ const GithubRepoNotFound = "GitHub repository not found"
//GithubRepoExists is a string that indicates the GitHub repository already exists
const GithubRepoExists = "GitHub repository exists"
+
+//GitHubEmailLabel represents the GH Email label used for email
+const GitHubEmailLabel = "GitHub Email Address"
+
+//GitHubUserLabel represents the GH username Label used for email
+const GitHubUserLabel = "GitHub Username"
+
+//EmailLabel represents LF/EasyCLA Email address
+const EmailLabel = "Email Address"
+
+//UserLabel represents the LF/EasyCLA username
+const UserLabel = "Username"
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index e4b41deb9..99ce1bdf2 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -188,7 +188,7 @@ func (s *service) SendEmailToCLAManagerDesigneeCorporate(ctx context.Context, re
}
}
-func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorEmail string, contributorName string) {
+func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorModel emails.Contributor) {
f := logrus.Fields{
"functionName": "cla_manager.service.SendEmailToCLAManagerDesignee",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -197,18 +197,17 @@ func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository
"projectNames": strings.Join(projectNames, ","),
"designeeEmail": designeeEmail,
"designeeName": designeeName,
- "contributorEmail": contributorEmail,
- "contributorName": contributorName,
+ "contributorEmail": contributorModel.Email,
+ "contributorName": contributorModel.Username,
}
subject := fmt.Sprintf("EasyCLA: Invitation to Sign the %s Corporate CLA and add to approved list %s ",
- companyName, contributorEmail)
+ companyName, contributorModel.Email)
recipients := []string{designeeEmail}
body, err := emails.RenderV2ToCLAManagerDesigneeTemplate(repository, projectService, projectSFIDs,
emails.V2ToCLAManagerDesigneeTemplateParams{
RecipientName: designeeName,
- ContributorEmail: contributorEmail,
- ContributorName: contributorName,
+ Contributor: contributorModel,
CorporateConsole: corporateConsole,
}, emails.V2ToCLAManagerDesigneeTemplate, emails.V2ToCLAManagerDesigneeTemplateName)
@@ -224,7 +223,7 @@ func (s *service) SendEmailToCLAManagerDesignee(ctx context.Context, repository
}
}
-func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectSFIDs []string, foundationSFID, role string, corporateConsoleV2URL string) error {
+func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectSFIDs []string, foundationSFID, role string, corporateConsoleV2URL string, contributorModel emails.Contributor) error {
f := logrus.Fields{
"functionName": "cla_manager.service.SendDesigneeEmailToUserWithNoLFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -234,8 +233,8 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, project
"projectNames": strings.Join(projectNames, ","),
"role": role,
"corporateConsoleV2URL": corporateConsoleV2URL,
- "requesterUsername": requesterUsername,
- "requesterEmail": requesterEmail,
+ "requesterUsername": contributorModel.Username,
+ "requesterEmail": contributorModel.Email,
}
subject := "EasyCLA: Invitation to create LF Login and complete process of becoming CLA Manager"
@@ -243,8 +242,7 @@ func (s *service) SendDesigneeEmailToUserWithNoLFID(ctx context.Context, project
body, err := emails.RenderV2ToCLAManagerDesigneeTemplate(repository, projectService, projectSFIDs,
emails.V2ToCLAManagerDesigneeTemplateParams{
RecipientName: userWithNoLFIDName,
- ContributorEmail: requesterEmail,
- ContributorName: requesterUsername,
+ Contributor: contributorModel,
CorporateConsole: corporateConsoleV2URL,
}, emails.V2DesigneeToUserWithNoLFIDTemplate, emails.V2DesigneeToUserWithNoLFIDTemplateName)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index b6596b0bd..ce8acf7d8 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -16,6 +16,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/communitybridge/easycla/cla-backend-go/emails"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -95,8 +96,8 @@ type Service interface {
SendEmailToOrgAdmin(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, adminEmail string, adminName string, companyName string, projectName, projectSFID string, senderEmail string, senderName string, corporateConsole string)
ContributorEmailToOrgAdmin(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, adminEmail string, adminName string, companyName string, projectSFIDs []string, contributor *v1Models.User, corporateConsole string)
SendEmailToCLAManagerDesigneeCorporate(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectSFID string, projectName string, designeeEmail string, designeeName string, senderEmail string, senderName string)
- SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorID string, contributorName string)
- SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectIDs []string, foundationSFID, role string, corporateConsoleV2URL string) error
+ SendEmailToCLAManagerDesignee(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, corporateConsole string, companyName string, projectNames, projectSFIDs []string, designeeEmail string, designeeName string, contributorModel emails.Contributor)
+ SendDesigneeEmailToUserWithNoLFID(ctx context.Context, projectService project.Service, repository projects_cla_groups.Repository, userWithNoLFIDName, userWithNoLFIDEmail, organizationName, organizationID string, projectNames, projectIDs []string, foundationSFID, role string, corporateConsoleV2URL string, contributorModel emails.Contributor) error
SendEmailToUserWithNoLFID(ctx context.Context, repository projects_cla_groups.Repository, projectService project.Service, projectName, requesterUsername, requesterEmail, userWithNoLFIDName, userWithNoLFIDEmail, organizationID string, projectID *string, role, corporateConsole string) error
}
@@ -983,19 +984,21 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
// Get suggested CLA Manager user details
user, userErr := userService.SearchUserByEmail(userEmail)
if userErr != nil || (user != nil && user.Username == "") {
- contributorEmail, contributorUsername := "", ""
+ var contributorModel emails.Contributor
msg := fmt.Sprintf("UserEmail: %s has no LF Login and has been sent an invite email to create an account , error: %+v", userEmail, userErr)
log.Warn(msg)
// Get username and useremail details for contributor
if contributor.LFEmail != "" && contributor.UserName != "" {
- contributorEmail = contributor.LFEmail
- contributorUsername = contributor.UserName
+ contributorModel.Email = contributor.LFEmail
+ contributorModel.Username = contributor.LFUsername
+ contributorModel.EmailLabel = utils.EmailLabel
+ contributorModel.UsernameLabel = utils.UserLabel
} else {
- contributorUsername, contributorEmail = getContributorPublicEmail(contributor)
+ contributorModel = getContributorPublicEmail(contributor)
}
- sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectService, s.projectCGRepo, contributorUsername, contributorEmail, name, userEmail, organization.Name, organization.ID, projectSFs, projectSFIDs, foundationSFID, "cla-manager-designee", LfxPortalURL)
+ sendErr := s.SendDesigneeEmailToUserWithNoLFID(ctx, s.projectService, s.projectCGRepo, name, userEmail, organization.Name, organization.ID, projectSFs, projectSFIDs, foundationSFID, "cla-manager-designee", LfxPortalURL, contributorModel)
if sendErr != nil {
msg := fmt.Sprintf("Problem sending email to user: %s , error: %+v", userEmail, sendErr)
log.Warn(msg)
@@ -1030,13 +1033,20 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
log.Debugf("Sending Email to CLA Manager Designee email: %s ", userEmail)
+ var contributorModel emails.Contributor
+
if contributor.LFUsername != "" && contributor.LFEmail != "" && len(projectSFs) > 0 {
- s.SendEmailToCLAManagerDesignee(ctx, s.projectCGRepo, s.projectService, CorporateConsoleV2URL, organization.Name, projectSFs, projectSFIDs, userEmail, user.Name, contributor.LFEmail, contributor.LFUsername)
+ contributorModel.Email = contributor.LFEmail
+ contributorModel.Username = contributor.LFUsername
+ contributorModel.EmailLabel = utils.EmailLabel
+ contributorModel.UsernameLabel = utils.UserLabel
+
} else {
- contributorUserName, contributorEmail := getContributorPublicEmail(contributor)
- s.SendEmailToCLAManagerDesignee(ctx, s.projectCGRepo, s.projectService, CorporateConsoleV2URL, organization.Name, projectSFs, projectSFIDs, userEmail, user.Name, contributorUserName, contributorEmail)
+ contributorModel = getContributorPublicEmail(contributor)
}
+ s.SendEmailToCLAManagerDesignee(ctx, s.projectCGRepo, s.projectService, CorporateConsoleV2URL, organization.Name, projectSFs, projectSFIDs, userEmail, user.Name, contributorModel)
+
log.Debugf("CLA Manager designee created : %+v", designeeScopes)
return designeeScopes, nil
@@ -1152,29 +1162,33 @@ func getBestUserName(model *v1Models.User) string {
return "User Name Unknown"
}
-func getContributorPublicEmail(model *v1User.User) (string, string) {
- var contributorUserName, contributorEmail string
+func getContributorPublicEmail(model *v1User.User) emails.Contributor {
+ var contributorModel emails.Contributor
if model.LFUsername != "" {
- contributorUserName = model.LFUsername
+ contributorModel.Username = model.LFUsername
+ contributorModel.UsernameLabel = utils.UserLabel
}
if model.LFEmail != "" {
- contributorEmail = model.LFEmail
+ contributorModel.Email = model.LFEmail
+ contributorModel.EmailLabel = utils.EmailLabel
}
- if contributorUserName == "" {
- contributorUserName = model.UserGithubUsername
+ if contributorModel.Username == "" {
+ contributorModel.Username = model.UserGithubUsername
+ contributorModel.UsernameLabel = utils.GitHubUserLabel
}
- if contributorEmail == "" && len(model.UserEmails) > 0 {
+ if contributorModel.Email == "" && len(model.UserEmails) > 0 {
for _, email := range model.UserEmails {
if strings.Contains(email, "users.noreply.github.com") {
continue
}
- contributorEmail = email
+ contributorModel.Email = email
+ contributorModel.EmailLabel = utils.GitHubEmailLabel
}
}
- return contributorUserName, contributorEmail
+ return contributorModel
}
// getFormattedUserDetails is a helper function to extract what information we can from the user record for purposes of displaying the user's information
From 2f7c89770a7a895a45430575962d3d99bc6c7292 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 4 Mar 2021 09:57:15 -0800
Subject: [PATCH 0136/1276] Resolved IndexError When User Not Found in User
Service (#2742)
- Added defensive checks when the logged in user is not defined in the User Service. Now we handle the situation.
- Resolved logging error with trailing comma
- Added return types for User service helper functions -> returns List[dict] type helpers for IDE
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 76 ++++++++++++-----------
cla-backend/cla/user_service.py | 12 ++--
2 files changed, 47 insertions(+), 41 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index ad9c5f1bd..23f0480ab 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -936,7 +936,7 @@ def request_corporate_signature(self, auth_user: object,
f'project id: {project_id}, '
f'company id: {company_id}, '
f'signing entity name: {signing_entity_name}, '
- f'send email: {send_as_email}, ',
+ f'send email: {send_as_email}, '
f'signatory name: {signatory_name}, '
f'signatory email: {signatory_email}, '
)
@@ -951,7 +951,7 @@ def request_corporate_signature(self, auth_user: object,
return {'errors': {'company_id': 'request_corporate_signature - company_id is empty'}}
if auth_user is None:
- return {'errors': {'user_error': 'request_corporate_signature - auth_user is empty'}}
+ return {'errors': {'user_error': 'request_corporate_signature - auth_user object is empty'}}
if auth_user.username is None:
return {'errors': {'user_error': 'request_corporate_signature - auth_user.username is empty'}}
@@ -1016,43 +1016,49 @@ def request_corporate_signature(self, auth_user: object,
# unlikely we'll have more than one
cla_manager_user = users_list[0]
- # Add some defensive checks to ensure the Name and Email are set for the CLA Manager
+ # Add some defensive checks to ensure the Name and Email are set for the CLA Manager - lookup the values
+ # from the platform user service - use this as the source of truth
us = UserService
cla.log.debug(f'{fn} - Loading user by username: {auth_user.username} from the platform user service...')
platform_users = us.get_users_by_username(auth_user.username)
- if platform_users is None:
- cla.log.warning(f'{fn} - Unable to load auth_user by username: {auth_user.username}. '
- 'Returning an error response')
- return {'errors': {'user_error': 'user does not exist'}}
- platform_user = platform_users[0]
-
- if cla_manager_user.get_user_name() is None:
- # Lookup user in the platform user service...
- cla.log.warning(f'{fn} - Loaded CLA Manager by username: {auth_user.username}, but '
- 'the user_name is missing from profile - required for DocuSign.')
- user_name = platform_user.get('Name', None)
- if user_name:
- cla.log.debug(f'{fn} - user_name: {user_name} update for cla_manager : {auth_user.username}...')
- cla_manager_user.set_user_name(user_name)
- cla_manager_user.save()
- else:
- return {'errors': {'user_error': 'user does not have user_name'}}
+ if platform_users:
+ platform_user = platform_users[0]
- if cla_manager_user.get_user_email() is None:
- cla.log.warning(f'{fn} - Loaded CLA Manager by username: {auth_user.username}, but '
- 'the user email is missing from profile - required for DocuSign.')
- # Add the emails
- platform_user_emails = platform_user.get('Emails', None)
- if len(platform_user_emails) > 0:
- email_list = []
- for platform_email in platform_user_emails:
- email_list.append(platform_email['EmailAddress'])
- if platform_email['IsPrimary']:
- cla_manager_user.set_lf_email(platform_email['EmailAddress'])
- cla_manager_user.set_user_emails(email_list)
- cla_manager_user.save()
- else:
- return {'errors': {'user_error': 'user does not have an email'}}
+ if cla_manager_user.get_user_name() is None:
+ # Lookup user in the platform user service...
+ cla.log.warning(f'{fn} - Loaded CLA Manager by username: {auth_user.username}, but '
+ 'the user_name is missing from profile - required for DocuSign.')
+ user_name = platform_user.get('Name', None)
+ if user_name:
+ if cla_manager_user.get_user_name() != user_name:
+ cla.log.debug(f'{fn} - user_name: {user_name} update for cla_manager : {auth_user.username}...')
+ cla_manager_user.set_user_name(user_name)
+ cla_manager_user.save()
+ else:
+ cla.log.debug(f'{fn} - user_name values match - no need to update the local record')
+ else:
+ cla.log.warning(f'{fn} - Unable to locate the user\'s name from the platform user service model. '
+ 'Unable to update the local user record.')
+
+ if cla_manager_user.get_user_email() is None:
+ cla.log.warning(f'{fn} - Loaded CLA Manager by username: {auth_user.username}, but '
+ 'the user email is missing from profile - required for DocuSign.')
+ # Add the emails
+ platform_user_emails = platform_user.get('Emails', None)
+ if len(platform_user_emails) > 0:
+ email_list = []
+ for platform_email in platform_user_emails:
+ email_list.append(platform_email['EmailAddress'])
+ if platform_email['IsPrimary']:
+ cla_manager_user.set_lf_email(platform_email['EmailAddress'])
+ cla_manager_user.set_user_emails(email_list)
+ cla_manager_user.save()
+ else:
+ cla.log.warning(f'{fn} - Unable to locate the user\'s email from the platform user service model. '
+ 'Unable to update the local user record.')
+ else:
+ cla.log.warning(f'{fn} - Unable to load auth_user from the platform user service '
+ f'by username: {auth_user.username}. Unable to update our local user record.')
cla.log.debug(f'{fn} - Loaded user {cla_manager_user} - this is our CLA Manager')
# Ensure the project exists
diff --git a/cla-backend/cla/user_service.py b/cla-backend/cla/user_service.py
index 6c39e1a48..da8c42a34 100644
--- a/cla-backend/cla/user_service.py
+++ b/cla-backend/cla/user_service.py
@@ -52,7 +52,7 @@ def get_user_by_sf_id(self, sf_user_id: str):
log.warning(msg)
return None
- def _get_users_by_key_value(self, key: str, value: str):
+ def _get_users_by_key_value(self, key: str, value: str) -> List[dict]:
"""
Queries the platform user service for the specified criteria.
The result will return summary information for the users as a
@@ -65,7 +65,7 @@ def _get_users_by_key_value(self, key: str, value: str):
'accept': 'application/json'
}
- users = []
+ users: List[dict] = []
offset = 0
pagesize = 1000
@@ -91,16 +91,16 @@ def _get_users_by_key_value(self, key: str, value: str):
log.debug(f'{fn} - total users : {len(users)}')
return users
- def get_users_by_username(self, user_name: str):
+ def get_users_by_username(self, user_name: str) -> List[dict]:
return self._get_users_by_key_value("username", user_name)
- def get_users_by_firstname(self, first_name: str):
+ def get_users_by_firstname(self, first_name: str) -> List[dict]:
return self._get_users_by_key_value("firstname", first_name)
- def get_users_by_lastname(self, last_name: str):
+ def get_users_by_lastname(self, last_name: str) -> List[dict]:
return self._get_users_by_key_value("lastname", last_name)
- def get_users_by_email(self, email: str):
+ def get_users_by_email(self, email: str) -> List[dict]:
return self._get_users_by_key_value("email", email)
def has_role(self, username: str, role: str, organization_id: str, cla_group_id: str) -> bool:
From 6dba66a51c0d279f33f46cfe69151c8fd511cadd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 4 Mar 2021 12:29:35 -0800
Subject: [PATCH 0137/1276] Enabled Landing Page PROD v2 Links (#2743)
Signed-off-by: David Deal
---
.../cla-console-section.component.html | 4 ++--
.../cla-console-section.component.scss | 16 ++++++++--------
.../cla-console-section.component.ts | 10 +---------
3 files changed, 11 insertions(+), 19 deletions(-)
diff --git a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
index 03bb868a3..65907e703 100644
--- a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
+++ b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
@@ -80,8 +80,8 @@
- EasyCLA Version 2
+ [ngClass]="{'active':selectedVersion==='2'}">EasyCLA Version 2
+
This is a notification email from EasyCLA regarding the organization {{.CompanyName}}.
-
The following contributor would like to submit a contribution to the CLA Group {{.CLAGroupName}} and is requesting to be approved as a contributor for your organization:
+
The following contributor would like to submit a contribution to the projects(s): {{.GetProjectsOrProject}} and is requesting to be approved as a contributor for your organization:
{{.UserDetails}}
-
Approval can be done at {{.CorporateConsoleV2URL}}
+
Approval can be done at {{.CorporateConsoleV2URL}}. Visit any of the project(s):{{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}} and add the contributor to the approved list.
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
This is a notification email from EasyCLA regarding the project %s.
You have been %s %s the Approval List of %s for %s by CLA Manager %s. This means that %s.
-
If you had previously submitted a pull request to EasyCLA Test Group that had failed,
-you can now go back to it and follow the link to verify with your organization.
+
+
If you had previously submitted a pull request to EasyCLA Test Group that had failed, you can now go back to it, re-click the “Not Covered” button in the EasyCLA message in your pull request, and then follow these steps
+
+
Select “Corporate Contributor”.
+
Select your company from the organization drop down list
+
Click Proceed
+
+
These steps will confirm your organization association and you will only need to do these once. After completing these steps, the EasyCLA check will be complete and enabled for all future code contributions for this project.
This is a notification email from EasyCLA regarding the organization {{.CompanyName}}.
-
The following contributor would like to submit a contribution to the projects(s): {{.GetProjectsOrProject}} and is requesting to be approved as a contributor for your organization:
+
The following contributor would like to submit a contribution to the projects(s): {{.GetProjectsOrProject}} and is requesting to be added to the approval list as a contributor for your organization:
{{.UserDetails}}
Approval can be done at {{.CorporateConsoleV2URL}}. Visit any of the project(s):{{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}} and add the contributor to the approved list.
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.
You have been added to the Approval list of {{.CompanyName}} for {{.CLAGroupName}} by CLA Manager {{.Approver}}.
-
This means that you are authorized to contribute to the any of the following project(s) associated with the CLA Group {{.CLAGroupName}}: {{.GetProjects}}
+
This means that you are authorized to contribute to the any of the following project(s) associated with the CLA Group {{.CLAGroupName}}: {{.GetProjectsOrProject}}
If you had previously submitted a pull request to any any the above project(s) that had failed, you can now go back to it and follow the link to verify with your organization.
`
)
+// RenderApprovalListTemplate renders RenderApprovalListTemplate
+func RenderApprovalListTemplate(svc EmailTemplateService, projectSFIDs []string, params ApprovalListApprovedTemplateParams) (string, error) {
+ // prefill the projects data
+ claGroupParams, err := svc.GetCLAGroupTemplateParamsFromProjectSFID(utils.V2, projectSFIDs[0])
+ if err != nil {
+ return "", err
+ }
+ params.CLAGroupTemplateParams = claGroupParams
+
+ return RenderTemplate(utils.V2, ApprovalListApprovedTemplateName,
+ ApprovalListApprovedTemplate, params)
+}
+
// RequestToAuthorizeTemplateParams is email params for RequestToAuthorizeTemplate
type RequestToAuthorizeTemplateParams struct {
- CLAManagerTemplateParams
- ContributorName string
- ContributorEmail string
- OptionalMessage string
- CorporateConsoleURL string
- CompanyID string
+ CommonEmailParams
+ // This field is prefilled most of the time with EmailService
+ CLAGroupTemplateParams
+ CLAManagers []ClaManagerInfoParams
+ ContributorName string
+ ContributorEmail string
+ OptionalMessage string
+ CompanyID string
}
const (
@@ -77,7 +107,7 @@ const (
{{.OptionalMessage}}
{{end}}
If you want to add them to the Approved List, please
-log into the EasyCLA Corporate
+log into the EasyCLA Corporate
Console, where you can approve this user's request by selecting the 'Manage Approved List' and adding the
contributor's email, the contributor's entire email domain, their GitHub ID or the entire GitHub Organization for the
repository. This will permit them to begin contributing to {{.Project.ExternalProjectName}} on behalf of {{.CompanyName}}.
{{.RequesterName}} ({{.RequesterEmail}}) has requested to be added as another CLA Manager from {{.CompanyName}} for {{.Project.ExternalProjectName}}. This would permit them to maintain the
lists of approved contributors and CLA Managers as well.
If you want to permit this, please log into the EasyCLA Corporate Console,
select your company, then select the {{.Project.ExternalProjectName}} project. From the CLA Manager requests, you can approve this user as an
additional CLA Manager.
You have now been approved as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
company’s CLA Managers for {{.Project.ExternalProjectName}}.
To get started, please log into the EasyCLA Corporate Console, and select your
company and then the project {{.Project.ExternalProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
`
)
+// RenderRequestApprovedToRequesterTemplate renders the RemovedCLAManagerTemplate
+func RenderRequestApprovedToRequesterTemplate(svc EmailTemplateService, claGroupModelVersion, projectSFID string, params RequestApprovedToRequesterTemplateParams) (string, error) {
+ claGroupParams, err := svc.GetCLAGroupTemplateParamsFromProjectSFID(claGroupModelVersion, projectSFID)
+ if err != nil {
+ return "", err
+ }
+ params.CLAGroupTemplateParams = claGroupParams
+
+ return RenderTemplate(claGroupModelVersion, RequestApprovedToRequesterTemplateName, RequestApprovedToRequesterTemplate, params)
+}
+
// RequestDeniedToCLAManagersTemplateParams is email params for RequestDeniedToCLAManagersTemplate
type RequestDeniedToCLAManagersTemplateParams struct {
- CLAManagerTemplateParams
+ CommonEmailParams
+ CLAGroupTemplateParams
RequesterName string
RequesterEmail string
}
@@ -139,9 +166,21 @@ be able to maintain the list of employees allowed to contribute to {{.Project.Ex
`
)
+// RenderRequestDeniedToCLAManagersTemplate renders the RemovedCLAManagerTemplate
+func RenderRequestDeniedToCLAManagersTemplate(svc EmailTemplateService, claGroupModelVersion, projectSFID string, params RequestDeniedToCLAManagersTemplateParams) (string, error) {
+ claGroupParams, err := svc.GetCLAGroupTemplateParamsFromProjectSFID(claGroupModelVersion, projectSFID)
+ if err != nil {
+ return "", err
+ }
+ params.CLAGroupTemplateParams = claGroupParams
+
+ return RenderTemplate(claGroupModelVersion, RequestDeniedToCLAManagersTemplateName, RequestDeniedToCLAManagersTemplate, params)
+}
+
// RequestDeniedToRequesterTemplateParams is email params for RequestDeniedToRequesterTemplate
type RequestDeniedToRequesterTemplateParams struct {
- CLAManagerTemplateParams
+ CommonEmailParams
+ CLAGroupTemplateParams
}
const (
@@ -156,40 +195,53 @@ list of employees allowed to contribute to {{.Project.ExternalProjectName}} on b
`
)
-// ClaManagerAddedEToUserTemplateParams is email params for ClaManagerAddedEToUserTemplate
+// RenderRequestDeniedToRequesterTemplate renders the RemovedCLAManagerTemplate
+func RenderRequestDeniedToRequesterTemplate(svc EmailTemplateService, claGroupModelVersion, projectSFID string, params RequestDeniedToRequesterTemplateParams) (string, error) {
+ claGroupParams, err := svc.GetCLAGroupTemplateParamsFromProjectSFID(claGroupModelVersion, projectSFID)
+ if err != nil {
+ return "", err
+ }
+ params.CLAGroupTemplateParams = claGroupParams
+
+ return RenderTemplate(claGroupModelVersion, RequestDeniedToRequesterTemplateName, RequestDeniedToRequesterTemplate, params)
+}
+
+// ClaManagerAddedEToUserTemplateParams is email params
type ClaManagerAddedEToUserTemplateParams struct {
- CLAManagerTemplateParams
- CorporateURL string
+ CommonEmailParams
+ CLAGroupTemplateParams
}
const (
- // ClaManagerAddedEToUserTemplateName is email template name for ClaManagerAddedEToUserTemplate
- ClaManagerAddedEToUserTemplateName = "ClaManagerAddedEToUserTemplate"
- // ClaManagerAddedEToUserTemplate is email template for
+ // ClaManagerAddedEToUserTemplateName is template name of ClaManagerAddedEToUserTemplate
+ ClaManagerAddedEToUserTemplateName = "V2ClaManagerAddedEToUserTemplate"
+ //ClaManagerAddedEToUserTemplate email template for cla manager v2
ClaManagerAddedEToUserTemplate = `
Hello {{.RecipientName}},
-
This is a notification email from EasyCLA regarding the project {{.CLAGroupName}}.
-
You have been added as a CLA Manager from {{.CompanyName}} for the project {{.CLAGroupName}}. This means that you can now maintain the
-list of employees allowed to contribute to {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
-company’s CLA Managers for {{.CLAGroupName}}.
-
To get started, please log into the EasyCLA Corporate Console, and select your
-company and then the project {{.CLAGroupName}}. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the project {{.Project.ExternalProjectName}} and CLA Group {{.CLAGroupName}}.
You have been added as a CLA Manager for the organization {{.CompanyName}} and the project {{.Project.ExternalProjectName}}. This means that you can now maintain the
list of employees allowed to contribute to the project {{.Project.ExternalProjectName}} on behalf of your company, as well as view and manage the list of your
company’s CLA Managers for the CLA Group {{.CLAGroupName}}.
To get started, please log into the EasyCLA Corporate Console, and select your
company and then the project {{.Project.ExternalProjectName}}. From here you will be able to edit the list of approved employees and CLA Managers.
This is a notification email from EasyCLA regarding the organization {{.CompanyName}}.
The following contributor would like to submit a contribution to the projects(s): {{.GetProjectsOrProject}} and is requesting to be added to the approval list as a contributor for your organization:
{{.UserDetails}}
-
Approval can be done at {{.CorporateConsole}}. Visit any of the project(s):{{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}} and add the contributor to the approved list.
+
Approval can be done at {{.CorporateConsole}}. Visit any of the project(s):{{range $index, $projectName := .Projects}}{{if $index}},{{end}}{{$projectName.GetProjectFullURL}}{{end}} and add the contributor to the approved list.
Please notify the contributor once they are added to the approved list of contributors so that they can complete their contribution.
This is a notification email from EasyCLA regarding the CLA setup and signing process for the organization {{.CompanyName}}.
-
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for the organization {{.CompanyName}} in support of the following project(s):
+
{{.SenderName}} {{.SenderEmail}} has identified you as a potential candidate to setup the Corporate CLA for the organization {{.CompanyName}} in support of the following project(s):
{{.Project.ExternalProjectName}}
diff --git a/cla-backend-go/utils/email.go b/cla-backend-go/utils/email.go
index 476b3d25b..bf3758f34 100644
--- a/cla-backend-go/utils/email.go
+++ b/cla-backend-go/utils/email.go
@@ -5,7 +5,6 @@ package utils
import (
"errors"
- "fmt"
"strings"
"github.com/sirupsen/logrus"
@@ -103,7 +102,7 @@ func GetCorporateURL(isV2Project bool) string {
if isV2Project {
return config.GetConfig().CorporateConsoleV2URL
}
- return fmt.Sprintf("https://%s", config.GetConfig().CorporateConsoleURL)
+ return config.GetConfig().CorporateConsoleV1URL
}
// GetEmailHelpContent returns the standard email help paragraph details.
From c6a1779fc783454db0060f446149bc4762d44d5a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 10 Mar 2021 17:50:30 -0800
Subject: [PATCH 0154/1276] Resolved Nil Pointer
- Added missing services in cmd/server.go and email templates
- Updated logging
Signed-off-by: David Deal
---
cla-backend-go/approval_list/handlers.go | 29 ++++--
cla-backend-go/approval_list/service.go | 111 ++++++++++++++---------
cla-backend-go/auth/authorizer.go | 4 +-
cla-backend-go/cmd/server.go | 18 ++--
cla-backend-go/emails/prefill.go | 3 +-
5 files changed, 104 insertions(+), 61 deletions(-)
diff --git a/cla-backend-go/approval_list/handlers.go b/cla-backend-go/approval_list/handlers.go
index 932cafbc8..c8df6a62d 100644
--- a/cla-backend-go/approval_list/handlers.go
+++ b/cla-backend-go/approval_list/handlers.go
@@ -27,7 +27,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
func(params company.AddCclaWhitelistRequestParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- requestID, err := service.AddCclaWhitelistRequest(ctx, params.CompanyID, params.ProjectID, params.Body)
+ requestID, err := service.AddCclaApprovalListRequest(ctx, params.CompanyID, params.ProjectID, params.Body)
if err != nil {
return company.NewAddCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
@@ -47,7 +47,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
func(params company.ApproveCclaWhitelistRequestParams, claUser *user.CLAUser) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- err := service.ApproveCclaWhitelistRequest(ctx, claUser, params.CompanyID, params.ProjectID, params.RequestID)
+ err := service.ApproveCclaApprovalListRequest(ctx, claUser, params.CompanyID, params.ProjectID, params.RequestID)
if err != nil {
return company.NewApproveCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
@@ -67,7 +67,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
func(params company.RejectCclaWhitelistRequestParams, claUser *user.CLAUser) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- err := service.RejectCclaWhitelistRequest(ctx, params.CompanyID, params.ProjectID, params.RequestID)
+ err := service.RejectCclaApprovalListRequest(ctx, params.CompanyID, params.ProjectID, params.RequestID)
if err != nil {
return company.NewRejectCclaWhitelistRequestBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
@@ -93,7 +93,7 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
}
log.WithFields(f).Debugf("Invoking ListCclaApprovalListRequests with Company ID: %+v, Project ID: %+v, Status: %+v",
params.CompanyID, params.ProjectID, params.Status)
- result, err := service.ListCclaWhitelistRequest(params.CompanyID, params.ProjectID, params.Status)
+ result, err := service.ListCclaApprovalListRequest(params.CompanyID, params.ProjectID, params.Status)
if err != nil {
return company.NewListCclaWhitelistRequestsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
@@ -103,9 +103,22 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
api.CompanyListCclaWhitelistRequestsByCompanyAndProjectHandler = company.ListCclaWhitelistRequestsByCompanyAndProjectHandlerFunc(
func(params company.ListCclaWhitelistRequestsByCompanyAndProjectParams, claUser *user.CLAUser) middleware.Responder {
- log.Debugf("Invoking ListCclaWhitelistRequestByCompanyProjectUser with Company ID: %+v, Project ID: %+v, Status: %+v",
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "v1.approval_list.handlers.CompanyListCclaWhitelistRequestsByCompanyAndProjectHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": params.CompanyID,
+ "projectID": params.ProjectID,
+ "status": utils.StringValue(params.Status),
+ "claUserName": claUser.Name,
+ "claUserUserID": claUser.UserID,
+ "claUserLFEmail": claUser.LFEmail,
+ "claUserLFUsername": claUser.LFUsername,
+ }
+ log.WithFields(f).Debugf("Invoking ListCclaApprovalListRequestByCompanyProjectUser with Company ID: %+v, Project ID: %+v, Status: %+v",
params.CompanyID, params.ProjectID, params.Status)
- result, err := service.ListCclaWhitelistRequestByCompanyProjectUser(params.CompanyID, ¶ms.ProjectID, params.Status, nil)
+ result, err := service.ListCclaApprovalListRequestByCompanyProjectUser(params.CompanyID, ¶ms.ProjectID, params.Status, nil)
if err != nil {
return company.NewListCclaWhitelistRequestsByCompanyAndProjectBadRequest().WithPayload(errorResponse(err))
}
@@ -115,9 +128,9 @@ func Configure(api *operations.ClaAPI, service IService, sessionStore *dynastore
api.CompanyListCclaWhitelistRequestsByCompanyAndProjectAndUserHandler = company.ListCclaWhitelistRequestsByCompanyAndProjectAndUserHandlerFunc(
func(params company.ListCclaWhitelistRequestsByCompanyAndProjectAndUserParams, claUser *user.CLAUser) middleware.Responder {
- log.Debugf("Invoking ListCclaWhitelistRequestByCompanyProjectUser with Company ID: %+v, Project ID: %+v, Status: %+v, User: %+v",
+ log.Debugf("Invoking ListCclaApprovalListRequestByCompanyProjectUser with Company ID: %+v, Project ID: %+v, Status: %+v, User: %+v",
params.CompanyID, params.ProjectID, params.Status, claUser.LFUsername)
- result, err := service.ListCclaWhitelistRequestByCompanyProjectUser(params.CompanyID, ¶ms.ProjectID, params.Status, &claUser.LFUsername)
+ result, err := service.ListCclaApprovalListRequestByCompanyProjectUser(params.CompanyID, ¶ms.ProjectID, params.Status, &claUser.LFUsername)
if err != nil {
return company.NewListCclaWhitelistRequestsByCompanyAndProjectAndUserBadRequest().WithPayload(errorResponse(err))
}
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index 67ea33315..4bf7077c1 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -40,11 +40,11 @@ const (
// IService interface defines the service methods/functions
type IService interface {
- AddCclaWhitelistRequest(ctx context.Context, companyID string, claGroupID string, args models.CclaWhitelistRequestInput) (string, error)
- ApproveCclaWhitelistRequest(ctx context.Context, claUser *user.CLAUser, ClacompanyID, claGroupID, requestID string) error
- RejectCclaWhitelistRequest(ctx context.Context, companyID, claGroupID, requestID string) error
- ListCclaWhitelistRequest(companyID string, claGroupID, status *string) (*models.CclaWhitelistRequestList, error)
- ListCclaWhitelistRequestByCompanyProjectUser(companyID string, claGroupID, status, userID *string) (*models.CclaWhitelistRequestList, error)
+ AddCclaApprovalListRequest(ctx context.Context, companyID string, claGroupID string, args models.CclaWhitelistRequestInput) (string, error)
+ ApproveCclaApprovalListRequest(ctx context.Context, claUser *user.CLAUser, ClacompanyID, claGroupID, requestID string) error
+ RejectCclaApprovalListRequest(ctx context.Context, companyID, claGroupID, requestID string) error
+ ListCclaApprovalListRequest(companyID string, claGroupID, status *string) (*models.CclaWhitelistRequestList, error)
+ ListCclaApprovalListRequestByCompanyProjectUser(companyID string, claGroupID, status, userID *string) (*models.CclaWhitelistRequestList, error)
}
type service struct {
@@ -61,54 +61,75 @@ type service struct {
}
// NewService creates a new approval list service
-func NewService(repo IRepository, projectsCLAGroupRepository projects_cla_groups.Repository, projService project.Service, userRepo users.UserRepository, companyRepo company.IRepository, projectRepo project.ProjectRepository, signatureRepo signatures.SignatureRepository, corpConsoleURL string, httpClient *http.Client) IService {
+func NewService(repo IRepository, projectsCLAGroupRepository projects_cla_groups.Repository, projService project.Service, userRepo users.UserRepository, companyRepo company.IRepository, projectRepo project.ProjectRepository, signatureRepo signatures.SignatureRepository, emailTemplateService emails.EmailTemplateService, corpConsoleURL string, httpClient *http.Client) IService {
return service{
repo: repo,
- projectsCLAGroupRepository: projectsCLAGroupRepository,
projectService: projService,
userRepo: userRepo,
companyRepo: companyRepo,
projectRepo: projectRepo,
signatureRepo: signatureRepo,
+ projectsCLAGroupRepository: projectsCLAGroupRepository,
+ emailTemplateService: emailTemplateService,
corpConsoleURL: corpConsoleURL,
httpClient: httpClient,
}
}
-func (s service) AddCclaWhitelistRequest(ctx context.Context, companyID string, claGroupID string, args models.CclaWhitelistRequestInput) (string, error) {
- list, err := s.ListCclaWhitelistRequestByCompanyProjectUser(companyID, &claGroupID, nil, &args.ContributorID)
+func (s service) AddCclaApprovalListRequest(ctx context.Context, companyID string, claGroupID string, args models.CclaWhitelistRequestInput) (string, error) {
+ f := logrus.Fields{
+ "functionName": "v1.approval_list.service.AddCclaApprovalListRequest",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": companyID,
+ "claGroupID": claGroupID,
+ "RecipientName": args.RecipientName,
+ "RecipientEmail": args.RecipientEmail,
+ "ContributorID": args.ContributorID,
+ "ContributorName": args.ContributorName,
+ "ContributorEmail": args.ContributorEmail,
+ "Message": args.Message,
+ }
+
+ list, err := s.ListCclaApprovalListRequestByCompanyProjectUser(companyID, &claGroupID, nil, &args.ContributorID)
if err != nil {
- log.Warnf("AddCclaApprovalRequest - error looking up existing contributor invite requests for company: %s, project: %s, user by id: %s with name: %s, email: %s, error: %+v",
+ log.WithFields(f).WithError(err).Warnf("error looking up existing contributor invite requests for company: %s, project: %s, user by id: %s with name: %s, email: %s, error: %+v",
companyID, claGroupID, args.ContributorID, args.ContributorName, args.ContributorEmail, err)
return "", err
}
for _, item := range list.List {
if item.RequestStatus == "pending" || item.RequestStatus == "approved" {
- log.Warnf("AddCclaApprovalRequest - found existing contributor invite - id: %s, request for company: %s, project: %s, user by id: %s with name: %s, email: %s",
+ log.WithFields(f).Warnf("found existing contributor invite - id: %s, request for company: %s, project: %s, user by id: %s with name: %s, email: %s",
list.List[0].RequestID, companyID, claGroupID, args.ContributorID, args.ContributorName, args.ContributorEmail)
return "", ErrCclaApprovalRequestAlreadyExists
}
}
companyModel, err := s.companyRepo.GetCompany(ctx, companyID)
if err != nil {
- log.Warnf("AddCclaApprovalRequest - unable to lookup company by id: %s, error: %+v", companyID, err)
+ log.WithFields(f).Warnf("unable to lookup company by id: %s, error: %+v", companyID, err)
return "", err
}
claGroupModel, err := s.projectRepo.GetCLAGroupByID(ctx, claGroupID, DontLoadRepoDetails)
if err != nil {
- log.Warnf("AddCclaApprovalRequest - unable to lookup project by id: %s, error: %+v", claGroupID, err)
+ log.WithFields(f).Warnf("unable to lookup project by id: %s, error: %+v", claGroupID, err)
return "", err
}
+
+ log.WithFields(f).Debugf("looking up user by user ID: %s", args.ContributorID)
userModel, err := s.userRepo.GetUser(args.ContributorID)
- if err != nil {
- log.Warnf("AddCclaApprovalRequest - unable to lookup user by id: %s with name: %s, email: %s, error: %+v",
+ if err != nil || userModel == nil {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup user by id: %s with name: %s, email: %s, error: %+v",
args.ContributorID, args.ContributorName, args.ContributorEmail, err)
- return "", err
- }
- if userModel == nil {
- log.Warnf("AddCclaApprovalRequest - unable to lookup user by id: %s with name: %s, email: %s, error: user object not found",
- args.ContributorID, args.ContributorName, args.ContributorEmail)
- return "", errors.New("invalid user")
+
+ log.WithFields(f).Debugf("looking up user by user email: %s", args.ContributorEmail)
+ userModel, err = s.userRepo.GetUserByEmail(args.ContributorEmail)
+ if err != nil || userModel == nil {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup user by email: %s with name: %s, error: %+v",
+ args.ContributorName, args.ContributorEmail, err)
+ if err != nil {
+ return "", err
+ }
+ return "", errors.New("invalid user")
+ }
}
signed, approved := true, true
@@ -116,14 +137,14 @@ func (s service) AddCclaWhitelistRequest(ctx context.Context, companyID string,
pageSize := int64(5)
sig, sigErr := s.signatureRepo.GetProjectCompanySignatures(ctx, companyID, claGroupID, &signed, &approved, nil, &sortOrder, &pageSize)
if sigErr != nil || sig == nil || sig.Signatures == nil {
- log.Warnf("AddCclaApprovalRequest - unable to lookup signature by company id: %s project id: %s - (or no managers), sig: %+v, error: %+v",
+ log.WithFields(f).Warnf("unable to lookup signature by company id: %s project id: %s - (or no managers), sig: %+v, error: %+v",
companyID, claGroupID, sig, err)
return "", err
}
requestID, addErr := s.repo.AddCclaApprovalRequest(companyModel, claGroupModel, userModel, args.ContributorName, args.ContributorEmail)
if addErr != nil {
- log.Warnf("AddCclaApprovalRequest - unable to add Approval Request for id: %s with name: %s, email: %s, error: %+v",
+ log.WithFields(f).Warnf("unable to add Approval Request for id: %s with name: %s, email: %s, error: %+v",
args.ContributorID, args.ContributorName, args.ContributorEmail, addErr)
}
@@ -133,16 +154,16 @@ func (s service) AddCclaWhitelistRequest(ctx context.Context, companyID string,
return requestID, nil
}
-// ApproveCclaWhitelistRequest is the handler for the approve CLA request
-func (s service) ApproveCclaWhitelistRequest(ctx context.Context, claUser *user.CLAUser, companyID, claGroupID, requestID string) error {
-
+// ApproveCclaApprovalListRequest is the handler for the approve CLA request
+func (s service) ApproveCclaApprovalListRequest(ctx context.Context, claUser *user.CLAUser, companyID, claGroupID, requestID string) error {
f := logrus.Fields{
- "functionName": "ApproveCclaApprovalListRequest",
+ "functionName": "v1.approval_list.service.ApproveCclaApprovalListRequest",
"companyID": companyID,
"claGroupID": claGroupID,
"requestID": requestID,
"Approver": claUser.Name,
}
+
err := s.repo.ApproveCclaApprovalListRequest(requestID)
if err != nil {
log.WithFields(f).Warnf("ApproveCclaApprovalListRequest - problem updating approved list with 'approved' status for request: %s, error: %+v",
@@ -219,28 +240,35 @@ func (s service) ApproveCclaWhitelistRequest(ctx context.Context, claUser *user.
return nil
}
-// RejectCclaWhitelistRequest is the handler for the decline CLA request
-func (s service) RejectCclaWhitelistRequest(ctx context.Context, companyID, claGroupID, requestID string) error {
+// RejectCclaApprovalListRequest is the handler for the decline CLA request
+func (s service) RejectCclaApprovalListRequest(ctx context.Context, companyID, claGroupID, requestID string) error {
+ f := logrus.Fields{
+ "functionName": "v1.approval_list.service.RejectCclaApprovalListRequest",
+ "companyID": companyID,
+ "claGroupID": claGroupID,
+ "requestID": requestID,
+ }
+
err := s.repo.RejectCclaApprovalListRequest(requestID)
if err != nil {
- log.Warnf("RejectCclaApprovalListRequest - problem updating approved list with 'rejected' status for request: %s, error: %+v", requestID, err)
+ log.WithFields(f).WithError(err).Warnf("problem updating approved list with 'rejected' status for request: %s, error: %+v", requestID, err)
return err
}
requestModel, err := s.repo.GetCclaApprovalListRequest(requestID)
if err != nil {
- log.Warnf("RejectCclaApprovalListRequest - unable to lookup request by id: %s, error: %+v", requestID, err)
+ log.WithFields(f).WithError(err).Warnf("unable to lookup request by id: %s, error: %+v", requestID, err)
return err
}
companyModel, err := s.companyRepo.GetCompany(ctx, companyID)
if err != nil {
- log.Warnf("RejectCclaApprovalListRequest - unable to lookup company by id: %s, error: %+v", companyID, err)
+ log.WithFields(f).WithError(err).Warnf("unable to lookup company by id: %s, error: %+v", companyID, err)
return err
}
claGroupModel, err := s.projectRepo.GetCLAGroupByID(ctx, claGroupID, DontLoadRepoDetails)
if err != nil {
- log.Warnf("RejectCclaApprovalListRequest - unable to lookup project by id: %s, error: %+v", claGroupID, err)
+ log.WithFields(f).WithError(err).Warnf("unable to lookup project by id: %s, error: %+v", claGroupID, err)
return err
}
@@ -249,15 +277,15 @@ func (s service) RejectCclaWhitelistRequest(ctx context.Context, companyID, claG
pageSize := int64(5)
sig, sigErr := s.signatureRepo.GetProjectCompanySignatures(ctx, companyID, claGroupID, &signed, &approved, nil, &sortOrder, &pageSize)
if sigErr != nil || sig == nil || sig.Signatures == nil {
- log.Warnf("RejectCclaApprovalListRequest - unable to lookup signature by company id: %s project id: %s - (or no managers), sig: %+v, error: %+v",
+ log.WithFields(f).WithError(sigErr).Warnf("unable to lookup signature by company id: %s project id: %s - (or no managers), sig: %+v, error: %+v",
companyID, claGroupID, sig, err)
return err
}
if requestModel.UserEmails == nil {
- msg := fmt.Sprintf("RejectCclaApprovalListRequest - unable to send approval email - email missing for request: %+v, error: %+v",
+ msg := fmt.Sprintf("unable to send approval email - email missing for request: %+v, error: %+v",
requestModel, err)
- log.Warnf(msg)
+ log.WithFields(f).Warnf(msg)
return errors.New(msg)
}
@@ -271,13 +299,13 @@ func (s service) RejectCclaWhitelistRequest(ctx context.Context, companyID, claG
return nil
}
-// ListCclaWhitelistRequest is the handler for the list CLA request
-func (s service) ListCclaWhitelistRequest(companyID string, claGroupID, status *string) (*models.CclaWhitelistRequestList, error) {
+// ListCclaApprovalListRequest is the handler for the list CLA request
+func (s service) ListCclaApprovalListRequest(companyID string, claGroupID, status *string) (*models.CclaWhitelistRequestList, error) {
return s.repo.ListCclaApprovalListRequests(companyID, claGroupID, status, nil)
}
-// ListCclaWhitelistRequestByCompanyProjectUser is the handler for the list CLA request
-func (s service) ListCclaWhitelistRequestByCompanyProjectUser(companyID string, claGroupID, status, userID *string) (*models.CclaWhitelistRequestList, error) {
+// ListCclaApprovalListRequestByCompanyProjectUser is the handler for the list CLA request
+func (s service) ListCclaApprovalListRequestByCompanyProjectUser(companyID string, claGroupID, status, userID *string) (*models.CclaWhitelistRequestList, error) {
return s.repo.ListCclaApprovalListRequests(companyID, claGroupID, status, userID)
}
@@ -401,9 +429,8 @@ func (s service) sendRequestRejectedEmailToRecipient(emailParams emails.CommonEm
}
func (s service) sendRequestApprovedEmailToRecipient(ctx context.Context, emailParams emails.CommonEmailParams, claUser user.CLAUser, projectSFIDs []string) {
-
f := logrus.Fields{
- "functionName": "sendRequestApprovedEmailToRecipient",
+ "functionName": "v1.approval_list.service.sendRequestApprovedEmailToRecipient",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": emailParams.CompanyName,
"recipientName": emailParams.RecipientName,
diff --git a/cla-backend-go/auth/authorizer.go b/cla-backend-go/auth/authorizer.go
index c973ed894..49569d2b3 100644
--- a/cla-backend-go/auth/authorizer.go
+++ b/cla-backend-go/auth/authorizer.go
@@ -5,6 +5,7 @@ package auth
import (
"errors"
+ "fmt"
"strings"
"github.com/sirupsen/logrus"
@@ -50,7 +51,7 @@ func NewAuthorizer(authValidator Validator, userPermissioner UserPermissioner) A
// SecurityAuth creates a new CLA user based on the token and scopes
func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser, error) {
f := logrus.Fields{
- "functionName": "auth.SecurityAuth",
+ "functionName": "auth.authorizer.SecurityAuth",
"scopes": strings.Join(scopes, ","),
}
// This handler is called by the runtime whenever a route needs authentication
@@ -68,6 +69,7 @@ func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser,
}
return nil, err
}
+ f["claims"] = fmt.Sprintf("%+v", claims)
// Get the username from the token claims
usernameClaim, ok := claims[a.authValidator.usernameClaim]
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 04d488cbe..a56c20175 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -238,7 +238,7 @@ func server(localMode bool) http.Handler {
v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo)
projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
- projectRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
+ v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
eventsRepo := events.NewRepository(awsSession, stage)
metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, projectClaGroupRepo)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
@@ -248,7 +248,7 @@ func server(localMode bool) http.Handler {
eventsService := events.NewService(eventsRepo, combinedRepo{
usersRepo,
v1CompanyRepo,
- projectRepo,
+ v1CLAGroupRepo,
projectClaGroupRepo,
})
@@ -263,19 +263,19 @@ func server(localMode bool) http.Handler {
usersService := users.NewService(usersRepo, eventsService)
healthService := health.New(Version, Commit, Branch, BuildDate)
templateService := template.NewService(stage, templateRepo, docraptorClient, awsSession)
- v1ProjectService := project.NewService(projectRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo)
- emailTemplateService := emails.NewEmailTemplateService(projectClaGroupRepo, v1ProjectService, configFile.CorporateConsoleV1URL, configFile.CorporateConsoleV2URL)
- v2ProjectService := v2Project.NewService(v1ProjectService, projectRepo, projectClaGroupRepo)
+ v1ProjectService := project.NewService(v1CLAGroupRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo)
+ emailTemplateService := emails.NewEmailTemplateService(v1CLAGroupRepo, projectClaGroupRepo, v1ProjectService, configFile.CorporateConsoleV1URL, configFile.CorporateConsoleV2URL)
+ v2ProjectService := v2Project.NewService(v1ProjectService, v1CLAGroupRepo, projectClaGroupRepo)
v1CompanyService := v1Company.NewService(v1CompanyRepo, configFile.CorporateConsoleV1URL, userRepo, usersService)
- v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, projectRepo, usersRepo, v1CompanyRepo, projectClaGroupRepo, eventsService)
- v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, projectRepo, projectClaGroupRepo, v1CompanyService)
+ v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, v1CLAGroupRepo, usersRepo, v1CompanyRepo, projectClaGroupRepo, eventsService)
+ v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, projectClaGroupRepo, v1CompanyService)
v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, projectClaGroupRepo)
v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, projectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
v2ClaManagerService := v2ClaManager.NewService(emailTemplateService, v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
- v1ApprovalListService := approval_list.NewService(approvalListRepo, projectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, projectRepo, signaturesRepo, configFile.CorporateConsoleV2URL, http.DefaultClient)
+ v1ApprovalListService := approval_list.NewService(approvalListRepo, projectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, v1CLAGroupRepo, signaturesRepo, emailTemplateService, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
@@ -311,7 +311,7 @@ func server(localMode bool) http.Handler {
v2Template.Configure(v2API, templateService, eventsService)
github.Configure(api, configFile.GitHub.ClientID, configFile.GitHub.ClientSecret, configFile.GitHub.AccessToken, sessionStore)
signatures.Configure(api, v1SignaturesService, sessionStore, eventsService)
- v2Signatures.Configure(v2API, v1ProjectService, projectRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
+ v2Signatures.Configure(v2API, v1ProjectService, v1CLAGroupRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
approval_list.Configure(api, v1ApprovalListService, sessionStore, v1SignaturesService, eventsService)
v1Company.Configure(api, v1CompanyService, usersService, companyUserValidation, eventsService)
docs.Configure(api)
diff --git a/cla-backend-go/emails/prefill.go b/cla-backend-go/emails/prefill.go
index c4a87ab9f..3dc3fba90 100644
--- a/cla-backend-go/emails/prefill.go
+++ b/cla-backend-go/emails/prefill.go
@@ -29,8 +29,9 @@ type emailTemplateServiceProvider struct {
}
// NewEmailTemplateService creates a new instance of email template service
-func NewEmailTemplateService(repository projects_cla_groups.Repository, projectService project.Service, corporateConsoleV1, corporateConsoleV2 string) EmailTemplateService {
+func NewEmailTemplateService(claGroupRepository project.ProjectRepository, repository projects_cla_groups.Repository, projectService project.Service, corporateConsoleV1, corporateConsoleV2 string) EmailTemplateService {
return &emailTemplateServiceProvider{
+ claGroupRepository: claGroupRepository,
repository: repository,
projectService: projectService,
corporateConsoleV1: corporateConsoleV1,
From 4802de32ec18c426333f059450ae3b3362c6c0a4 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Thu, 11 Mar 2021 11:14:06 +0200
Subject: [PATCH 0155/1276] don't fail if foundation sfid is not set properly
Signed-off-by: makkalot
---
cla-backend-go/emails/prefill.go | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/emails/prefill.go b/cla-backend-go/emails/prefill.go
index 3dc3fba90..790a1ef14 100644
--- a/cla-backend-go/emails/prefill.go
+++ b/cla-backend-go/emails/prefill.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
@@ -126,9 +128,12 @@ func (s *emailTemplateServiceProvider) getV1CLAGroupTemplateParamsFromProjectSFI
return CLAGroupTemplateParams{}, fmt.Errorf("project service lookup error for SFID: %s, error : %+v", projectSFID, projectErr)
}
- signedResult, err := s.projectService.SignedAtFoundationLevel(context.Background(), claGroup.FoundationSFID)
- if err != nil {
- return CLAGroupTemplateParams{}, fmt.Errorf("fetching the SignedAtFoundationLevel for foundation : %s failed : %v", claGroup.FoundationSFID, err)
+ var signedResult bool
+ if claGroup.FoundationSFID != "" {
+ signedResult, err = s.projectService.SignedAtFoundationLevel(context.Background(), claGroup.FoundationSFID)
+ if err != nil {
+ log.Warnf("fetching the SignedAtFoundationLevel for foundation : %s failed : %v skipping assigning in email params", claGroup.FoundationSFID, err)
+ }
}
return CLAGroupTemplateParams{
From d99f9ef907b59421b349672454862d764171aaf3 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Thu, 11 Mar 2021 17:33:36 +0300
Subject: [PATCH 0156/1276] Bug/Corporate Console Events
- Updated log events with right parent project details(standalone, multi level types)
- Updated query using parent_project index in the search query
Signed-off-by: wanyaland
---
cla-backend-go/events/repository.go | 18 +++++++++---------
cla-backend-go/events/service.go | 14 +++++++++++---
cla-backend-go/v2/events/handlers.go | 7 ++++---
3 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index f6a95c284..62e531950 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -359,12 +359,15 @@ func (repo *repository) queryEventsTable(indexName string, condition expression.
ExpressionAttributeValues: expr.Values(),
KeyConditionExpression: expr.KeyCondition(),
ProjectionExpression: expr.Projection(),
- FilterExpression: expr.Filter(),
TableName: aws.String(tableName),
IndexName: aws.String(indexName),
ScanIndexForward: aws.Bool(false),
}
+ if filter != nil {
+ queryInput.FilterExpression = expr.Filter()
+ }
+
if all {
pageSize = aws.Int64(HugePageSize)
} else {
@@ -497,15 +500,12 @@ func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, found
"paramPageSize": utils.Int64Value(paramPageSize),
"loadAll": all,
}
- key := fmt.Sprintf("%s#%s", companySFID, foundationSFID)
- log.WithFields(f).Debugf("adding key condition of 'company_sfid_foundation_sfid = %s'", key)
- keyCondition := expression.Key("company_sfid_foundation_sfid").Equal(expression.Value(key))
+ log.WithFields(f).Debugf("adding key condition of 'event_parent_project_sfid = %s'", foundationSFID)
+ keyCondition := expression.Key("event_parent_project_sfid").Equal(expression.Value(foundationSFID))
var filter expression.ConditionBuilder
- if companyID != "" {
- log.WithFields(f).Debugf("adding filter condition of 'event_company_id = %s'", companyID)
- filter = expression.Name("event_company_id").Equal(expression.Value(companyID))
- }
- return repo.queryEventsTable(CompanySFIDFoundationSFIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
+ log.WithFields(f).Debugf("adding filter condition of 'event_company_sfid = %s ", companySFID)
+ filter = expression.Name("event_company_sfid").Equal(expression.Value(companySFID))
+ return repo.queryEventsTable(EventFoundationSFIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
// GetCompanyClaGroupEvents returns the list of events for cla group and the company
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index dbef1e7ff..c2a9bb26a 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -242,10 +242,18 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", project.Parent)
return nil
}
+ var parentProjectName, parentProjectID string
+ if utils.IsProjectCategory(project, parentProject) {
+ parentProjectName = parentProject.Name
+ parentProjectID = parentProject.ID
+ } else {
+ parentProjectName = project.Name
+ parentProjectID = project.ID
+ }
log.WithFields(f).Debugf("loaded salesforce project by parent ID: %s - resulting in ID: %s with name: %s",
- project.Parent, parentProject.ID, parentProject.Name)
- args.ParentProjectSFID = parentProject.ID
- args.ParentProjectName = parentProject.Name
+ project.Parent, parentProjectID, parentProjectName)
+ args.ParentProjectSFID = parentProjectID
+ args.ParentProjectName = parentProjectName
}
}
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index 0803f5934..224b5c3f3 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -307,11 +307,12 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
log.WithFields(f).Warnf("problem loading project by SFID: %s", params.ProjectSFID)
return events.NewGetCompanyProjectEventsBadRequest().WithPayload(errorResponse(reqID, err))
}
-
var result *v1Models.EventList
- if projectDetails.ProjectType == utils.ProjectTypeProjectGroup {
- result, err = service.GetCompanyFoundationEvents(v1Company.CompanyExternalID, params.CompanyID, params.ProjectSFID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
+ if utils.IsProjectHasRootParent(projectDetails) {
+ log.WithFields(f).Debugf("loading foundation level events for projectSFID: %s...", params.ProjectSFID)
+ result, err = service.GetCompanyFoundationEvents(v1Company.CompanyExternalID, "", params.ProjectSFID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
} else {
+ log.WithFields(f).Debugf("loading project level events for projectSFID :%s...", params.ProjectSFID)
pm, perr := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
if perr != nil {
if perr == projects_cla_groups.ErrProjectNotAssociatedWithClaGroup {
From 9e102bfad1fd455526ed8660feb0aa2c20b22482 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 11 Mar 2021 21:52:19 +0300
Subject: [PATCH 0157/1276] Feature/Get GH orgs response (#2776)
- Updated GET endpoint for gh orgs with response installationURL key
Signed-off-by: wanyaland
---
cla-backend-go/swagger/cla.v2.yaml | 5 +++++
cla-backend-go/v2/github_organizations/service.go | 10 ++++++++++
2 files changed, 15 insertions(+)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index b3735a92e..c45018913 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -5039,6 +5039,11 @@ definitions:
type: boolean
description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
x-omitempty: false
+ installationURL:
+ type: string
+ x-nullable: true
+ example: "https://github.com/organizations/deal-test-org-2/settings/installations/1235464"
+ format: uri
github_organization_name:
type: string
description: The GitHub Organization name
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 46dd3e082..aa80c5b39 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -6,12 +6,14 @@ package github_organizations
import (
"context"
"fmt"
+ "net/url"
"sort"
"strconv"
"strings"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ "github.com/go-openapi/strfmt"
"github.com/sirupsen/logrus"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -137,6 +139,13 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
}
+ installURL := url.URL{
+ Scheme: "https",
+ Host: "github.com",
+ Path: fmt.Sprintf("/%s/settings/installations/%d", org.OrganizationName, org.OrganizationInstallationID),
+ }
+ installationURL := strfmt.URI(installURL.String())
+
rorg := &models.ProjectGithubOrganization{
AutoEnabled: org.AutoEnabled,
AutoEnableCLAGroupID: org.AutoEnabledClaGroupID,
@@ -145,6 +154,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
ConnectionStatus: "", // updated below
GithubOrganizationName: org.OrganizationName,
Repositories: make([]*models.ProjectGithubRepository, 0),
+ InstallationURL: &installationURL,
}
orgmap[org.OrganizationName] = rorg
From c917b4256a2a23a76d129f4a3cf526f9854b3a2c Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 11 Mar 2021 23:57:25 +0300
Subject: [PATCH 0158/1276] Bug/Project Service (#2777)
- Resolved project service query by updating base path for client stub calls
Signed-off-by: wanyaland
---
cla-backend-go/v2/project-service/client.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 00831915d..724c47e29 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -44,7 +44,7 @@ func InitClient(APIGwURL string) {
projectServiceClient = &Client{
cl: client.NewHTTPClientWithConfig(strfmt.Default, &client.TransportConfig{
Host: APIGwURL,
- BasePath: "project-service/v1",
+ BasePath: "project-service",
Schemes: []string{"https"},
}),
}
From 72555656d41d5155cc2488498bc0a34db7325e42 Mon Sep 17 00:00:00 2001
From: wanyaland
Date: Fri, 12 Mar 2021 13:44:24 +0300
Subject: [PATCH 0159/1276] Bug/Installation URL
- Updated installation url path with 'organizations'
Signed-off-by: wanyaland
---
cla-backend-go/github_organizations/helpers.go | 2 +-
cla-backend-go/v2/github_organizations/service.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/github_organizations/helpers.go b/cla-backend-go/github_organizations/helpers.go
index f256e38d6..a42e43a3e 100644
--- a/cla-backend-go/github_organizations/helpers.go
+++ b/cla-backend-go/github_organizations/helpers.go
@@ -43,7 +43,7 @@ func buildGithubOrganizationListModels(ctx context.Context, githubOrganizations
installURL := netURL.URL{
Scheme: "https",
Host: "github.com",
- Path: fmt.Sprintf("/%s/settings/installations/%d", ghorg.OrganizationName, ghorg.OrganizationInstallationID),
+ Path: fmt.Sprintf("/organizations/%s/settings/installations/%d", ghorg.OrganizationName, ghorg.OrganizationInstallationID),
}
installationURL := strfmt.URI(installURL.String())
ghorg.GithubInfo.Details = &models.GithubOrganizationGithubInfoDetails{
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index aa80c5b39..c45350976 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -142,7 +142,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
installURL := url.URL{
Scheme: "https",
Host: "github.com",
- Path: fmt.Sprintf("/%s/settings/installations/%d", org.OrganizationName, org.OrganizationInstallationID),
+ Path: fmt.Sprintf("/organizations/%s/settings/installations/%d", org.OrganizationName, org.OrganizationInstallationID),
}
installationURL := strfmt.URI(installURL.String())
From 33c65e2f7626f5c39f2ca103d861851fde5e4d4b Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 12 Mar 2021 20:45:53 +0300
Subject: [PATCH 0160/1276] Bug/Cla Group Events (#2779)
- Resolved query for cla-group-events with using event_cla_group_id as index
Signed-off-by: wanyaland
---
cla-backend-go/events/repository.go | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 62e531950..544d3f14b 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -503,7 +503,7 @@ func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, found
log.WithFields(f).Debugf("adding key condition of 'event_parent_project_sfid = %s'", foundationSFID)
keyCondition := expression.Key("event_parent_project_sfid").Equal(expression.Value(foundationSFID))
var filter expression.ConditionBuilder
- log.WithFields(f).Debugf("adding filter condition of 'event_company_sfid = %s ", companySFID)
+ log.WithFields(f).Debugf("adding filter condition of 'event_company_sfid = %s'", companySFID)
filter = expression.Name("event_company_sfid").Equal(expression.Value(companySFID))
return repo.queryEventsTable(EventFoundationSFIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
@@ -519,15 +519,12 @@ func (repo *repository) GetCompanyClaGroupEvents(companySFID, companyID, claGrou
"paramPageSize": utils.Int64Value(paramPageSize),
"loadAll": all,
}
- key := fmt.Sprintf("%s#%s", companySFID, claGroupID)
- log.WithFields(f).Debugf("adding key condition of 'company_sfid_project_id = %s'", key)
- keyCondition := expression.Key("company_sfid_project_id").Equal(expression.Value(key))
+ log.WithFields(f).Debugf("adding key condition of 'event_cla_group_id = %s'", claGroupID)
+ keyCondition := expression.Key("event_cla_group_id").Equal(expression.Value(claGroupID))
var filter expression.ConditionBuilder
- if companyID != "" {
- log.WithFields(f).Debugf("adding filter condition of 'event_company_id = %s'", companyID)
- filter = expression.Name("event_company_id").Equal(expression.Value(companyID))
- }
- return repo.queryEventsTable(CompanySFIDProjectIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
+ log.WithFields(f).Debugf("adding filter condition of 'event_company_sfid = %s'", companySFID)
+ filter = expression.Name("event_company_sfid").Equal(expression.Value(companySFID))
+ return repo.queryEventsTable(EventCLAGroupIDEpochIndex, keyCondition, &filter, nextKey, paramPageSize, all, nil)
}
// GetCompanyEvents returns the list of events for given company id and event types
From bb0b5e9db4aebf91892aa3e39809d87218035fa1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 12 Mar 2021 13:45:37 -0800
Subject: [PATCH 0161/1276] Updated Get Company Projects Error Handling (#2780)
- Updated error handling
- Sped up get signature details/metrics routines - decreased PROD AWSF API latency from 11s to 5s
Signed-off-by: David Deal
---
cla-backend-go/project/repository.go | 2 +-
.../projects_cla_groups/repository.go | 1 -
cla-backend-go/signatures/converters.go | 31 +--
cla-backend-go/signatures/repository.go | 3 -
cla-backend-go/v2/cla_groups/service.go | 48 +++--
cla-backend-go/v2/company/handlers.go | 54 ++++--
cla-backend-go/v2/company/service.go | 176 ++++++++++++------
7 files changed, 211 insertions(+), 104 deletions(-)
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 2dfa9863f..7f83486d5 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -354,7 +354,7 @@ func (repo *repo) GetClaGroupsByFoundationSFID(ctx context.Context, foundationSF
}
}
- log.WithFields(f).Debugf("foundation projects!: %#v ", projects)
+ // log.WithFields(f).Debugf("foundation projects!: %#v ", projects)
return &models.ClaGroups{
ResultCount: int64(len(projects)),
diff --git a/cla-backend-go/projects_cla_groups/repository.go b/cla-backend-go/projects_cla_groups/repository.go
index 0126440c5..be33acecf 100644
--- a/cla-backend-go/projects_cla_groups/repository.go
+++ b/cla-backend-go/projects_cla_groups/repository.go
@@ -79,7 +79,6 @@ func (repo *repo) queryClaGroupsProjects(keyCondition expression.KeyConditionBui
"keyCondition": fmt.Sprintf("%+v", keyCondition),
}
- log.WithFields(f).Debug("building query...")
expr, err := expression.NewBuilder().WithKeyCondition(keyCondition).Build()
if err != nil {
log.WithFields(f).Warnf("error building expression for project cla groups, error: %v", err)
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index a347d2a50..3639a6b13 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -18,11 +18,11 @@ import (
)
// buildProjectSignatureModels converts the response model into a response data model
-func (repo repository) buildProjectSignatureModels(ctx context.Context, results *dynamodb.QueryOutput, projectID string, loadACLDetails bool) ([]*models.Signature, error) {
+func (repo repository) buildProjectSignatureModels(ctx context.Context, results *dynamodb.QueryOutput, claGroupID string, loadACLDetails bool) ([]*models.Signature, error) {
f := logrus.Fields{
- "functionName": "signatures.converters.buildProjectSignatureModels",
+ "functionName": "v1.signatures.converters.buildProjectSignatureModels",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectID": projectID,
+ "claGroupID": claGroupID,
}
var sigs []*models.Signature
@@ -31,8 +31,8 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
err := dynamodbattribute.UnmarshalListOfMaps(results.Items, &dbSignatures)
if err != nil {
- log.WithFields(f).Warnf("error unmarshalling signatures from database for project: %s, error: %v",
- projectID, err)
+ log.WithFields(f).Warnf("error unmarshalling signatures from database for cla group ID: %s, error: %v",
+ claGroupID, err)
return nil, err
}
@@ -103,7 +103,8 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
if sigModel.SignatureReferenceType == utils.SignatureReferenceTypeUser {
userModel, userErr := repo.usersRepo.GetUser(sigModel.SignatureReferenceID)
if userErr != nil || userModel == nil {
- log.WithFields(f).Warnf("unable to lookup user using id: %s, error: %v", sigModel.SignatureReferenceID, userErr)
+ log.WithFields(f).WithError(userErr).Warnf("unable to lookup user for signature: %s with reference type: %s using signature reference id: %s",
+ sigModel.SignatureID, sigModel.SignatureReferenceType, sigModel.SignatureReferenceID)
} else {
userName = userModel.Username
userLFID = userModel.LfUsername
@@ -114,7 +115,8 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
if signatureUserCompanyID != "" {
dbCompanyModel, companyErr := repo.companyRepo.GetCompany(ctx, signatureUserCompanyID)
if companyErr != nil {
- log.WithFields(f).Warnf("unable to lookup company using id: %s, error: %v", signatureUserCompanyID, companyErr)
+ log.WithFields(f).WithError(companyErr).Warnf("unable to lookup company record for signature: %s with reference type: %s using signature user company id: %s",
+ sigModel.SignatureID, sigModel.SignatureReferenceType, signatureUserCompanyID)
} else {
companyName = dbCompanyModel.CompanyName
companySigningEntityName = dbCompanyModel.SigningEntityName
@@ -123,7 +125,8 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
} else if sigModel.SignatureReferenceType == utils.SignatureReferenceTypeCompany {
dbCompanyModel, companyErr := repo.companyRepo.GetCompany(ctx, sigModel.SignatureReferenceID)
if companyErr != nil {
- log.WithFields(f).Warnf("unable to lookup company using id: %s, error: %v", sigModel.SignatureReferenceID, companyErr)
+ log.WithFields(f).WithError(companyErr).Warnf("unable to lookup company record for signature: %s with reference type: %s using signature reference id: %s",
+ sigModel.SignatureID, sigModel.SignatureReferenceType, sigModel.SignatureReferenceID)
} else {
companyName = dbCompanyModel.CompanyName
companySigningEntityName = dbCompanyModel.SigningEntityName
@@ -138,10 +141,10 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
if loadACLDetails {
userModel, userErr := repo.usersRepo.GetUserByUserName(userName, true)
if userErr != nil {
- log.WithFields(f).Warnf("unable to lookup user using username: %s, error: %v", userName, userErr)
+ log.WithFields(f).WithError(userErr).Warnf("unable to lookup user by userNmae: %s in ACL for signature: %s", userName, sigModel.SignatureID)
} else {
if userModel == nil {
- log.WithFields(f).Warnf("User looking for username is null: %s for signature: %s", userName, sigModel.SignatureID)
+ log.WithFields(f).Warnf("unable to lookup user by userNmae: %s in ACL for signature: %s", userName, sigModel.SignatureID)
} else {
signatureACL = append(signatureACL, *userModel)
}
@@ -168,7 +171,7 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
// buildProjectSignatureSummaryModels converts the response model into a signature summary model
func (repo repository) buildProjectSignatureSummaryModels(ctx context.Context, results *dynamodb.QueryOutput, projectID string) ([]*models.SignatureSummary, error) {
f := logrus.Fields{
- "functionName": "signatures.converters.buildProjectSignatureSummaryModels",
+ "functionName": "v1.signatures.converters.buildProjectSignatureSummaryModels",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectID": projectID,
}
@@ -235,7 +238,8 @@ func (repo repository) buildProjectSignatureSummaryModels(ctx context.Context, r
if signatureUserCompanyID != "" {
dbCompanyModel, companyErr := repo.companyRepo.GetCompany(ctx, signatureUserCompanyID)
if companyErr != nil {
- log.WithFields(f).Warnf("unable to lookup company using id: %s, error: %v", signatureUserCompanyID, companyErr)
+ log.WithFields(f).WithError(companyErr).Warnf("unable to lookup company record for signature: %s with reference type: %s using signature user company id: %s",
+ sigModel.SignatureID, sigModel.SignatureReferenceType, signatureUserCompanyID)
} else {
companyName = dbCompanyModel.CompanyName
companySigningEntityName = dbCompanyModel.SigningEntityName
@@ -244,7 +248,8 @@ func (repo repository) buildProjectSignatureSummaryModels(ctx context.Context, r
} else if sigModel.SignatureReferenceType == "company" {
dbCompanyModel, companyErr := repo.companyRepo.GetCompany(ctx, sigModel.SignatureReferenceID)
if companyErr != nil {
- log.WithFields(f).Warnf("unable to lookup company using id: %s, error: %v", sigModel.SignatureReferenceID, companyErr)
+ log.WithFields(f).WithError(companyErr).Warnf("unable to lookup company record for signature: %s with reference type: %s using signature reference id: %s",
+ sigModel.SignatureID, sigModel.SignatureReferenceType, sigModel.SignatureReferenceID)
} else {
companyName = dbCompanyModel.CompanyName
companySigningEntityName = dbCompanyModel.SigningEntityName
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index db0ae7a95..6cda340ae 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -466,7 +466,6 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
- //log.WithFields(f).Debugf("Running signature project query using queryInput: %+v", queryInput)
results, errQuery := repo.dynamoDBClient.Query(queryInput)
//log.WithFields(f).Debugf("Ran signature project query, results: %+v, error: %+v", results, errQuery)
if errQuery != nil {
@@ -807,7 +806,6 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
- log.WithFields(f).Debugf("Running signature project query using queryInput: %+v", queryInput)
results, errQuery := repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
log.WithFields(f).Warnf("error retrieving project signature ID for project: %s, error: %v",
@@ -1026,7 +1024,6 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
- log.WithFields(f).Debugf("Running signature project query using queryInput: %+v", queryInput)
results, errQuery := repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
log.WithFields(f).Warnf("error retrieving project signature ID for project: %s, error: %v",
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 885563452..cfd2b74e5 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -576,23 +576,41 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
}
// One more pass to update the metrics - bulk lookup the metrics and update the response model
- log.WithFields(f).Debugf("Loading metrics for %d CLA Groups - updating response", len(claGroupIDList.List()))
- var iclaSignatureCount, cclaSignatureCount int64
- for _, responseEntry := range responseModel.List {
- log.Debugf("cla group entry logs %s", responseEntry.ClaGroupID)
- iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeICLA), SignatureType: aws.String(utils.SignatureTypeCLA)})
- if err != nil {
- log.Warnf("error while getting ICLA Signature using clagroupID %s Error: %v", responseEntry.ClaGroupID, err)
- }
- iclaSignatureCount = iclaSignatureDetails.ResultCount
+ log.WithFields(f).Debugf("Loading metrics for %d CLA Groups...", len(claGroupIDList.List()))
+ type MetricsResult struct {
+ index int
+ iclaSignatureCount int64
+ cclaSignatureCount int64
+ Error error
+ }
+ metricsResultChannel := make(chan *MetricsResult, len(responseModel.List))
+
+ for idx, responseEntry := range responseModel.List {
+ go func(index int, responseEntry *models.ClaGroupSummary) {
+ log.WithFields(f).Debugf("fetching project signature metrics for CLA Group (%d): %s - %s", index, responseEntry.ClaGroupID, responseEntry.ClaGroupName)
+ iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeICLA), SignatureType: aws.String(utils.SignatureTypeCLA)})
+ if err != nil {
+ log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ }
- cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeCCLA), SignatureType: aws.String(utils.SignatureTypeCCLA)})
- if err != nil {
- log.Warnf("error while getting ICLA Signature using clagroupID %s Error: %v", responseEntry.ClaGroupID, err)
- }
- cclaSignatureCount = cclaSignatureDetails.ResultCount
+ cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeCCLA), SignatureType: aws.String(utils.SignatureTypeCCLA)})
+ if err != nil {
+ log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ }
+
+ metricsResultChannel <- &MetricsResult{
+ index: index,
+ iclaSignatureCount: iclaSignatureDetails.ResultCount,
+ cclaSignatureCount: cclaSignatureDetails.ResultCount,
+ Error: err,
+ }
+ }(idx, responseEntry)
+ }
- responseEntry.TotalSignatures = cclaSignatureCount + iclaSignatureCount
+ log.WithFields(f).Debugf("Waiting for metrics responses for %d CLA Groups...", len(claGroupIDList.List()))
+ for range responseModel.List {
+ response := <-metricsResultChannel
+ responseModel.List[response.index].TotalSignatures = response.cclaSignatureCount + response.iclaSignatureCount
}
// Sort the response based on the Foundation and CLA group name
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index f0d7f3428..fc1704d87 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -35,7 +35,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyByInternalIDHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyByInternalIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": params.CompanyID,
"authUserName": utils.StringValue(params.XUSERNAME),
@@ -77,7 +77,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyByExternalIDHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyByExternalIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
"authUserName": utils.StringValue(params.XUSERNAME),
@@ -123,7 +123,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyProjectClaManagersHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyProjectClaManagersHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
@@ -165,7 +165,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyCLAGroupManagersHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyCLAGroupManagersHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"companyID": params.CompanyID,
@@ -192,7 +192,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyProjectActiveClaHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyProjectActiveClaHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
@@ -240,10 +240,11 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyProjectContributorsHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyProjectContributorsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
+ "searchTerm": utils.StringValue(params.SearchTerm),
"authUserName": utils.StringValue(params.XUSERNAME),
"authUserEmail": utils.StringValue(params.XEMAIL),
}
@@ -259,6 +260,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
return company.NewGetCompanyProjectActiveClaBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
+ log.WithFields(f).Debugf("looked company by internal ID")
// PM - check if authorized by project scope - allow if PM has project ID scope that matches
// Contact,Community Program Manager,CLA Manager,CLA Manager Designee,Company Admin - check if authorized by organization scope - allow if {Contact,Community Program Manager,CLA Manager,CLA Manager Designee,Company Admin} has organization ID scope that matches
@@ -272,10 +274,26 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
utils.ErrorResponseForbidden(reqID, msg))
}
+ log.WithFields(f).Debugf("querying for employee contributors...")
result, err := service.GetCompanyProjectContributors(ctx, params.ProjectSFID, params.CompanyID, utils.StringValue(params.SearchTerm))
if err != nil {
- if _, ok := err.(*utils.CompanyNotFound); ok {
- return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID)
+ if companyErr, ok := err.(*utils.CompanyNotFound); ok {
+ msg := fmt.Sprintf("Company not found with ID: %s", companyErr.CompanyID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ if claGroupErr, ok := err.(*utils.CLAGroupNotFound); ok {
+ msg := fmt.Sprintf("CLA Group not found with ID: %s", claGroupErr.CLAGroupID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ if _, ok := err.(*utils.ProjectCLAGroupMappingNotFound); ok {
+ msg := fmt.Sprintf("CLA Group not found with project SFID: %s", params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return company.NewGetCompanyProjectContributorsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
return company.NewGetCompanyProjectContributorsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
@@ -288,7 +306,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyProjectClaHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyProjectClaHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companySFID": params.CompanySFID,
@@ -328,7 +346,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyCreateCompanyHandler",
+ "functionName": "v2.company.handlers.CompanyCreateCompanyHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": params.UserID,
"companyName": aws.StringValue(params.Input.CompanyName),
@@ -368,7 +386,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyByNameHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyByNameHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": params.CompanyName,
}
@@ -428,7 +446,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyByNameHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyByNameHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signingEntityName": params.SigningEntityName,
}
@@ -458,7 +476,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyDeleteCompanyByIDHandler",
+ "functionName": "v2.company.handlers.CompanyDeleteCompanyByIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": params.CompanyID,
"authUserName": utils.StringValue(params.XUSERNAME),
@@ -512,7 +530,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "company.handlers.CompanyDeleteCompanyBySFIDHandler",
+ "functionName": "v2.company.handlers.CompanyDeleteCompanyBySFIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
"authUserName": utils.StringValue(params.XUSERNAME),
@@ -569,7 +587,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyContributorAssociationHandler",
+ "functionName": "v2.company.handlers.CompanyContributorAssociationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
"userEmail": params.Body.UserEmail.String(),
@@ -595,7 +613,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyContributorAssociationHandler",
+ "functionName": "v2.company.handlers.CompanyContributorAssociationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": params.CompanySFID,
}
@@ -652,7 +670,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "company.handlers.CompanyGetCompanyByInternalIDHandler",
+ "functionName": "v2.company.handlers.CompanyGetCompanyByInternalIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": params.CompanyName,
"websiteName": params.WebsiteName,
@@ -701,7 +719,7 @@ func errorResponse(reqID string, err error) *models.ErrorResponse {
// isUserHaveAccessToCLAProjectOrganization is a helper function to determine if the user has access to the specified project and organization
func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *auth.User, projectSFID, organizationSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool {
f := logrus.Fields{
- "functionName": "company.handlers.isUserHaveAccessToCLAProjectOrganization",
+ "functionName": "v2.company.handlers.isUserHaveAccessToCLAProjectOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"organizationSFID": organizationSFID,
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index beda500d3..f1fadcb39 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -127,7 +127,7 @@ func NewService(v1CompanyService v1Company.IService, sigRepo signatures.Signatur
func (s *service) GetCompanyProjectCLAManagers(ctx context.Context, v1CompanyModel *models.Company, projectSFID string) (*models.CompanyClaManagers, error) {
f := logrus.Fields{
- "functionName": "GetCompanyProjectCLAManagers",
+ "functionName": "v2.company.service.GetCompanyProjectCLAManagers",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"companyID": v1CompanyModel.CompanyID,
@@ -254,7 +254,7 @@ func fillEventsInfo(claManagers []*v2Models.CompanyClaManager, addedEvents *v1Mo
func (s *service) GetCompanyAdmins(ctx context.Context, companySFID string) (*models.CompanyAdminList, error) {
f := logrus.Fields{
- "functionName": "GetCompanyAdmins",
+ "functionName": "v2.company.service.GetCompanyAdmins",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
}
@@ -289,7 +289,7 @@ func (s *service) GetCompanyAdmins(ctx context.Context, companySFID string) (*mo
func (s *service) GetCompanyProjectActiveCLAs(ctx context.Context, companyID string, projectSFID string) (*models.ActiveClaList, error) {
f := logrus.Fields{
- "functionName": "GetCompanyProjectActiveCLAs",
+ "functionName": "v2.company.service.GetCompanyProjectActiveCLAs",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"companyID": companyID,
@@ -330,23 +330,28 @@ func (s *service) GetCompanyProjectActiveCLAs(ctx context.Context, companyID str
func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID string, companyID string, searchTerm string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
- "functionName": "GetCompanyProjectContributors",
+ "functionName": "v2.company.service.GetCompanyProjectContributors",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"companyID": companyID,
"searchTerm": searchTerm,
}
+
list := make([]*models.CorporateContributor, 0)
+ log.WithFields(f).Debugf("querying for employee contributors...")
sigs, err := s.getAllCompanyProjectEmployeeSignatures(ctx, companyID, projectSFID)
if err != nil {
- log.WithFields(f).Warnf("problem fetching all company project employee signatures, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn("problem fetching all company project employee signatures")
return nil, err
}
if len(sigs) == 0 {
+ log.WithFields(f).Debug("not signatures found - returning emtpy list")
return &models.CorporateContributorList{
List: list,
}, nil
}
+ log.WithFields(f).Debugf("found %d signatures", len(sigs))
+
var wg sync.WaitGroup
result := make(chan *models.CorporateContributor)
wg.Add(len(sigs))
@@ -355,6 +360,7 @@ func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID
close(result)
}()
+ log.WithFields(f).Debugf("adding additional corporate contributor details for %d signatures...", len(sigs))
for _, sig := range sigs {
go fillCorporateContributorModel(&wg, s.userRepo, sig, result, searchTerm)
}
@@ -370,7 +376,7 @@ func (s *service) GetCompanyProjectContributors(ctx context.Context, projectSFID
func (s *service) CreateCompany(ctx context.Context, params *v2Ops.CreateCompanyParams) (*models.CompanyOutput, error) {
f := logrus.Fields{
- "functionName": "service.CreateCompany",
+ "functionName": "v2.company.service.CreateCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": params.Input.CompanyName,
"signingEntityName": params.Input.SigningEntityName,
@@ -485,7 +491,7 @@ func (s *service) CreateCompany(ctx context.Context, params *v2Ops.CreateCompany
func (s *service) CreateCompanyFromSFModel(ctx context.Context, orgModel *orgModels.Organization, authUser *auth.User) (*models.CompanyOutput, error) {
f := logrus.Fields{
- "functionName": "company.service.CreateCompanyFromSFModel",
+ "functionName": "v2.company.service.CreateCompanyFromSFModel",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"organizationID": orgModel.Name,
"organizationName": orgModel.Name,
@@ -520,7 +526,7 @@ func (s *service) CreateCompanyFromSFModel(ctx context.Context, orgModel *orgMod
// GetCompanyByName deletes the company by name
func (s *service) GetCompanyByName(ctx context.Context, companyName string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "GetCompanyByName",
+ "functionName": "v2.company.service.GetCompanyByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": companyName,
}
@@ -548,7 +554,7 @@ func (s *service) GetCompanyByName(ctx context.Context, companyName string) (*mo
// GetCompanyBySigningEntityName retrieves the company by signing entity name
func (s *service) GetCompanyBySigningEntityName(ctx context.Context, signingEntityName string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "company.service.GetCompanyBySigningEntityName",
+ "functionName": "v2.company.service.GetCompanyBySigningEntityName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signingEntityName": signingEntityName,
}
@@ -600,7 +606,7 @@ func (s *service) GetCompanyBySigningEntityName(ctx context.Context, signingEnti
// GetCompanyByID retrieves the company by internal ID
func (s *service) GetCompanyByID(ctx context.Context, companyID string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "company.service.GetCompanyByID",
+ "functionName": "v2.company.service.GetCompanyByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
}
@@ -627,7 +633,7 @@ func (s *service) GetCompanyByID(ctx context.Context, companyID string) (*models
func (s *service) AssociateContributor(ctx context.Context, companySFID string, userEmail string) (*models.Contributor, error) {
f := logrus.Fields{
- "functionName": "company.service.AssociateContributor",
+ "functionName": "v2.company.service.AssociateContributor",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"userEmail": userEmail,
@@ -674,7 +680,7 @@ func (s *service) AssociateContributor(ctx context.Context, companySFID string,
//CreateContributor creates contributor for contributor prospect
func (s *service) CreateContributor(ctx context.Context, companyID string, projectID string, userEmail string, ClaGroupID string) (*models.Contributor, error) {
f := logrus.Fields{
- "functionName": "company.service.CreateContributor",
+ "functionName": "v2.company.service.CreateContributor",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectID": projectID,
@@ -766,7 +772,7 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
//AssociateContributorByGroup creates contributor by group for contributor prospect
func (s *service) AssociateContributorByGroup(ctx context.Context, companySFID, userEmail string, projectCLAGroups []*projects_cla_groups.ProjectClaGroup, ClaGroupID string) ([]*models.Contributor, string, error) {
f := logrus.Fields{
- "functionName": "company.service.AssociateContributorByGroup",
+ "functionName": "v2.company.service.AssociateContributorByGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
"ClaGroupID": ClaGroupID,
@@ -809,7 +815,7 @@ func (s *service) AssociateContributorByGroup(ctx context.Context, companySFID,
// GetCompanyBySFID retrieves the company by external SFID
func (s *service) GetCompanyBySFID(ctx context.Context, companySFID string) (*models.Company, error) {
f := logrus.Fields{
- "functionName": "company.service.GetCompanyBySFID",
+ "functionName": "v2.company.service.GetCompanyBySFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
}
@@ -866,7 +872,7 @@ func (s *service) DeleteCompanyBySFID(ctx context.Context, companyID string) err
func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User, companySFID, projectSFID string, companyID *string) (*models.CompanyProjectClaList, error) {
f := logrus.Fields{
- "functionName": "companyModel.service.GetCompanyProjectCLA",
+ "functionName": "v2.company.service.GetCompanyProjectCLA",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
@@ -1075,7 +1081,7 @@ func (s *service) GetCompanyProjectCLA(ctx context.Context, authUser *auth.User,
// corresponding CLA managers
func (s *service) GetCompanyCLAGroupManagers(ctx context.Context, companyID, claGroupID string) (*models.CompanyClaManagers, error) {
f := logrus.Fields{
- "functionName": "company.service.GetCompanyCLAGroupManagers",
+ "functionName": "v2.company.service.GetCompanyCLAGroupManagers",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"claGroupID": claGroupID,
@@ -1153,7 +1159,7 @@ func v2ProjectToMap(projectDetails *v2ProjectServiceModels.ProjectOutputDetailed
func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, projectSFID string) (map[string]*claGroupModel, error) {
f := logrus.Fields{
- "functionName": "company.service.getCLAGroupsUnderProjectOrFoundation",
+ "functionName": "v2.company.service.getCLAGroupsUnderProjectOrFoundation",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -1292,7 +1298,7 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, proj
func (s *service) getAllCCLASignatures(ctx context.Context, companyID string) ([]*v1Models.Signature, error) {
f := logrus.Fields{
- "functionName": "company.service.getAllCCLASignatures",
+ "functionName": "v2.company.service.getAllCCLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
}
@@ -1335,7 +1341,7 @@ func getUsersInfo(lfUsernames []string) (map[string]*v2UserServiceModels.User, e
func fillUsersInfo(claManagers []*models.CompanyClaManager, usermap map[string]*v2UserServiceModels.User) {
f := logrus.Fields{
- "functionName": "company.service.fillUsersInfo",
+ "functionName": "v2.company.service.fillUsersInfo",
}
log.WithFields(f).Debug("filling users info...")
@@ -1359,7 +1365,7 @@ func fillUsersInfo(claManagers []*models.CompanyClaManager, usermap map[string]*
func fillProjectInfo(claManagers []*models.CompanyClaManager, claGroups map[string]*claGroupModel) {
f := logrus.Fields{
- "functionName": "company.service.fillProjectInfo",
+ "functionName": "v2.company.service.fillProjectInfo",
}
log.WithFields(f).Debug("filling project info...")
for _, claManager := range claManagers {
@@ -1375,7 +1381,7 @@ func fillProjectInfo(claManagers []*models.CompanyClaManager, claGroups map[stri
func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1Models.Signature, activeCla *models.ActiveCla, claGroups map[string]*claGroupModel, companyID string) {
f := logrus.Fields{
- "functionName": "v1CompanyModel.service.fillActiveCLA",
+ "functionName": "v2.company.service.fillActiveCLA",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
}
@@ -1509,18 +1515,23 @@ func (s *service) filterClaProjects(ctx context.Context, projects []*v2ProjectSe
}
func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepository, sig *v1Models.Signature, result chan *models.CorporateContributor, searchTerm string) {
+ f := logrus.Fields{
+ "functionName": "v2.company.service.fillCorporateContributorModel",
+ }
defer wg.Done()
user, err := usersRepo.GetUser(sig.SignatureReferenceID)
if err != nil {
- log.Error("fillCorporateContributorModel: unable to get user info", err)
+ log.WithFields(f).Warnf("unable to load user information using signature ID: %s", sig.SignatureReferenceID)
return
}
+
if searchTerm != "" {
ls := strings.ToLower(searchTerm)
if !(strings.Contains(strings.ToLower(user.Username), ls) || strings.Contains(strings.ToLower(user.LfUsername), ls)) {
return
}
}
+
var contributor models.CorporateContributor
var sigSignedTime = sig.SignatureCreated
contributor.GithubID = user.GithubUsername
@@ -1528,7 +1539,7 @@ func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepos
contributor.Name = user.Username
t, err := utils.ParseDateTime(sig.SignatureCreated)
if err != nil {
- log.Error("fillCorporateContributorModel: unable to parse time", err)
+ log.WithFields(f).WithError(err).Warn("unable to parse time")
} else {
sigSignedTime = utils.TimeToString(t)
}
@@ -1541,12 +1552,12 @@ func fillCorporateContributorModel(wg *sync.WaitGroup, usersRepo users.UserRepos
func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, companyID string, projectSFID string) ([]*v1Models.Signature, error) {
f := logrus.Fields{
- "functionName": "company.service.getAllCompanyProjectEmployeeSignatures",
+ "functionName": "v2.company.service.getAllCompanyProjectEmployeeSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectSFID": projectSFID,
}
- log.WithFields(f).Debug("getAllCompanyProjectEmployeeSignatures")
+ log.WithFields(f).Debug("querying company and project...")
_, claGroup, err := s.getCompanyAndClaGroup(ctx, companyID, projectSFID)
if err != nil {
return nil, err
@@ -1566,50 +1577,109 @@ func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, co
// get company and project in parallel
func (s *service) getCompanyAndClaGroup(ctx context.Context, companyID, projectSFID string) (*v1Models.Company, *v1Models.ClaGroup, error) {
f := logrus.Fields{
- "functionName": "company.service.getCompanyAndClaGroup",
+ "functionName": "v2.company.service.getCompanyAndClaGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectSFID": projectSFID,
}
- var comp *v1Models.Company
- var claGroup *v1Models.ClaGroup
- var companyErr, projectErr error
- // query projects and company
- var cp sync.WaitGroup
- cp.Add(2)
- go func() {
- defer cp.Done()
- comp, companyErr = s.companyRepo.GetCompany(ctx, companyID)
- }()
- go func() {
- defer cp.Done()
+
+ type CompanyResult struct {
+ companyModel *v1Models.Company
+ Error error
+ }
+ companyResultChannel := make(chan *CompanyResult, 1)
+ type CLAGroupResult struct {
+ claGroupModel *v1Models.ClaGroup
+ Error error
+ }
+ claGroupResultChannel := make(chan *CLAGroupResult, 1)
+
+ // Load the company
+ go func(companyID string) {
+ log.WithFields(f).Debugf("loading company by ID: %s", companyID)
+ comp, companyErr := s.companyRepo.GetCompany(ctx, companyID)
+ // Return the result through the channel
+ companyResultChannel <- &CompanyResult{
+ companyModel: comp,
+ Error: companyErr,
+ }
+ }(companyID)
+
+ // Load the CLA group associated with the project
+ go func(projectSFID string) {
t := time.Now()
var pm *projects_cla_groups.ProjectClaGroup
- pm, projectErr = s.projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ log.WithFields(f).Debugf("loading CLA Group by project SFID: %s", projectSFID)
+ pm, projectErr := s.projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
if projectErr != nil {
log.WithFields(f).Debugf("cla group mapping not found for projectSFID %s", projectSFID)
+ // Return the result through the channel
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: nil,
+ Error: projectErr,
+ }
+ return
+ } else if pm == nil || pm.ClaGroupID == "" {
+ // Return the result through the channel
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: nil,
+ Error: &utils.ProjectCLAGroupMappingNotFound{
+ ProjectSFID: projectSFID,
+ },
+ }
return
}
- claGroup, projectErr = s.projectRepo.GetCLAGroupByID(ctx, pm.ClaGroupID, DontLoadRepoDetails)
- if claGroup == nil {
- projectErr = ErrProjectNotFound
+
+ log.WithFields(f).Debugf("loading CLA Group by ID: %s", pm.ClaGroupID)
+ claGroup, projectErr := s.projectRepo.GetCLAGroupByID(ctx, pm.ClaGroupID, DontLoadRepoDetails)
+ if projectErr != nil {
+ // Return the result through the channel
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: claGroup,
+ Error: &utils.CLAGroupNotFound{
+ CLAGroupID: pm.ClaGroupID,
+ Err: projectErr,
+ },
+ }
+ } else if claGroup == nil {
+ // Return the result through the channel
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: claGroup,
+ Error: &utils.CLAGroupNotFound{
+ CLAGroupID: pm.ClaGroupID,
+ },
+ }
+ } else {
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: claGroup,
+ Error: nil,
+ }
+ log.WithField("time_taken", time.Since(t).String()).Debugf("getting project by external id : %s completed", projectSFID)
}
- log.WithField("time_taken", time.Since(t).String()).Debugf("getting project by external id : %s completed", projectSFID)
- }()
- cp.Wait()
- if companyErr != nil {
- return nil, nil, companyErr
+ }(projectSFID)
+
+ // Grab the results
+ log.WithFields(f).Debug("waiting for companies query to finish...")
+ companyResponse := <-companyResultChannel
+ if companyResponse.Error != nil {
+ return nil, nil, companyResponse.Error
}
- if projectErr != nil {
- return nil, nil, projectErr
+ log.WithFields(f).Debug("company query finished")
+
+ log.WithFields(f).Debug("waiting for CLA Groups query to finish...")
+ claGroupResponse := <-claGroupResultChannel
+ if claGroupResponse.Error != nil {
+ return nil, nil, claGroupResponse.Error
}
- return comp, claGroup, nil
+ log.WithFields(f).Debug("cla groups query finished")
+
+ return companyResponse.companyModel, claGroupResponse.claGroupModel, nil
}
// autoCreateCompany helper function to create a new company record based on the SF ID and underlying record in SF
func (s service) autoCreateCompany(ctx context.Context, companySFID string) (*v1Models.Company, error) {
f := logrus.Fields{
- "functionName": "company.service.autoCreateCompany",
+ "functionName": "v2.company.service.autoCreateCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companySFID": companySFID,
}
@@ -1655,7 +1725,7 @@ func (s service) autoCreateCompany(ctx context.Context, companySFID string) (*v1
func (s *service) GetCompanyLookup(ctx context.Context, orgName string, websiteName string) (*models.Lookup, error) {
f := logrus.Fields{
- "functionName": "company.service.GetCompanyLookup",
+ "functionName": "v2.company.service.GetCompanyLookup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"orgName": orgName,
"websiteName": websiteName,
@@ -1707,7 +1777,7 @@ func (s *service) GetCompanyLookup(ctx context.Context, orgName string, websiteN
func (s *service) RequestCompanyAdmin(ctx context.Context, userID string, claManagerEmail string, claManagerName string, contributorName string, contributorEmail string, projectName string, companyName string, corporateLink string) error {
orgServices := orgService.GetClient()
f := logrus.Fields{
- "functionName": "RequestCompanyAdmin",
+ "functionName": "v2.company.service.RequestCompanyAdmin",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyName": companyName,
"claGroupName": projectName,
From ff3e8e54986d70b790c8e574d9d4c21c1a586d62 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 15 Mar 2021 09:31:30 -0700
Subject: [PATCH 0162/1276] Added signature ID to contributors response (#2783)
- Included signatureID in the corporate contributors response
- added docusign date/time if available, otherwise use signature modified date
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 25 +++++++++++++++++--
.../swagger/common/corporate-contributor.yaml | 6 +++--
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 6cda340ae..5c9a68428 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2494,21 +2494,42 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
return nil, err
}
+ log.WithFields(f).Debugf("located %d signatures...", len(dbSignatures))
for _, sig := range dbSignatures {
if searchTerm != nil {
if !strings.Contains(sig.SignatureReferenceNameLower, *searchTerm) {
continue
}
}
+
var sigCreatedTime = sig.DateCreated
t, err := utils.ParseDateTime(sig.DateCreated)
if err != nil {
- log.Error("fillCorporateContributorModel: unable to parse time", err)
+ log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
} else {
sigCreatedTime = utils.TimeToString(t)
}
+
+ var sigSignedTime = sig.DateModified
+ t, err = utils.ParseDateTime(sig.DateModified)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature date modified time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
+ }
+ // Use the user docusign date signed value if it is present - older signatures do not have this
+ if sig.UserDocusignDateSigned != "" {
+ t, err = utils.ParseDateTime(sig.UserDocusignDateSigned)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
+ }
+ }
+
signatureVersion := fmt.Sprintf("v%s.%s", sig.SignatureDocumentMajorVersion, sig.SignatureDocumentMinorVersion)
out.List = append(out.List, &models.CorporateContributor{
+ SignatureID: sig.SignatureID,
GithubID: sig.UserGithubUsername,
LinuxFoundationID: sig.UserLFUsername,
Name: sig.UserName,
@@ -2516,7 +2537,7 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
Email: sig.UserEmail,
Timestamp: sigCreatedTime,
UserDocusignName: sig.UserDocusignName,
- UserDocusignDateSigned: sig.UserDocusignDateSigned,
+ UserDocusignDateSigned: sigSignedTime,
SignatureModified: sig.DateModified,
})
}
diff --git a/cla-backend-go/swagger/common/corporate-contributor.yaml b/cla-backend-go/swagger/common/corporate-contributor.yaml
index fc4a5f489..28144b042 100644
--- a/cla-backend-go/swagger/common/corporate-contributor.yaml
+++ b/cla-backend-go/swagger/common/corporate-contributor.yaml
@@ -3,6 +3,10 @@
type: object
properties:
+ signatureID:
+ description: internal signature ID
+ $ref: './common/properties/internal-id.yaml'
+ x-omitempty: false
name:
type: string
example: "john doe"
@@ -36,5 +40,3 @@ properties:
type: string
description: the signature modified created time
example: '2019-05-03T18:59:13.082304+0000'
-
-
From 8eee8cad3f95218964bea2a4bb7da1246ab959be Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 16 Mar 2021 22:55:40 +0300
Subject: [PATCH 0163/1276] [#2784] Feature/Event Data for company Admin
(#2787)
- Resolved masked email showing in event logs
Signed-off-by: wanyaland
---
cla-backend-go/v2/cla_manager/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 0c2d297da..e9765a1ef 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -749,7 +749,7 @@ func (s *service) CreateCLAManagerRequest(ctx context.Context, contactAdmin bool
CompanyID: v1CompanyModel.CompanyID,
EventData: &events.ContributorNotifyCompanyAdminData{
AdminName: admin.Contact.Name,
- AdminEmail: admin.Contact.EmailAddress,
+ AdminEmail: userService.GetPrimaryEmail(adminUser),
},
})
}
From 0324b33d51bde910573383cf68801c22cdef86ec Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 17 Mar 2021 21:04:40 -0700
Subject: [PATCH 0164/1276] Added Additional Event Logging (#2794)
Signed-off-by: David Deal
---
cla-backend-go/events/service.go | 55 ++++++++++++++++++++++++++------
1 file changed, 45 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index c2a9bb26a..efffffb43 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -143,10 +143,14 @@ type LogEventArgs struct {
func (s *service) loadCompany(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
- "functionName": "loadCompany",
+ "functionName": "v1.events.service.loadCompany",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
+ if args == nil {
+ return errors.New("unable to load company data - args is nil")
+ }
+
if args.CompanyModel != nil {
args.CompanyName = args.CompanyModel.CompanyName
args.CompanyID = args.CompanyModel.CompanyID
@@ -168,10 +172,14 @@ func (s *service) loadCompany(ctx context.Context, args *LogEventArgs) error {
func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
- "functionName": "events.service.loadCLAGroup",
+ "functionName": "v1.events.service.loadCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
+ if args == nil {
+ return errors.New("unable to load CLA Group data - args is nil")
+ }
+
// First, attempt to user the CLA Group model that was provided...
if args.ClaGroupModel != nil {
args.CLAGroupID = args.ClaGroupModel.ProjectID
@@ -211,10 +219,14 @@ func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
- "functionName": "loadSFProject",
+ "functionName": "v1.events.service.loadSFProject",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
+ if args == nil {
+ return errors.New("unable to load SF project data - args is nil")
+ }
+
// Should be the same value for now...cleanup: need to remove one or the other
if args.ProjectID == "" && args.ProjectSFID != "" {
args.ProjectID = args.ProjectSFID
@@ -262,37 +274,46 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
- "functionName": "loadUser",
+ "functionName": "v1.events.service.loadUser",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
+ if args == nil {
+ return errors.New("unable to load user data - args is nil")
+ }
+
if args.UserModel != nil {
args.UserName = args.UserModel.Username
args.UserID = args.UserModel.UserID
args.LfUsername = args.UserModel.LfUsername
+ log.WithFields(f).Debug("loaded user for event log by caller provided user model")
return nil
- }
- if args.UserID == "" && args.LfUsername == "" {
+ } else if args.UserID == "" && args.LfUsername == "" {
log.WithFields(f).Warn("failed to load user for event log - user ID and username were not set")
return errors.New("require userID or LfUsername")
}
+
var userModel *models.User
var err error
+ // Try loading by LF username
if args.LfUsername != "" {
+ log.WithFields(f).Debugf("loading user by LF username: %s...", args.LfUsername)
userModel, err = s.combinedRepo.GetUserByUserName(args.LfUsername, true)
if err != nil {
log.WithFields(f).WithError(err).Warnf("failed to load user by username: %s", args.LfUsername)
- return err
}
}
+
+ // Try loading by user ID
if args.UserID != "" {
+ log.WithFields(f).Debugf("loading user by user ID: %s...", args.UserID)
userModel, err = s.combinedRepo.GetUser(args.UserID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("failed to load user by ID: %s", args.UserID)
- return err
}
}
+ // Did we finally load the user model?
if userModel != nil {
args.UserModel = userModel
args.UserName = userModel.Username
@@ -307,23 +328,37 @@ func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
// loadDetails fetches and sets additional information into the data model required to fill out the event log entry
func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "v1.events.service.loadDetails",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "args": fmt.Sprintf("%+v", args),
+ }
+
+ log.WithFields(f).Debug("loading company details...")
err := s.loadCompany(ctx, args)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load company details...")
return err
}
+ log.WithFields(f).Debug("loading SF project details...")
err = s.loadSFProject(ctx, args)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load SF project details...")
return err
}
+ log.WithFields(f).Debug("loading CLA Group details...")
err = s.loadCLAGroup(ctx, args)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load CLA Group details...")
return err
}
+ log.WithFields(f).Debug("loading user details...")
err = s.loadUser(ctx, args)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load user details...")
return err
}
@@ -333,13 +368,13 @@ func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
// LogEventWithContext logs the event in database
func (s *service) LogEventWithContext(ctx context.Context, args *LogEventArgs) {
f := logrus.Fields{
- "functionName": "events.service.LogEvent",
+ "functionName": "events.service.LogEventWithContext",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
defer func() {
if r := recover(); r != nil {
- log.WithFields(f).Error("panic occurred in CreateEvent", fmt.Errorf("%v", r))
+ log.WithFields(f).Errorf("panic occurred - %+v", r)
}
}()
From e8cfa83f75481f82ce85bc3d8d95f3d69cce8c74 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 18 Mar 2021 15:05:14 -0700
Subject: [PATCH 0165/1276] Updated Logic to Set the DocuSign Signed Date in
the Response Model (#2798)
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 45 +++++++++++++++------
cla-backend-go/v2/cla_groups/handlers.go | 2 +-
cla-backend-go/v2/project-service/client.go | 2 +
3 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 5c9a68428..2569cf9c2 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2365,10 +2365,27 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
}
}
- signedOn := sig.DateCreated
- if sig.SignedOn != "" {
- signedOn = sig.SignedOn
+ // Set the signed date/time
+ var sigSignedTime string
+ // Use the user docusign date signed value if it is present - older signatures do not have this
+ if sig.UserDocusignDateSigned != "" {
+ // Put the date into a standard format
+ t, err := utils.ParseDateTime(sig.UserDocusignDateSigned)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
+ }
+ } else {
+ // Put the date into a standard format
+ t, err := utils.ParseDateTime(sig.DateCreated)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
+ }
}
+
intermediateResponse = append(intermediateResponse, &IclaSignatureWithDetails{
IclaSignature: &models.IclaSignature{
GithubUsername: sig.UserGithubUsername,
@@ -2376,9 +2393,9 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
SignatureID: sig.SignatureID,
UserEmail: sig.UserEmail,
UserName: sig.UserName,
- SignedOn: signedOn,
+ SignedOn: sigSignedTime,
UserDocusignName: sig.UserDocusignName,
- UserDocusignDateSigned: sig.UserDocusignDateSigned,
+ UserDocusignDateSigned: sigSignedTime,
SignatureModified: sig.DateModified,
},
SignatureReferenceID: sig.SignatureReferenceID,
@@ -2510,21 +2527,25 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
sigCreatedTime = utils.TimeToString(t)
}
- var sigSignedTime = sig.DateModified
- t, err = utils.ParseDateTime(sig.DateModified)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to parse signature date modified time")
- } else {
- sigSignedTime = utils.TimeToString(t)
- }
+ // Set the signed date/time
+ var sigSignedTime string
// Use the user docusign date signed value if it is present - older signatures do not have this
if sig.UserDocusignDateSigned != "" {
+ // Put the date into a standard format
t, err = utils.ParseDateTime(sig.UserDocusignDateSigned)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
} else {
sigSignedTime = utils.TimeToString(t)
}
+ } else {
+ // Put the date into a standard format
+ t, err = utils.ParseDateTime(sig.DateCreated)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
+ }
}
signatureVersion := fmt.Sprintf("v%s.%s", sig.SignatureDocumentMajorVersion, sig.SignatureDocumentMinorVersion)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index eb89bfb2f..a6e97d184 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -259,7 +259,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
project, projectErr := psc.GetProject(projectSFID)
if projectErr != nil || project == nil {
msg := fmt.Sprintf("Failed to get salesforce project: %s", projectSFID)
- log.WithFields(f).Warn(msg)
+ log.WithFields(f).WithError(projectErr).Warn(msg)
if _, ok := projectErr.(*v2ProjectServiceClient.GetProjectNotFound); ok {
return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseNotFoundWithError(reqID, fmt.Sprintf("project not found with ID: [%s]", projectSFID), projectErr))
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 724c47e29..54ecb21cc 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -87,8 +87,10 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
clientAuth := runtimeClient.BearerToken(tok)
// Lookup the project
+ log.WithFields(f).Debugf("cache miss - looking up project in the service for: %s...", projectSFID)
projectModel, err := pmm.getProject(projectSFID, clientAuth)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup project in the project service for: %s", projectSFID)
return nil, err
}
From 32c58c247c6ab0245775ea1c729691a2ec1cc0a9 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 19 Mar 2021 01:06:10 +0300
Subject: [PATCH 0166/1276] [#2790] Feature/Activity Log User update (#2800)
- Updated activity log with LF Name details
Signed-off-by: wanyaland
---
cla-backend-go/events/event_data.go | 12 ++++-----
cla-backend-go/events/service.go | 38 ++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index d6d54d7b8..1bd7802a1 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -457,7 +457,7 @@ func (ed *CompanyACLRequestDeniedEventData) GetEventDetailsString(args *LogEvent
// GetEventDetailsString . . .
func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User with LF Username: %s added to the ACL for Company: %s by: %s.",
- ed.UserLFID, args.CompanyName, args.UserName)
+ args.LFUser.Name, args.CompanyName, args.UserName)
return data, true
}
@@ -725,14 +725,14 @@ func (ed *ContributorAssignCLADesignee) GetEventDetailsString(args *LogEventArgs
// GetEventDetailsString . . .
func (ed *UserConvertToContactData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s was converted to Contact state for Project: %s with ID: %s.",
- args.LfUsername, args.ProjectName, args.ProjectSFID)
+ args.LFUser.Name, args.ProjectName, args.ProjectSFID)
return data, true
}
// GetEventDetailsString . . .
func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s was assigned Scope: %s with Role: %s for Project: %s with ID: %s.",
- args.LfUsername,
+ args.LFUser.Name,
ed.Scope, ed.Role, args.ProjectName, args.ProjectSFID)
return data, true
}
@@ -980,7 +980,7 @@ func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEvent
// GetEventSummaryString . . .
func (ed *CompanyACLUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user with LF username %s was added to the access list for the company %s by the user %s.",
- ed.UserLFID, args.CompanyName, args.UserName)
+ args.LFUser.Name, args.CompanyName, args.UserName)
return data, true
}
@@ -1533,7 +1533,7 @@ func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs
// GetEventSummaryString . . .
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was converted to a contact", args.LfUsername)
+ data := fmt.Sprintf("The user %s was converted to a contact", args.LFUser.Name)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1549,7 +1549,7 @@ func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString . . .
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s", args.LfUsername, ed.Role)
+ data := fmt.Sprintf("The user %s was added to the role %s", args.LFUser.Name, ed.Role)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index efffffb43..b2ea502ce 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -11,6 +11,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+ user_service "github.com/communitybridge/easycla/cla-backend-go/v2/user-service"
+ userServiceModels "github.com/communitybridge/easycla/cla-backend-go/v2/user-service/models"
"github.com/sirupsen/logrus"
@@ -122,6 +124,7 @@ type LogEventArgs struct {
LfUsername string
UserName string
UserModel *models.User
+ LFUser *userServiceModels.User
CLAGroupID string
CLAGroupName string
@@ -316,7 +319,12 @@ func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
// Did we finally load the user model?
if userModel != nil {
args.UserModel = userModel
- args.UserName = userModel.Username
+ // Update username with LF Name value if exists ...
+ if args.LFUser != nil {
+ args.UserName = args.LFUser.Name
+ } else {
+ args.UserName = userModel.Username
+ }
args.UserID = userModel.UserID
args.LfUsername = userModel.LfUsername
} else {
@@ -326,6 +334,27 @@ func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
return nil
}
+func (s *service) loadLFUser(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "v1.events.service.loadLFUser",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if args.LFUser != nil {
+ return nil
+ }
+
+ if args.LfUsername != "" {
+ lfUser, lfUserErr := user_service.GetClient().GetUserByUsername(args.LfUsername)
+ if lfUserErr != nil || lfUser == nil {
+ log.WithFields(f).Warnf("unable to fetch LF User by username: %s", args.LfUsername)
+ return nil
+ }
+ args.LFUser = lfUser
+ }
+ return nil
+}
+
// loadDetails fetches and sets additional information into the data model required to fill out the event log entry
func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
@@ -355,6 +384,13 @@ func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
return err
}
+ log.WithFields(f).Debug("loading LF user details ...")
+ err = s.loadLFUser(ctx, args)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load LF User details...")
+ return err
+ }
+
log.WithFields(f).Debug("loading user details...")
err = s.loadUser(ctx, args)
if err != nil {
From f859ee8d2b618773dcedcd2657bdc27148459dc8 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Fri, 19 Mar 2021 00:07:50 +0200
Subject: [PATCH 0167/1276] [#2432] github v4 branch protection (#2788)
Signed-off-by: makkalot
---
cla-backend-go/Makefile | 3 +
cla-backend-go/gerrits/repository.go | 2 +-
.../github/branch_protection/interfaces.go | 32 +
.../github/branch_protection/mock.go | 133 ++++
.../branch_protection/protected_branch.go | 336 +++++++++
.../protected_branch_limiter.go | 114 +++
.../protected_branch_test.go | 375 ++++++++++
.../branch_protection/protected_branch_v4.go | 160 +++++
cla-backend-go/github/client.go | 3 +-
cla-backend-go/github/mock.go | 103 ---
cla-backend-go/github/protected_branch.go | 655 ------------------
.../github/protected_branch_test.go | 351 ----------
cla-backend-go/repositories/handlers.go | 6 +-
.../repositories/mock/mock_repository.go | 46 +-
.../repositories/mock/mock_service.go | 59 +-
cla-backend-go/repositories/repository.go | 6 +-
cla-backend-go/swagger/cla.v2.yaml | 5 +
cla-backend-go/tests/github_v4_test.go | 10 +-
cla-backend-go/utils/constants.go | 3 +
cla-backend-go/v2/dynamo_events/autoenable.go | 8 +-
.../v2/dynamo_events/github_organization.go | 23 +-
.../v2/dynamo_events/github_repository.go | 23 +-
cla-backend-go/v2/metrics/repository.go | 2 +-
cla-backend-go/v2/repositories/handlers.go | 23 +-
cla-backend-go/v2/repositories/service.go | 71 +-
25 files changed, 1335 insertions(+), 1217 deletions(-)
create mode 100644 cla-backend-go/github/branch_protection/interfaces.go
create mode 100644 cla-backend-go/github/branch_protection/mock.go
create mode 100644 cla-backend-go/github/branch_protection/protected_branch.go
create mode 100644 cla-backend-go/github/branch_protection/protected_branch_limiter.go
create mode 100644 cla-backend-go/github/branch_protection/protected_branch_test.go
create mode 100644 cla-backend-go/github/branch_protection/protected_branch_v4.go
delete mode 100644 cla-backend-go/github/mock.go
delete mode 100644 cla-backend-go/github/protected_branch.go
delete mode 100644 cla-backend-go/github/protected_branch_test.go
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 862bb8d54..e4579a430 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -179,6 +179,9 @@ mock:
@cd $(MAKEFILE_DIR) && mkdir -p repositories/mock
@cd $(MAKEFILE_DIR) && mockgen -copyright_file=copyright-header.txt -source=repositories/service.go -package=mock -destination=repositories/mock/mock_service.go
@cd $(MAKEFILE_DIR) && mockgen -copyright_file=copyright-header.txt -source=repositories/repository.go -package=mock -destination=repositories/mock/mock_repository.go
+ # mocks for github
+ @cd $(MAKEFILE_DIR) && mockgen -copyright_file=copyright-header.txt -package=branch_protection -destination=github/branch_protection/mock.go -self_package=github.com/communitybridge/easycla/cla-backend-go/github/branch_protection github.com/communitybridge/easycla/cla-backend-go/github/branch_protection CombinedRepository
+
run:
go run main.go
diff --git a/cla-backend-go/gerrits/repository.go b/cla-backend-go/gerrits/repository.go
index d93a21e5b..57c2c29a8 100644
--- a/cla-backend-go/gerrits/repository.go
+++ b/cla-backend-go/gerrits/repository.go
@@ -32,7 +32,7 @@ var (
ErrGerritNotFound = errors.New("gerrit not found")
)
-// Repository defines functions of Repositories
+// Repository defines functions of V3Repositories
type Repository interface {
AddGerrit(ctx context.Context, input *models.Gerrit) (*models.Gerrit, error)
GetGerrit(ctx context.Context, gerritID string) (*models.Gerrit, error)
diff --git a/cla-backend-go/github/branch_protection/interfaces.go b/cla-backend-go/github/branch_protection/interfaces.go
new file mode 100644
index 000000000..847b3d250
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/interfaces.go
@@ -0,0 +1,32 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package branch_protection
+
+import (
+ "context"
+
+ "github.com/google/go-github/v33/github"
+ "github.com/shurcooL/githubv4"
+)
+
+// V3Repositories is part of the interface working with github repositories, it's inside of the github client
+// It's extracted here as interface so we can mock that functionality in the tests.
+type V3Repositories interface {
+ ListByOrg(ctx context.Context, org string, opt *github.RepositoryListByOrgOptions) ([]*github.Repository, *github.Response, error)
+ Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error)
+}
+
+// V4BranchProtectionRepository has v4 (graphQL) branch protection functionality
+type V4BranchProtectionRepository interface {
+ GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error)
+ CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error)
+ UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error)
+ GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error)
+}
+
+//CombinedRepository is combination of V3Repositories and V4BranchProtectionRepository
+type CombinedRepository interface {
+ V3Repositories
+ V4BranchProtectionRepository
+}
diff --git a/cla-backend-go/github/branch_protection/mock.go b/cla-backend-go/github/branch_protection/mock.go
new file mode 100644
index 000000000..456925c0e
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/mock.go
@@ -0,0 +1,133 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+//
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/communitybridge/easycla/cla-backend-go/github/branch_protection (interfaces: CombinedRepository)
+
+// Package branch_protection is a generated GoMock package.
+package branch_protection
+
+import (
+ context "context"
+ reflect "reflect"
+
+ gomock "github.com/golang/mock/gomock"
+ github "github.com/google/go-github/v33/github"
+ githubv4 "github.com/shurcooL/githubv4"
+)
+
+// MockCombinedRepository is a mock of CombinedRepository interface
+type MockCombinedRepository struct {
+ ctrl *gomock.Controller
+ recorder *MockCombinedRepositoryMockRecorder
+}
+
+// MockCombinedRepositoryMockRecorder is the mock recorder for MockCombinedRepository
+type MockCombinedRepositoryMockRecorder struct {
+ mock *MockCombinedRepository
+}
+
+// NewMockCombinedRepository creates a new mock instance
+func NewMockCombinedRepository(ctrl *gomock.Controller) *MockCombinedRepository {
+ mock := &MockCombinedRepository{ctrl: ctrl}
+ mock.recorder = &MockCombinedRepositoryMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockCombinedRepository) EXPECT() *MockCombinedRepositoryMockRecorder {
+ return m.recorder
+}
+
+// CreateBranchProtection mocks base method
+func (m *MockCombinedRepository) CreateBranchProtection(arg0 context.Context, arg1 *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CreateBranchProtection", arg0, arg1)
+ ret0, _ := ret[0].(*CreateRepoBranchProtectionMutation)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// CreateBranchProtection indicates an expected call of CreateBranchProtection
+func (mr *MockCombinedRepositoryMockRecorder) CreateBranchProtection(arg0, arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBranchProtection", reflect.TypeOf((*MockCombinedRepository)(nil).CreateBranchProtection), arg0, arg1)
+}
+
+// Get mocks base method
+func (m *MockCombinedRepository) Get(arg0 context.Context, arg1, arg2 string) (*github.Repository, *github.Response, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2)
+ ret0, _ := ret[0].(*github.Repository)
+ ret1, _ := ret[1].(*github.Response)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
+}
+
+// Get indicates an expected call of Get
+func (mr *MockCombinedRepositoryMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCombinedRepository)(nil).Get), arg0, arg1, arg2)
+}
+
+// GetRepositoryBranchProtections mocks base method
+func (m *MockCombinedRepository) GetRepositoryBranchProtections(arg0 context.Context, arg1, arg2 string) (*RepoBranchProtectionQueryResult, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetRepositoryBranchProtections", arg0, arg1, arg2)
+ ret0, _ := ret[0].(*RepoBranchProtectionQueryResult)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRepositoryBranchProtections indicates an expected call of GetRepositoryBranchProtections
+func (mr *MockCombinedRepositoryMockRecorder) GetRepositoryBranchProtections(arg0, arg1, arg2 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryBranchProtections", reflect.TypeOf((*MockCombinedRepository)(nil).GetRepositoryBranchProtections), arg0, arg1, arg2)
+}
+
+// GetRepositoryIDFromName mocks base method
+func (m *MockCombinedRepository) GetRepositoryIDFromName(arg0 context.Context, arg1, arg2 string) (string, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetRepositoryIDFromName", arg0, arg1, arg2)
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRepositoryIDFromName indicates an expected call of GetRepositoryIDFromName
+func (mr *MockCombinedRepositoryMockRecorder) GetRepositoryIDFromName(arg0, arg1, arg2 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryIDFromName", reflect.TypeOf((*MockCombinedRepository)(nil).GetRepositoryIDFromName), arg0, arg1, arg2)
+}
+
+// ListByOrg mocks base method
+func (m *MockCombinedRepository) ListByOrg(arg0 context.Context, arg1 string, arg2 *github.RepositoryListByOrgOptions) ([]*github.Repository, *github.Response, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "ListByOrg", arg0, arg1, arg2)
+ ret0, _ := ret[0].([]*github.Repository)
+ ret1, _ := ret[1].(*github.Response)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
+}
+
+// ListByOrg indicates an expected call of ListByOrg
+func (mr *MockCombinedRepositoryMockRecorder) ListByOrg(arg0, arg1, arg2 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListByOrg", reflect.TypeOf((*MockCombinedRepository)(nil).ListByOrg), arg0, arg1, arg2)
+}
+
+// UpdateBranchProtection mocks base method
+func (m *MockCombinedRepository) UpdateBranchProtection(arg0 context.Context, arg1 *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateBranchProtection", arg0, arg1)
+ ret0, _ := ret[0].(*UpdateRepoBranchProtectionMutation)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateBranchProtection indicates an expected call of UpdateBranchProtection
+func (mr *MockCombinedRepositoryMockRecorder) UpdateBranchProtection(arg0, arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateBranchProtection", reflect.TypeOf((*MockCombinedRepository)(nil).UpdateBranchProtection), arg0, arg1)
+}
diff --git a/cla-backend-go/github/branch_protection/protected_branch.go b/cla-backend-go/github/branch_protection/protected_branch.go
new file mode 100644
index 000000000..ea56ef5a3
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/protected_branch.go
@@ -0,0 +1,336 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package branch_protection
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/shurcooL/githubv4"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
+ githubpkg "github.com/google/go-github/v33/github"
+)
+
+const (
+ // DefaultBranchName is the default branch we'll be working with if not specified
+ DefaultBranchName = "main"
+)
+
+var (
+ // ErrBranchNotProtected indicates the situation where the branch is not enabled for protection on github side
+ ErrBranchNotProtected = errors.New("not protected")
+)
+
+type combinedRepositoryProvider struct {
+ V3Repositories
+ V4BranchProtectionRepository
+}
+
+type branchProtectionRepositoryConfig struct {
+ enableBlockingLimiter bool
+ enableNonBlockingLimiter bool
+}
+
+// BranchProtectionRepositoryOption enables optional parameters to BranchProtectionRepository
+type BranchProtectionRepositoryOption func(config *branchProtectionRepositoryConfig)
+
+// EnableBlockingLimiter enables the blocking limiter
+func EnableBlockingLimiter() BranchProtectionRepositoryOption {
+ return func(config *branchProtectionRepositoryConfig) {
+ config.enableBlockingLimiter = true
+ }
+}
+
+// EnableNonBlockingLimiter enables the non-blocking limiter
+func EnableNonBlockingLimiter() BranchProtectionRepositoryOption {
+ return func(config *branchProtectionRepositoryConfig) {
+ config.enableNonBlockingLimiter = true
+ }
+}
+
+// BranchProtectionRepository contains helper methods interacting with github api related to branch protection
+type BranchProtectionRepository struct {
+ combinedRepo CombinedRepository
+}
+
+// NewBranchProtectionRepository creates a new BranchProtectionRepository
+func NewBranchProtectionRepository(installationID int64, opts ...BranchProtectionRepositoryOption) (*BranchProtectionRepository, error) {
+ v4BranchProtectionRepo, err := NewBranchProtectionRepositoryV4(installationID)
+ if err != nil {
+ return nil, fmt.Errorf("initializing v4 github client failed : %v", err)
+ }
+
+ v3Client, err := github.NewGithubAppClient(installationID)
+ if err != nil {
+ return nil, fmt.Errorf("initializing v3 github client failed : %v", err)
+ }
+
+ combinedRepo := combinedRepositoryProvider{
+ V3Repositories: v3Client.Repositories,
+ V4BranchProtectionRepository: v4BranchProtectionRepo,
+ }
+
+ return newBranchProtectionRepository(combinedRepo, opts...), nil
+}
+
+func newBranchProtectionRepository(combinedRepo CombinedRepository, opts ...BranchProtectionRepositoryOption) *BranchProtectionRepository {
+ config := &branchProtectionRepositoryConfig{}
+ for _, o := range opts {
+ o(config)
+ }
+
+ if config.enableNonBlockingLimiter {
+ combinedRepo = NewNonBlockLimiterRepositories(combinedRepo)
+ } else if config.enableBlockingLimiter {
+ combinedRepo = NewBlockLimiterRepositories(combinedRepo)
+ }
+
+ return &BranchProtectionRepository{
+ combinedRepo: combinedRepo,
+ }
+}
+
+// GetOwnerName retrieves the owner name of the given org and repo name
+func (bp *BranchProtectionRepository) GetOwnerName(ctx context.Context, orgName, repoName string) (string, error) {
+ repoName = CleanGithubRepoName(repoName)
+ log.Debugf("GetOwnerName : getting owner name for org %s and repoName : %s", orgName, repoName)
+ listOpt := &githubpkg.RepositoryListByOrgOptions{
+ ListOptions: githubpkg.ListOptions{
+ PerPage: 30,
+ },
+ }
+ for {
+ repos, resp, err := bp.combinedRepo.ListByOrg(ctx, orgName, listOpt)
+ if err != nil {
+ if ok, wErr := github.CheckAndWrapForKnownErrors(resp, err); ok {
+ return "", wErr
+ }
+ return "", err
+ }
+
+ if len(repos) == 0 {
+ log.Warnf("GetOwnerName : no repos found under orgName : %s (maybe no access ?)", orgName)
+ return "", nil
+ }
+
+ for _, repo := range repos {
+ if *repo.Name == repoName {
+ if repo.Owner != nil {
+ owner := *repo.Owner
+ return *owner.Login, nil
+ }
+ }
+ }
+
+ // means we're at the end of it so exit
+ if resp.NextPage == 0 {
+ log.Warnf("GetOwnerName : owner name not found for org : %s and repo : %s", orgName, repoName)
+ return "", nil
+ }
+
+ // set it to the next page
+ listOpt.Page = resp.NextPage
+ }
+}
+
+// GetDefaultBranchForRepo helps with pulling the default branch for the given repo
+func (bp *BranchProtectionRepository) GetDefaultBranchForRepo(ctx context.Context, owner, repoName string) (string, error) {
+ repoName = CleanGithubRepoName(repoName)
+ repo, resp, err := bp.combinedRepo.Get(ctx, owner, repoName)
+ if err != nil {
+ if ok, wErr := github.CheckAndWrapForKnownErrors(resp, err); ok {
+ return "", wErr
+ }
+ return "", err
+ }
+
+ var defaultBranch string
+ if repo.DefaultBranch == nil {
+ defaultBranch = DefaultBranchName
+ } else {
+ defaultBranch = *repo.DefaultBranch
+ }
+
+ return defaultBranch, nil
+}
+
+// GetProtectedBranch fetches the protected branch details
+func (bp *BranchProtectionRepository) GetProtectedBranch(ctx context.Context, owner, repoName, protectedBranchName string) (*BranchProtectionRule, error) {
+ repoName = CleanGithubRepoName(repoName)
+ branchProtections, err := bp.combinedRepo.GetRepositoryBranchProtections(ctx, owner, repoName)
+ if err != nil {
+ return nil, fmt.Errorf("fetching repo protections for owner : %s and repoName : %s failed : %w", owner, repoName, err)
+ }
+
+ // it's not found this pattern or branch
+ if branchProtections.RepositoryOwner.Repository.BranchProtectionRules.TotalCount == 0 {
+ return nil, ErrBranchNotProtected
+ }
+
+ for _, protection := range branchProtections.RepositoryOwner.Repository.BranchProtectionRules.Nodes {
+ if protection.Pattern == protectedBranchName {
+ return &protection, nil
+ }
+ }
+
+ return nil, ErrBranchNotProtected
+}
+
+//EnableBranchProtection enables branch protection if not enabled and makes sure passed arguments such as enforceAdmin
+//statusChecks are applied. The operation makes sure it doesn't override the existing checks.
+func (bp *BranchProtectionRepository) EnableBranchProtection(ctx context.Context, owner, repoName, branchName string, enforceAdmin bool, enableStatusChecks, disableStatusChecks []string) error {
+ repoName = CleanGithubRepoName(repoName)
+
+ // fetch the existing ones
+ queryResult, err := bp.combinedRepo.GetRepositoryBranchProtections(ctx, owner, repoName)
+ if err != nil {
+ return err
+ }
+
+ currentProtections := queryResult.RepositoryOwner.Repository.BranchProtectionRules.Nodes
+ repoID := queryResult.RepositoryOwner.Repository.ID
+
+ createInput, updateInput := prepareBranchProtectionMutation(repoID, currentProtections, &BranchProtectionRule{
+ Pattern: branchName,
+ RequiredStatusCheckContexts: enableStatusChecks,
+ RequiresStatusChecks: true,
+ IsAdminEnforced: enforceAdmin,
+ AllowsDeletions: false,
+ AllowsForcePushes: false,
+ })
+ if createInput != nil {
+ _, createErr := bp.combinedRepo.CreateBranchProtection(ctx, createInput)
+ if createErr != nil {
+ return fmt.Errorf("creating new branch protection rule for owner : %s and repo : %s failed : %v", owner, repoName, createErr)
+ }
+ return nil
+ }
+
+ _, err = bp.combinedRepo.UpdateBranchProtection(ctx, updateInput)
+ if err != nil {
+ return fmt.Errorf("updating current branch rule for owner : %s and repo name : %s, failed : %v", owner, repoName, err)
+ }
+
+ return nil
+}
+
+//mergeStatusChecks merges the current checks with the new ones and disable the ones that are specified
+func mergeStatusChecks(currentChecks []string, enableContexts, disableContexts []string) []string {
+
+ // seems github api is not happy with nils for arrays ;)
+ if len(enableContexts) == 0 {
+ enableContexts = []string{}
+ }
+
+ if currentChecks == nil {
+ currentChecks = []string{}
+ }
+
+ finalContexts := []string{}
+ uniqueEnableContexts := map[string]bool{}
+
+ for _, c := range currentChecks {
+ // first disable the ones we're not interested into
+ found := false
+ if len(disableContexts) > 0 {
+ for _, disableContext := range disableContexts {
+ if disableContext == c {
+ found = true
+ break
+ }
+ }
+ }
+
+ if found {
+ continue
+ }
+
+ uniqueEnableContexts[c] = true
+ finalContexts = append(finalContexts, c)
+ }
+
+ for _, c := range enableContexts {
+ if uniqueEnableContexts[c] {
+ continue
+ }
+
+ uniqueEnableContexts[c] = true
+ finalContexts = append(finalContexts, c)
+ }
+
+ return finalContexts
+}
+
+// prepareBranchProtectionMutation creates the mutation input objects to modify the branch protection
+// the logic is pulled out so we can unit test it without mocking the connections
+func prepareBranchProtectionMutation(repoID string, currentProtections []BranchProtectionRule, input *BranchProtectionRule) (*githubv4.CreateBranchProtectionRuleInput, *githubv4.UpdateBranchProtectionRuleInput) {
+ var foundBranchProtectionRule *BranchProtectionRule
+ if len(currentProtections) > 0 {
+ for _, protection := range currentProtections {
+ if protection.Pattern == input.Pattern {
+ currentProtection := protection
+ foundBranchProtectionRule = ¤tProtection
+ break
+ }
+ }
+ }
+
+ if foundBranchProtectionRule == nil {
+ var statusChecks []githubv4.String
+ for _, check := range input.RequiredStatusCheckContexts {
+ statusChecks = append(statusChecks, githubv4.String(check))
+ }
+
+ createInput := githubv4.CreateBranchProtectionRuleInput{
+ RepositoryID: repoID,
+ Pattern: githubv4.String(input.Pattern),
+ AllowsForcePushes: githubv4.NewBoolean(false),
+ AllowsDeletions: githubv4.NewBoolean(false),
+ IsAdminEnforced: githubv4.NewBoolean(githubv4.Boolean(input.IsAdminEnforced)),
+ RequiresStatusChecks: githubv4.NewBoolean(true),
+ RequiredStatusCheckContexts: &statusChecks,
+ }
+
+ return &createInput, nil
+ }
+
+ // it's an existing one we need to update and make sure all of them it's at state we want it
+ mergedStatusChecks := mergeStatusChecks(foundBranchProtectionRule.RequiredStatusCheckContexts, input.RequiredStatusCheckContexts, nil)
+ var finalStatusChecks []githubv4.String
+
+ for _, check := range mergedStatusChecks {
+ finalStatusChecks = append(finalStatusChecks, githubv4.String(check))
+ }
+
+ updateInput := githubv4.UpdateBranchProtectionRuleInput{
+ BranchProtectionRuleID: githubv4.ID(foundBranchProtectionRule.ID),
+ Pattern: githubv4.NewString(githubv4.String(input.Pattern)),
+ IsAdminEnforced: githubv4.NewBoolean(githubv4.Boolean(input.IsAdminEnforced)),
+ RequiresStatusChecks: githubv4.NewBoolean(true),
+ AllowsDeletions: githubv4.NewBoolean(false),
+ AllowsForcePushes: githubv4.NewBoolean(false),
+ RequiredStatusCheckContexts: &finalStatusChecks,
+ }
+
+ return nil, &updateInput
+}
+
+//IsEnforceAdminEnabled checks if enforce admin option is enabled for the branch protection
+func IsEnforceAdminEnabled(protection *BranchProtectionRule) bool {
+ return protection.IsAdminEnforced
+}
+
+// CleanGithubRepoName removes the orgname if existing in the string
+func CleanGithubRepoName(githubRepoName string) string {
+ if strings.Contains(githubRepoName, "/") {
+ parts := strings.Split(githubRepoName, "/")
+ githubRepoName = parts[len(parts)-1]
+ }
+ return githubRepoName
+}
diff --git a/cla-backend-go/github/branch_protection/protected_branch_limiter.go b/cla-backend-go/github/branch_protection/protected_branch_limiter.go
new file mode 100644
index 000000000..7166e9de8
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/protected_branch_limiter.go
@@ -0,0 +1,114 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package branch_protection
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ githubpkg "github.com/google/go-github/v33/github"
+ "github.com/shurcooL/githubv4"
+ "go.uber.org/ratelimit"
+ "golang.org/x/time/rate"
+)
+
+// rate limiting variables
+var (
+ // blockingRateLimit is useful for background tasks where the interaction is more predictable
+ blockingRateLimit = ratelimit.New(2)
+ // nonBlockingRateLimit is preferred when the github methods would be called realtime
+ // in this case we can call Allow method to check if can proceed or return error
+ nonBlockingRateLimit = rate.NewLimiter(2, 5)
+)
+
+type blockingRateLimitRepositories struct {
+ CombinedRepository
+}
+
+// NewBlockLimiterRepositories returns a new instance of V3Repositories interface with blocking rate limiting
+// where when the limit is reached the next call blocks till the bucket is ready again
+func NewBlockLimiterRepositories(repo CombinedRepository) CombinedRepository {
+ return blockingRateLimitRepositories{
+ CombinedRepository: repo,
+ }
+}
+
+func (b blockingRateLimitRepositories) ListByOrg(ctx context.Context, org string, opt *githubpkg.RepositoryListByOrgOptions) ([]*githubpkg.Repository, *githubpkg.Response, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.ListByOrg(ctx, org, opt)
+}
+
+func (b blockingRateLimitRepositories) Get(ctx context.Context, owner, repo string) (*githubpkg.Repository, *githubpkg.Response, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.Get(ctx, owner, repo)
+}
+
+func (b blockingRateLimitRepositories) GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.GetRepositoryBranchProtections(ctx, repositoryOwner, repositoryName)
+}
+func (b blockingRateLimitRepositories) CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.CreateBranchProtection(ctx, input)
+}
+func (b blockingRateLimitRepositories) UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.UpdateBranchProtection(ctx, input)
+}
+func (b blockingRateLimitRepositories) GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error) {
+ blockingRateLimit.Take()
+ return b.CombinedRepository.GetRepositoryIDFromName(ctx, repositoryOwner, repositoryName)
+}
+
+type nonBlockingRateLimitRepositories struct {
+ CombinedRepository
+}
+
+// NewNonBlockLimiterRepositories returns a new instance of V3Repositories interface with non blocking rate limiting
+func NewNonBlockLimiterRepositories(repo CombinedRepository) CombinedRepository {
+ return nonBlockingRateLimitRepositories{CombinedRepository: repo}
+}
+
+func (nb nonBlockingRateLimitRepositories) ListByOrg(ctx context.Context, org string, opt *githubpkg.RepositoryListByOrgOptions) ([]*githubpkg.Repository, *githubpkg.Response, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.ListByOrg(ctx, org, opt)
+ }
+ return nil, nil, fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
+
+func (nb nonBlockingRateLimitRepositories) Get(ctx context.Context, owner, repo string) (*githubpkg.Repository, *githubpkg.Response, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.Get(ctx, owner, repo)
+ }
+ return nil, nil, fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
+
+func (nb nonBlockingRateLimitRepositories) GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.GetRepositoryBranchProtections(ctx, repositoryOwner, repositoryName)
+ }
+ return nil, fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
+
+func (nb nonBlockingRateLimitRepositories) CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.CreateBranchProtection(ctx, input)
+ }
+ return nil, fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
+
+func (nb nonBlockingRateLimitRepositories) UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.UpdateBranchProtection(ctx, input)
+ }
+ return nil, fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
+
+func (nb nonBlockingRateLimitRepositories) GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error) {
+ if nonBlockingRateLimit.Allow() {
+ return nb.CombinedRepository.GetRepositoryIDFromName(ctx, repositoryOwner, repositoryName)
+ }
+ return "", fmt.Errorf("too many requests : %w", github.ErrRateLimited)
+}
diff --git a/cla-backend-go/github/branch_protection/protected_branch_test.go b/cla-backend-go/github/branch_protection/protected_branch_test.go
new file mode 100644
index 000000000..96e0ceec4
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/protected_branch_test.go
@@ -0,0 +1,375 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package branch_protection
+
+import (
+ "context"
+ "errors"
+ "testing"
+ "time"
+
+ "github.com/bmizerany/assert"
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/golang/mock/gomock"
+ "github.com/shurcooL/githubv4"
+)
+
+func V4StringSlice(val ...string) *[]githubv4.String {
+ var v []githubv4.String
+ for _, c := range val {
+ v = append(v, githubv4.String(c))
+ }
+
+ return &v
+}
+
+// TestMergeStatusChecks tests the functionality of where we enable/disable checks
+func TestMergeStatusChecks(t *testing.T) {
+
+ testCases := []struct {
+ Name string
+ currentChecks []string
+ expectedChecks []string
+ enableContexts []string
+ disableContexts []string
+ }{
+ {
+ Name: "all empty",
+ expectedChecks: []string{},
+ },
+ {
+ Name: "empty state enable",
+ expectedChecks: []string{"EasyCLA"},
+ enableContexts: []string{"EasyCLA"},
+ },
+ {
+ Name: "preserve existing enable more",
+ currentChecks: []string{"travis-ci"},
+ expectedChecks: []string{"travis-ci", "EasyCLA"},
+ enableContexts: []string{"EasyCLA"},
+ },
+ {
+ Name: "preserve existing disable some",
+ currentChecks: []string{"travis-ci", "EasyCLA"},
+ expectedChecks: []string{"travis-ci"},
+ disableContexts: []string{"EasyCLA"},
+ },
+ {
+ Name: "add and remove in same operation",
+ currentChecks: []string{"travis-ci", "DCO", "EasyCLA"},
+ expectedChecks: []string{"travis-ci", "EasyCLA", "CodeQL"},
+ enableContexts: []string{"CodeQL"},
+ disableContexts: []string{"DCO"},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(tt *testing.T) {
+ result := mergeStatusChecks(tc.currentChecks, tc.enableContexts, tc.disableContexts)
+ assert.Equal(tt, tc.expectedChecks, result)
+ })
+ }
+}
+
+func TestEnableBranchProtection(t *testing.T) {
+ owner := "johnenable"
+ repo := "johnsrepoenable"
+ branchName := DefaultBranchName
+
+ testCases := []struct {
+ Name string
+ Checks []string
+ CurrentProtections *RepoBranchProtectionQueryResult
+ CreateProtectionRequest *githubv4.CreateBranchProtectionRuleInput
+ UpdateProtectionRequest *githubv4.UpdateBranchProtectionRuleInput
+ Err error
+ }{
+ {
+ Name: "success",
+ Checks: []string{"easyCLA"},
+ CurrentProtections: &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{},
+ }},
+ },
+ CreateProtectionRequest: &githubv4.CreateBranchProtectionRuleInput{
+ RepositoryID: githubv4.ID("repoIDValue"),
+ Pattern: githubv4.String(branchName),
+ AllowsForcePushes: githubv4.NewBoolean(false),
+ AllowsDeletions: githubv4.NewBoolean(false),
+ IsAdminEnforced: githubv4.NewBoolean(true),
+ RequiresStatusChecks: githubv4.NewBoolean(true),
+ RequiredStatusCheckContexts: V4StringSlice("easyCLA"),
+ },
+ },
+ {
+ Name: "preserve existing checks",
+ Checks: []string{"easyCLA"},
+ CurrentProtections: &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{
+ TotalCount: 1,
+ Nodes: []BranchProtectionRule{
+ {
+ ID: "branchProtectionID",
+ Pattern: branchName,
+ RequiredStatusCheckContexts: []string{
+ "circle/ci",
+ },
+ },
+ },
+ },
+ }},
+ },
+ UpdateProtectionRequest: &githubv4.UpdateBranchProtectionRuleInput{
+ BranchProtectionRuleID: githubv4.ID("branchProtectionID"),
+ Pattern: githubv4.NewString(githubv4.String(branchName)),
+ AllowsForcePushes: githubv4.NewBoolean(false),
+ AllowsDeletions: githubv4.NewBoolean(false),
+ IsAdminEnforced: githubv4.NewBoolean(true),
+ RequiresStatusChecks: githubv4.NewBoolean(true),
+ RequiredStatusCheckContexts: V4StringSlice("circle/ci", "easyCLA"),
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(tt *testing.T) {
+ ctrl := gomock.NewController(tt)
+ defer ctrl.Finish()
+
+ m := NewMockCombinedRepository(ctrl)
+ m.
+ EXPECT().
+ GetRepositoryBranchProtections(gomock.Any(), owner, repo).
+ Return(tc.CurrentProtections, nil)
+
+ if tc.CreateProtectionRequest != nil {
+ m.
+ EXPECT().
+ CreateBranchProtection(gomock.Any(), tc.CreateProtectionRequest).
+ Return(nil, nil)
+ }
+
+ if tc.UpdateProtectionRequest != nil {
+ m.
+ EXPECT().
+ UpdateBranchProtection(gomock.Any(), tc.UpdateProtectionRequest).
+ Return(nil, nil)
+ }
+
+ branchProtectionRepo := newBranchProtectionRepository(m)
+ err := branchProtectionRepo.EnableBranchProtection(context.Background(), owner, repo, branchName, true, tc.Checks, nil)
+ if err != nil {
+ tt.Errorf("enable branch proteciton failed : %v", err)
+ }
+ })
+ }
+}
+
+func TestNonBlockingRateLimitRepositories_GetBranchProtection(t *testing.T) {
+ owner := "johnblocking"
+ repo := "johnsrepoblocking"
+ branchName := DefaultBranchName
+
+ t.Run("no limit reached", func(tt *testing.T) {
+ ctrl := gomock.NewController(tt)
+ defer ctrl.Finish()
+
+ protection := &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{
+ TotalCount: 1,
+ Nodes: []BranchProtectionRule{
+ {
+ ID: "branchProtectionID",
+ Pattern: branchName,
+ RequiredStatusCheckContexts: []string{
+ "circle/ci",
+ },
+ },
+ },
+ },
+ }},
+ }
+
+ m := NewMockCombinedRepository(ctrl)
+ m.
+ EXPECT().
+ GetRepositoryBranchProtections(gomock.Any(), owner, repo).
+ Return(protection, nil)
+
+ nonBlockLimitRepo := newBranchProtectionRepository(m, EnableNonBlockingLimiter())
+ p, err := nonBlockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
+ if err != nil {
+ tt.Errorf("no error expected : %v", err)
+ }
+ assert.Equal(tt, protection.RepositoryOwner.Repository.BranchProtectionRules.Nodes[0], *p)
+ })
+
+ t.Run("limit reached", func(tt *testing.T) {
+ ctrl := gomock.NewController(tt)
+ defer ctrl.Finish()
+
+ protection := &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{
+ TotalCount: 1,
+ Nodes: []BranchProtectionRule{
+ {
+ ID: "branchProtectionID",
+ Pattern: branchName,
+ RequiredStatusCheckContexts: []string{
+ "circle/ci",
+ },
+ },
+ },
+ },
+ }},
+ }
+
+ m := NewMockCombinedRepository(ctrl)
+ m.
+ EXPECT().
+ GetRepositoryBranchProtections(gomock.Any(), owner, repo).
+ Return(protection, nil).AnyTimes()
+
+ nonBlockLimitRepo := newBranchProtectionRepository(m, EnableNonBlockingLimiter())
+ // call it 100 times in loop to make it fail
+ var expectedErr error
+ for i := 0; i < 100; i++ {
+ _, err := nonBlockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
+ if err != nil {
+ expectedErr = err
+ break
+ }
+ }
+
+ if expectedErr == nil {
+ tt.Fatalf("no error returned")
+ return
+ }
+
+ if !errors.Is(expectedErr, github.ErrRateLimited) {
+ tt.Fatalf("was expecting ErrRateLimited got : %v", expectedErr)
+ return
+ }
+ })
+}
+
+func TestBlockingRateLimitRepositories_GetBranchProtection(t *testing.T) {
+ owner := "john"
+ repo := "johnsrepo"
+ branchName := DefaultBranchName
+
+ t.Run("no limit reached", func(tt *testing.T) {
+ ctrl := gomock.NewController(tt)
+ defer ctrl.Finish()
+
+ protection := &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{
+ TotalCount: 1,
+ Nodes: []BranchProtectionRule{
+ {
+ ID: "branchProtectionID",
+ Pattern: branchName,
+ RequiredStatusCheckContexts: []string{
+ "circle/ci",
+ },
+ },
+ },
+ },
+ }},
+ }
+
+ m := NewMockCombinedRepository(ctrl)
+ m.
+ EXPECT().
+ GetRepositoryBranchProtections(gomock.Any(), owner, repo).
+ Return(protection, nil)
+
+ blockLimitRepo := newBranchProtectionRepository(m, EnableBlockingLimiter())
+ p, err := blockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
+ if err != nil {
+ tt.Errorf("no error expected : %v", err)
+ }
+ assert.Equal(tt, protection.RepositoryOwner.Repository.BranchProtectionRules.Nodes[0], *p)
+ })
+
+ t.Run("limit reached", func(tt *testing.T) {
+ ctrl := gomock.NewController(tt)
+ defer ctrl.Finish()
+
+ protection := &RepoBranchProtectionQueryResult{
+ RepositoryOwner: struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ }{Repository: BranchProtectionRuleRepositoryParam{
+ Name: "repoNameValue",
+ ID: "repoIDValue",
+ BranchProtectionRules: BranchProtectionRuleQueryParam{
+ TotalCount: 1,
+ Nodes: []BranchProtectionRule{
+ {
+ ID: "branchProtectionID",
+ Pattern: branchName,
+ RequiredStatusCheckContexts: []string{
+ "circle/ci",
+ },
+ },
+ },
+ },
+ }},
+ }
+
+ m := NewMockCombinedRepository(ctrl)
+ m.
+ EXPECT().
+ GetRepositoryBranchProtections(gomock.Any(), owner, repo).
+ Return(protection, nil).AnyTimes()
+
+ blockLimitRepo := newBranchProtectionRepository(m, EnableBlockingLimiter())
+
+ // call it 100 times in loop to make it fail
+ var expectedErr error
+ start := time.Now()
+ for i := 0; i < 10; i++ {
+ _, err := blockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
+ if err != nil {
+ expectedErr = err
+ break
+ }
+ }
+ elapsed := time.Since(start)
+
+ if expectedErr != nil {
+ tt.Fatalf("no error was expected got : %v", expectedErr)
+ return
+ }
+
+ if elapsed < 4*time.Second {
+ tt.Fatalf("is rate limit enabled")
+ }
+ })
+}
diff --git a/cla-backend-go/github/branch_protection/protected_branch_v4.go b/cla-backend-go/github/branch_protection/protected_branch_v4.go
new file mode 100644
index 000000000..20ce3ca71
--- /dev/null
+++ b/cla-backend-go/github/branch_protection/protected_branch_v4.go
@@ -0,0 +1,160 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package branch_protection
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/shurcooL/githubv4"
+)
+
+// BranchProtectionRule is the data structure that's used to reflect the remote github branch protection rule
+type BranchProtectionRule struct {
+ ID string
+ Pattern string
+ RequiredStatusCheckContexts []string
+ RequiresStatusChecks bool
+ IsAdminEnforced bool
+ AllowsDeletions bool
+ AllowsForcePushes bool
+}
+
+// BranchProtectionRuleQueryParam is part of RepoBranchProtectionQueryResult extracted here so can
+// easily be initialized
+type BranchProtectionRuleQueryParam struct {
+ TotalCount int
+ Nodes []BranchProtectionRule
+}
+
+// BranchProtectionRuleRepositoryParam is part of RepoBranchProtectionQueryResult extracted here so can
+// easily be initialized
+type BranchProtectionRuleRepositoryParam struct {
+ Name string
+ ID string
+ BranchProtectionRules BranchProtectionRuleQueryParam `graphql:"branchProtectionRules(first:10)"`
+}
+
+// RepoBranchProtectionQueryResult is the query which queries for given owner and repository name
+type RepoBranchProtectionQueryResult struct {
+ RepositoryOwner struct {
+ Repository BranchProtectionRuleRepositoryParam `graphql:"repository(name: $name)"`
+ } `graphql:"repositoryOwner(login: $login)"`
+}
+
+// CreateRepoBranchProtectionMutation adds a new branch protection rule
+type CreateRepoBranchProtectionMutation struct {
+ CreateBranchProtectionRule struct {
+ BranchProtectionRule struct {
+ Repository struct {
+ Name string
+ }
+ Pattern string
+ IsAdminEnforced bool
+ RequiresStatusChecks bool
+ AllowsDeletions bool
+ AllowsForcePushes bool
+ }
+ } `graphql:"createBranchProtectionRule(input: $input)"`
+}
+
+// UpdateRepoBranchProtectionMutation updates existing branch protection rule
+type UpdateRepoBranchProtectionMutation struct {
+ UpdateBranchProtectionRule struct {
+ BranchProtectionRule struct {
+ Repository struct {
+ Name string
+ }
+ Pattern string
+ IsAdminEnforced bool
+ RequiresStatusChecks bool
+ AllowsDeletions bool
+ AllowsForcePushes bool
+ }
+ } `graphql:"updateBranchProtectionRule(input: $input)"`
+}
+
+type branchProtectionRepositoryV4 struct {
+ client *githubv4.Client
+}
+
+// NewBranchProtectionRepositoryV4 creates a new branchProtectionRepositoryV4
+func NewBranchProtectionRepositoryV4(installationID int64) (*branchProtectionRepositoryV4, error) {
+ client, clientErr := github.NewGithubV4AppClient(installationID)
+ if clientErr != nil {
+ return nil, clientErr
+ }
+ return &branchProtectionRepositoryV4{
+ client: client,
+ }, nil
+}
+
+func (r *branchProtectionRepositoryV4) GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error) {
+ var queryResult RepoBranchProtectionQueryResult
+
+ variables := map[string]interface{}{
+ "login": githubv4.String(repositoryOwner),
+ "name": githubv4.String(repositoryName),
+ }
+
+ err := r.client.Query(ctx, &queryResult, variables)
+ if err != nil {
+ return nil, fmt.Errorf("fetching branch protection rules for owner : %s and repo : %s failed : %v", repositoryOwner, repositoryName, err)
+ }
+
+ return &queryResult, nil
+}
+
+func (r *branchProtectionRepositoryV4) CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
+ var createMutationResult CreateRepoBranchProtectionMutation
+ err := r.client.Mutate(ctx, &createMutationResult, *input, nil)
+ if err != nil {
+ return nil, fmt.Errorf("creating new branch protection failed : %w", err)
+ }
+ return &createMutationResult, nil
+}
+
+func (r *branchProtectionRepositoryV4) UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
+ var updateMutationResult UpdateRepoBranchProtectionMutation
+ err := r.client.Mutate(ctx, &updateMutationResult, *input, nil)
+ if err != nil {
+ return nil, fmt.Errorf("updating current branch rule failed : %w", err)
+ }
+ return &updateMutationResult, nil
+}
+
+// GetRepositoryIDFromName when provided the organization and repository name, returns the repository ID
+func (r *branchProtectionRepositoryV4) GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error) {
+
+ // Define the graphql query
+ //"query": "query{repository(name: \"test1\", owner: \"deal-test-org\") {id}}"
+ var query struct {
+ Viewer struct {
+ Login githubv4.String
+ }
+ Repository struct {
+ ID string
+ } `graphql:"repository(owner:$repositoryOwner, name:$repositoryName)"`
+ }
+
+ // Define the variables for the query
+ variables := map[string]interface{}{
+ "repositoryOwner": githubv4.String(repositoryOwner),
+ "repositoryName": githubv4.String(repositoryName),
+ }
+
+ err := r.client.Query(ctx, &query, variables)
+ if err != nil {
+ return "", err
+ }
+
+ return query.Repository.ID, nil
+}
+
+// EnableBranchProtectionForPattern enables branch protection for the given branch protection input
+func EnableBranchProtectionForPattern(ctx context.Context, repositoryOwner, repositoryName string, input *BranchProtectionRule) error {
+
+ return nil
+}
diff --git a/cla-backend-go/github/client.go b/cla-backend-go/github/client.go
index 7f526b7a7..84070b97e 100644
--- a/cla-backend-go/github/client.go
+++ b/cla-backend-go/github/client.go
@@ -56,7 +56,8 @@ func isGithubRateLimit(err error) (bool, error) {
return false, nil
}
-func checkAndWrapForKnownErrors(resp *github.Response, err error) (bool, error) {
+// CheckAndWrapForKnownErrors checks for some of the known error types
+func CheckAndWrapForKnownErrors(resp *github.Response, err error) (bool, error) {
if err == nil {
return false, err
}
diff --git a/cla-backend-go/github/mock.go b/cla-backend-go/github/mock.go
deleted file mode 100644
index 63a447c33..000000000
--- a/cla-backend-go/github/mock.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-// Code generated by MockGen. DO NOT EDIT.
-// Source: protected_branch.go
-
-// Package github is a generated GoMock package.
-package github
-
-import (
- context "context"
- reflect "reflect"
-
- gomock "github.com/golang/mock/gomock"
- github "github.com/google/go-github/v33/github"
-)
-
-// MockRepositories is a mock of Repositories interface
-type MockRepositories struct {
- ctrl *gomock.Controller
- recorder *MockRepositoriesMockRecorder
-}
-
-// MockRepositoriesMockRecorder is the mock recorder for MockRepositories
-type MockRepositoriesMockRecorder struct {
- mock *MockRepositories
-}
-
-// NewMockRepositories creates a new mock instance
-func NewMockRepositories(ctrl *gomock.Controller) *MockRepositories {
- mock := &MockRepositories{ctrl: ctrl}
- mock.recorder = &MockRepositoriesMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (m *MockRepositories) EXPECT() *MockRepositoriesMockRecorder {
- return m.recorder
-}
-
-// ListByOrg mocks base method
-func (m *MockRepositories) ListByOrg(ctx context.Context, org string, opt *github.RepositoryListByOrgOptions) ([]*github.Repository, *github.Response, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ListByOrg", ctx, org, opt)
- ret0, _ := ret[0].([]*github.Repository)
- ret1, _ := ret[1].(*github.Response)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// ListByOrg indicates an expected call of ListByOrg
-func (mr *MockRepositoriesMockRecorder) ListByOrg(ctx, org, opt interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListByOrg", reflect.TypeOf((*MockRepositories)(nil).ListByOrg), ctx, org, opt)
-}
-
-// Get mocks base method
-func (m *MockRepositories) Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, owner, repo)
- ret0, _ := ret[0].(*github.Repository)
- ret1, _ := ret[1].(*github.Response)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// Get indicates an expected call of Get
-func (mr *MockRepositoriesMockRecorder) Get(ctx, owner, repo interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepositories)(nil).Get), ctx, owner, repo)
-}
-
-// GetBranchProtection mocks base method
-func (m *MockRepositories) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*github.Protection, *github.Response, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetBranchProtection", ctx, owner, repo, branch)
- ret0, _ := ret[0].(*github.Protection)
- ret1, _ := ret[1].(*github.Response)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// GetBranchProtection indicates an expected call of GetBranchProtection
-func (mr *MockRepositoriesMockRecorder) GetBranchProtection(ctx, owner, repo, branch interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBranchProtection", reflect.TypeOf((*MockRepositories)(nil).GetBranchProtection), ctx, owner, repo, branch)
-}
-
-// UpdateBranchProtection mocks base method
-func (m *MockRepositories) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *github.ProtectionRequest) (*github.Protection, *github.Response, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateBranchProtection", ctx, owner, repo, branch, preq)
- ret0, _ := ret[0].(*github.Protection)
- ret1, _ := ret[1].(*github.Response)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// UpdateBranchProtection indicates an expected call of UpdateBranchProtection
-func (mr *MockRepositoriesMockRecorder) UpdateBranchProtection(ctx, owner, repo, branch, preq interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateBranchProtection", reflect.TypeOf((*MockRepositories)(nil).UpdateBranchProtection), ctx, owner, repo, branch, preq)
-}
diff --git a/cla-backend-go/github/protected_branch.go b/cla-backend-go/github/protected_branch.go
deleted file mode 100644
index 1f449d14a..000000000
--- a/cla-backend-go/github/protected_branch.go
+++ /dev/null
@@ -1,655 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-package github
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
-
- "github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/go-openapi/swag"
- "github.com/sirupsen/logrus"
-
- "github.com/jinzhu/copier"
-
- log "github.com/communitybridge/easycla/cla-backend-go/logging"
-
- githubpkg "github.com/google/go-github/v33/github"
- "github.com/shurcooL/githubv4"
- "go.uber.org/ratelimit"
- "golang.org/x/time/rate"
-)
-
-const (
- defaultBranchName = "master"
-)
-
-var (
- // ErrBranchNotProtected indicates the situation where the branch is not enabled for protection on github side
- ErrBranchNotProtected = errors.New("not protected")
-)
-
-// rate limiting variables
-var (
- // blockingRateLimit is useful for background tasks where the interaction is more predictable
- blockingRateLimit = ratelimit.New(2)
- // nonBlockingRateLimit is preferred when the github methods would be called realtime
- // in this case we can call Allow method to check if can proceed or return error
- nonBlockingRateLimit = rate.NewLimiter(2, 5)
-)
-
-// Repositories is part of the interface working with github repositories, it's inside of the github client
-// It's extracted here as interface so we can mock that functionality.
-type Repositories interface {
- ListByOrg(ctx context.Context, org string, opt *githubpkg.RepositoryListByOrgOptions) ([]*githubpkg.Repository, *githubpkg.Response, error)
- Get(ctx context.Context, owner, repo string) (*githubpkg.Repository, *githubpkg.Response, error)
- GetBranchProtection(ctx context.Context, owner, repo, branch string) (*githubpkg.Protection, *githubpkg.Response, error)
- UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *githubpkg.ProtectionRequest) (*githubpkg.Protection, *githubpkg.Response, error)
-}
-
-type blockingRateLimitRepositories struct {
- Repositories
-}
-
-// NewBlockLimiterRepositories returns a new instance of Repositories interface with blocking rate limiting
-// where when the limit is reached the next call blocks till the bucket is ready again
-func NewBlockLimiterRepositories(repos Repositories) Repositories {
- return blockingRateLimitRepositories{repos}
-}
-
-func (b blockingRateLimitRepositories) ListByOrg(ctx context.Context, org string, opt *githubpkg.RepositoryListByOrgOptions) ([]*githubpkg.Repository, *githubpkg.Response, error) {
- blockingRateLimit.Take()
- return b.Repositories.ListByOrg(ctx, org, opt)
-}
-
-func (b blockingRateLimitRepositories) Get(ctx context.Context, owner, repo string) (*githubpkg.Repository, *githubpkg.Response, error) {
- blockingRateLimit.Take()
- return b.Repositories.Get(ctx, owner, repo)
-}
-
-func (b blockingRateLimitRepositories) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*githubpkg.Protection, *githubpkg.Response, error) {
- blockingRateLimit.Take()
- return b.Repositories.GetBranchProtection(ctx, owner, repo, branch)
-}
-
-func (b blockingRateLimitRepositories) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *githubpkg.ProtectionRequest) (*githubpkg.Protection, *githubpkg.Response, error) {
- blockingRateLimit.Take()
- return b.Repositories.UpdateBranchProtection(ctx, owner, repo, branch, preq)
-}
-
-type nonBlockingRateLimitRepositories struct {
- Repositories
-}
-
-// NewNonBlockLimiterRepositories returns a new instance of Repositories interface with non blocking rate limiting
-func NewNonBlockLimiterRepositories(repos Repositories) Repositories {
- return nonBlockingRateLimitRepositories{repos}
-}
-
-func (nb nonBlockingRateLimitRepositories) ListByOrg(ctx context.Context, org string, opt *githubpkg.RepositoryListByOrgOptions) ([]*githubpkg.Repository, *githubpkg.Response, error) {
- if nonBlockingRateLimit.Allow() {
- return nb.Repositories.ListByOrg(ctx, org, opt)
- }
- return nil, nil, fmt.Errorf("too many requests : %w", ErrRateLimited)
-}
-
-func (nb nonBlockingRateLimitRepositories) Get(ctx context.Context, owner, repo string) (*githubpkg.Repository, *githubpkg.Response, error) {
- if nonBlockingRateLimit.Allow() {
- return nb.Repositories.Get(ctx, owner, repo)
- }
- return nil, nil, fmt.Errorf("too many requests : %w", ErrRateLimited)
-}
-
-func (nb nonBlockingRateLimitRepositories) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*githubpkg.Protection, *githubpkg.Response, error) {
- if nonBlockingRateLimit.Allow() {
- return nb.Repositories.GetBranchProtection(ctx, owner, repo, branch)
- }
- return nil, nil, fmt.Errorf("too many requests : %w", ErrRateLimited)
-}
-
-func (nb nonBlockingRateLimitRepositories) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *githubpkg.ProtectionRequest) (*githubpkg.Protection, *githubpkg.Response, error) {
- if nonBlockingRateLimit.Allow() {
- return nb.Repositories.UpdateBranchProtection(ctx, owner, repo, branch, preq)
- }
- return nil, nil, fmt.Errorf("too many requests : %w", ErrRateLimited)
-}
-
-type branchProtectionRepositoryConfig struct {
- enableBlockingLimiter bool
- enableNonBlockingLimiter bool
-}
-
-// BranchProtectionRepositoryOption enables optional parameters to BranchProtectionRepository
-type BranchProtectionRepositoryOption func(config *branchProtectionRepositoryConfig)
-
-// EnableBlockingLimiter enables the blocking limiter
-func EnableBlockingLimiter() BranchProtectionRepositoryOption {
- return func(config *branchProtectionRepositoryConfig) {
- config.enableBlockingLimiter = true
- }
-}
-
-// EnableNonBlockingLimiter enables the non-blocking limiter
-func EnableNonBlockingLimiter() BranchProtectionRepositoryOption {
- return func(config *branchProtectionRepositoryConfig) {
- config.enableNonBlockingLimiter = true
- }
-}
-
-// BranchProtectionRepository contains helper methods interacting with github api related to branch protection
-type BranchProtectionRepository struct {
- githubRepo Repositories
-}
-
-// NewBranchProtectionRepository creates a new BranchProtectionRepository
-func NewBranchProtectionRepository(githubRepo Repositories, opts ...BranchProtectionRepositoryOption) *BranchProtectionRepository {
- config := &branchProtectionRepositoryConfig{}
- for _, o := range opts {
- o(config)
- }
-
- if config.enableNonBlockingLimiter {
- githubRepo = NewNonBlockLimiterRepositories(githubRepo)
- } else if config.enableBlockingLimiter {
- githubRepo = NewBlockLimiterRepositories(githubRepo)
- }
-
- return &BranchProtectionRepository{
- githubRepo: githubRepo,
- }
-}
-
-// GetOwnerName retrieves the owner name of the given org and repo name
-func (bp *BranchProtectionRepository) GetOwnerName(ctx context.Context, orgName, repoName string) (string, error) {
- repoName = CleanGithubRepoName(repoName)
- log.Debugf("GetOwnerName : getting owner name for org %s and repoName : %s", orgName, repoName)
- listOpt := &githubpkg.RepositoryListByOrgOptions{
- ListOptions: githubpkg.ListOptions{
- PerPage: 30,
- },
- }
- for {
- repos, resp, err := bp.githubRepo.ListByOrg(ctx, orgName, listOpt)
- if err != nil {
- if ok, wErr := checkAndWrapForKnownErrors(resp, err); ok {
- return "", wErr
- }
- return "", err
- }
-
- if len(repos) == 0 {
- log.Warnf("GetOwnerName : no repos found under orgName : %s (maybe no access ?)", orgName)
- return "", nil
- }
-
- for _, repo := range repos {
- if *repo.Name == repoName {
- if repo.Owner != nil {
- owner := *repo.Owner
- return *owner.Login, nil
- }
- }
- }
-
- // means we're at the end of it so exit
- if resp.NextPage == 0 {
- log.Warnf("GetOwnerName : owner name not found for org : %s and repo : %s", orgName, repoName)
- return "", nil
- }
-
- // set it to the next page
- listOpt.Page = resp.NextPage
- }
-}
-
-// GetDefaultBranchForRepo helps with pulling the default branch for the given repo
-func (bp *BranchProtectionRepository) GetDefaultBranchForRepo(ctx context.Context, owner, repoName string) (string, error) {
- repoName = CleanGithubRepoName(repoName)
- repo, resp, err := bp.githubRepo.Get(ctx, owner, repoName)
- if err != nil {
- if ok, wErr := checkAndWrapForKnownErrors(resp, err); ok {
- return "", wErr
- }
- return "", err
- }
-
- var defaultBranch string
- if repo.DefaultBranch == nil {
- defaultBranch = defaultBranchName
- } else {
- defaultBranch = *repo.DefaultBranch
- }
-
- return defaultBranch, nil
-}
-
-// GetProtectedBranch fetches the protected branch details
-func (bp *BranchProtectionRepository) GetProtectedBranch(ctx context.Context, owner, repoName, protectedBranchName string) (*githubpkg.Protection, error) {
- repoName = CleanGithubRepoName(repoName)
- protection, resp, err := bp.githubRepo.GetBranchProtection(ctx, owner, repoName, protectedBranchName)
-
- if err != nil {
- if ok, wErr := checkAndWrapForKnownErrors(resp, err); ok {
- return nil, wErr
- }
- if resp != nil && resp.StatusCode == 404 {
- if gErr, ok := err.(*githubpkg.ErrorResponse); ok {
- if strings.Contains(strings.ToLower(gErr.Message), "not protected") {
- return nil, ErrBranchNotProtected
- }
- }
- }
-
- return nil, fmt.Errorf("fetching branch proteciton : %w", err)
- }
- return protection, err
-}
-
-//EnableBranchProtection enables branch protection if not enabled and makes sure passed arguments such as enforceAdmin
-//statusChecks are applied. The operation makes sure it doesn't override the existing checks.
-func (bp *BranchProtectionRepository) EnableBranchProtection(ctx context.Context, owner, repoName, branchName string, enforceAdmin bool, enableStatusChecks, disableStatusChecks []string) error {
- repoName = CleanGithubRepoName(repoName)
- protectedBranch, err := bp.GetProtectedBranch(ctx, owner, repoName, branchName)
- if err != nil && !errors.Is(err, ErrBranchNotProtected) {
- return fmt.Errorf("fetching the protected branch for repo : %s : %w", repoName, err)
- }
-
- branchProtectionRequest, err := createBranchProtectionRequest(protectedBranch, enableStatusChecks, disableStatusChecks, enforceAdmin)
- if err != nil {
- return fmt.Errorf("creating branch protection request failed : %v", err)
- }
-
- _, resp, err := bp.githubRepo.UpdateBranchProtection(ctx, owner, repoName, branchName, branchProtectionRequest)
-
- if ok, wErr := checkAndWrapForKnownErrors(resp, err); ok {
- return wErr
- }
- return err
-}
-
-// GetRepositoryIDFromName when provided the organization and repository name, returns the repository ID
-func GetRepositoryIDFromName(ctx context.Context, installationID int64, repositoryOwner, repositoryName string) (string, error) {
- f := logrus.Fields{
- "functionName": "github.GetRepositoryIDFromName",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "installationID": installationID,
- "repositoryOwner": repositoryOwner,
- "repositoryName": repositoryName,
- }
-
- log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
- client, clientErr := NewGithubV4AppClient(installationID)
- if clientErr != nil {
- log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
- return "", clientErr
- }
- log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
-
- // Define the graphql query
- //"query": "query{repository(name: \"test1\", owner: \"deal-test-org\") {id}}"
- var query struct {
- Viewer struct {
- Login githubv4.String
- }
- Repository struct {
- ID string
- } `graphql:"repository(owner:$repositoryOwner, name:$repositoryName)"`
- }
-
- // Define the variables for the query
- variables := map[string]interface{}{
- "repositoryOwner": githubv4.String(repositoryOwner),
- "repositoryName": githubv4.String(repositoryName),
- }
-
- log.WithFields(f).Debug("executing the query...")
- err := client.Query(ctx, &query, variables)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem executing GitHub v4 query using: %+v with variables: %+v",
- query, variables)
- return "", err
- }
-
- log.WithFields(f).Debugf("User %s looked up repository ID: %s wth installation ID: %d using repository name: %s",
- query.Viewer.Login, query.Repository.ID, installationID, repositoryName)
- return query.Repository.ID, nil
-}
-
-// GetRepositoryBranchProtection when provided the organization and repository name, returns the repository branch protection rules/info
-func GetRepositoryBranchProtection(ctx context.Context, installationID int64, repositoryOwner, repositoryName string) error {
- f := logrus.Fields{
- "functionName": "github.GetRepositoryBranchProtection",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "installationID": installationID,
- "repositoryOwner": repositoryOwner,
- "repositoryName": repositoryName,
- }
-
- // NOTE: This function is not complete - does not return the values as we are still evaluating/testing this API
-
- log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
- client, clientErr := NewGithubV4AppClient(installationID)
- if clientErr != nil {
- log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
- return clientErr
- }
- log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
-
- // Define the graphql query
- /*
- query {
- repository(owner: "lee-dohm", name: "test-repo") {
- branchProtectionRules(first: 10) {
- nodes {
- pattern
- }
- }
- }
- }
- */
- var query struct {
- Viewer struct {
- Login githubv4.String
- }
- //Repository struct {
- // BranchProtectionRepositoryOption struct {
- // }
- //} `graphql:"repository(owner:$repositoryOwner, name:$repositoryName)"`
- }
-
- // Define the variables for the query
- variables := map[string]interface{}{
- "repositoryOwner": githubv4.String(repositoryOwner),
- "repositoryName": githubv4.String(repositoryName),
- }
-
- log.WithFields(f).Debug("executing the query...")
- err := client.Query(ctx, &query, variables)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem executing GitHub v4 query using: %+v with variables: %+v",
- query, variables)
- return err
- }
-
- // NOTE: still need to implement logic above
- return nil
-}
-
-// EnableBranchProtectionForAll sets the branch protection for all branches for the specified repository
-func EnableBranchProtectionForAll(ctx context.Context, installationID int64, repositoryOwner, repositoryName string, enforceAdmin bool, enableStatusChecks, disableStatusChecks []string) error {
- f := logrus.Fields{
- "functionName": "github.EnableBranchProtectionForAll",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "installationID": installationID,
- "repositoryOwner": repositoryOwner,
- "repositoryName": repositoryName,
- "enforceAdmin": enforceAdmin,
- "enableStatusChecks": strings.Join(enableStatusChecks, ","),
- "disableStatusChecks": strings.Join(disableStatusChecks, ","),
- }
-
- log.WithFields(f).Debugf("loading GitHub v4 client using installation ID: %d", installationID)
- client, clientErr := NewGithubV4AppClient(installationID)
- if clientErr != nil {
- log.WithFields(f).WithError(clientErr).Warnf("problem creating GitHub v4 API client with installation ID: %d", installationID)
- return clientErr
- }
- log.WithFields(f).Debugf("loaded GitHub v4 client using installation ID: %d", installationID)
-
- // Define the graphql mutation/update
- // This is a sample, not implemented yet
- var mutation struct {
- AddReaction struct {
- Reaction struct {
- Content githubv4.ReactionContent
- }
- Subject struct {
- ID githubv4.ID
- }
- } `graphql:"addReaction(input: $input)"`
- Repository struct {
- ID string
- } `graphql:"repository(repositoryOwner:$repositoryOwner, name:$repositoryName)"`
- }
-
- // Lookup the unique repository ID from the organization and repository name
- repositoryID, lookupErr := GetRepositoryIDFromName(ctx, installationID, repositoryOwner, repositoryName)
- if lookupErr != nil {
- log.WithFields(f).WithError(lookupErr).Warnf("problem loading repository ID from repository owner and repository name values using installation ID: %d", installationID)
- return lookupErr
- }
-
- input := githubv4.CreateBranchProtectionRuleInput{
- RepositoryID: repositoryID,
- Pattern: "**/**",
- RequiresApprovingReviews: nil,
- RequiredApprovingReviewCount: nil,
- RequiresCommitSignatures: nil,
- RequiresLinearHistory: nil,
- AllowsForcePushes: githubv4.NewBoolean(false),
- AllowsDeletions: nil,
- IsAdminEnforced: githubv4.NewBoolean(githubv4.Boolean(enforceAdmin)),
- RequiresStatusChecks: githubv4.NewBoolean(len(enableStatusChecks) > 0),
- RequiresStrictStatusChecks: nil,
- RequiresCodeOwnerReviews: nil,
- DismissesStaleReviews: nil,
- RestrictsReviewDismissals: nil,
- ReviewDismissalActorIDs: nil,
- RestrictsPushes: nil,
- PushActorIDs: nil,
- RequiredStatusCheckContexts: nil,
- ClientMutationID: nil,
- }
-
- // Define the variables for the query
- variables := map[string]interface{}{
- "repositoryOwner": githubv4.String(repositoryOwner),
- "repositoryName": githubv4.String(repositoryName),
- }
-
- return client.Mutate(ctx, &mutation, input, variables)
-}
-
-// createBranchProtectionRequest creates a branch protection request from existing protection
-func createBranchProtectionRequest(protection *githubpkg.Protection, enableStatusChecks, disableStatusChecks []string, enforceAdmin bool) (*githubpkg.ProtectionRequest, error) {
- var currentChecks *githubpkg.RequiredStatusChecks
- if protection != nil {
- currentChecks = protection.RequiredStatusChecks
- }
- requiredStatusChecks := mergeStatusChecks(currentChecks, enableStatusChecks, disableStatusChecks)
-
- branchProtectionRequest := &githubpkg.ProtectionRequest{
- RequiredStatusChecks: requiredStatusChecks,
- EnforceAdmins: enforceAdmin,
- }
-
- // don't have to check further in this case
- if protection == nil {
- return branchProtectionRequest, nil
- }
-
- if protection.RequireLinearHistory != nil {
- branchProtectionRequest.RequireLinearHistory = swag.Bool(protection.RequireLinearHistory.Enabled)
- }
-
- if protection.AllowForcePushes != nil {
- branchProtectionRequest.AllowForcePushes = swag.Bool(protection.AllowForcePushes.Enabled)
- }
-
- if protection.AllowDeletions != nil {
- branchProtectionRequest.AllowDeletions = swag.Bool(protection.AllowDeletions.Enabled)
- }
-
- if protection.RequiredPullRequestReviews != nil {
- var pullRequestReviewEnforcement githubpkg.PullRequestReviewsEnforcementRequest
- if err := copier.Copy(&pullRequestReviewEnforcement, protection.RequiredPullRequestReviews); err != nil {
- return nil, fmt.Errorf("copying from protected branch to request failed : requiredPullRequestReviews : %v", err)
- }
-
- // github is not happy about null arrays, prefers empty arrays ...
- //No subschema in "anyOf" matched.
- //For 'properties/teams', nil is not an array.
- //Not all subschemas of "allOf" matched.
- var anyEnabled bool
- if len(protection.RequiredPullRequestReviews.DismissalRestrictions.Users) > 0 {
- anyEnabled = true
- var users []string
- for _, user := range protection.RequiredPullRequestReviews.DismissalRestrictions.Users {
- users = append(users, *user.Login)
- }
- if pullRequestReviewEnforcement.DismissalRestrictionsRequest == nil {
- pullRequestReviewEnforcement.DismissalRestrictionsRequest = &githubpkg.DismissalRestrictionsRequest{}
- }
- pullRequestReviewEnforcement.DismissalRestrictionsRequest.Users = &users
- }
-
- if len(protection.RequiredPullRequestReviews.DismissalRestrictions.Teams) > 0 {
- anyEnabled = true
- var teams []string
- for _, team := range protection.RequiredPullRequestReviews.DismissalRestrictions.Teams {
- teams = append(teams, *team.Slug)
- }
- if pullRequestReviewEnforcement.DismissalRestrictionsRequest == nil {
- pullRequestReviewEnforcement.DismissalRestrictionsRequest = &githubpkg.DismissalRestrictionsRequest{}
- }
- pullRequestReviewEnforcement.DismissalRestrictionsRequest.Teams = &teams
- }
-
- if anyEnabled {
- if pullRequestReviewEnforcement.DismissalRestrictionsRequest.Users == nil {
- pullRequestReviewEnforcement.DismissalRestrictionsRequest.Users = &[]string{}
- }
-
- if pullRequestReviewEnforcement.DismissalRestrictionsRequest.Teams == nil {
- pullRequestReviewEnforcement.DismissalRestrictionsRequest.Teams = &[]string{}
- }
-
- }
-
- branchProtectionRequest.RequiredPullRequestReviews = &pullRequestReviewEnforcement
- }
-
- if protection.Restrictions != nil {
- var restrictions githubpkg.BranchRestrictionsRequest
- var anyEnabled bool
- if len(protection.Restrictions.Users) > 0 {
- anyEnabled = true
- var users []string
- for _, user := range protection.Restrictions.Users {
- users = append(users, *user.Login)
- }
- restrictions.Users = users
- }
-
- if len(protection.Restrictions.Teams) > 0 {
- anyEnabled = true
- var teams []string
- for _, team := range protection.Restrictions.Teams {
- teams = append(teams, *team.Slug)
- }
- restrictions.Teams = teams
- }
-
- if len(protection.Restrictions.Apps) > 0 {
- anyEnabled = true
- var apps []string
- for _, app := range protection.Restrictions.Apps {
- apps = append(apps, *app.Slug)
- }
- restrictions.Apps = apps
- }
-
- // make sure we don't send nil arrays ...
- if anyEnabled {
- if restrictions.Users == nil {
- restrictions.Users = []string{}
- }
-
- if restrictions.Teams == nil {
- restrictions.Teams = []string{}
- }
-
- if restrictions.Apps == nil {
- restrictions.Apps = []string{}
- }
- }
-
- branchProtectionRequest.Restrictions = &restrictions
- }
-
- return branchProtectionRequest, nil
-}
-
-//mergeStatusChecks merges the current checks with the new ones and disable the ones that are specified
-func mergeStatusChecks(currentCheck *githubpkg.RequiredStatusChecks, enableContexts, disableContexts []string) *githubpkg.RequiredStatusChecks {
-
- // seems github api is not happy with nils for arrays ;)
- if len(enableContexts) == 0 {
- enableContexts = []string{}
- }
-
- if currentCheck == nil || len(currentCheck.Contexts) == 0 {
- return &githubpkg.RequiredStatusChecks{
- Strict: true,
- Contexts: enableContexts,
- }
- }
-
- finalContexts := []string{}
- uniqueEnableContexts := map[string]bool{}
-
- for _, c := range currentCheck.Contexts {
- // first disable the ones we're not interested into
- found := false
- if len(disableContexts) > 0 {
- for _, disableContext := range disableContexts {
- if disableContext == c {
- found = true
- break
- }
- }
- }
-
- if found {
- continue
- }
-
- uniqueEnableContexts[c] = true
- finalContexts = append(finalContexts, c)
- }
-
- for _, c := range enableContexts {
- if uniqueEnableContexts[c] {
- continue
- }
-
- uniqueEnableContexts[c] = true
- finalContexts = append(finalContexts, c)
- }
-
- currentCheck.Contexts = finalContexts
- currentCheck.Strict = true
-
- return currentCheck
-}
-
-//IsEnforceAdminEnabled checks if enforce admin option is enabled for the branch protection
-func IsEnforceAdminEnabled(protection *githubpkg.Protection) bool {
- if protection.EnforceAdmins == nil {
- return false
- }
-
- return protection.EnforceAdmins.Enabled
-}
-
-// CleanGithubRepoName removes the orgname if existing in the string
-func CleanGithubRepoName(githubRepoName string) string {
- if strings.Contains(githubRepoName, "/") {
- parts := strings.Split(githubRepoName, "/")
- githubRepoName = parts[len(parts)-1]
- }
- return githubRepoName
-}
diff --git a/cla-backend-go/github/protected_branch_test.go b/cla-backend-go/github/protected_branch_test.go
deleted file mode 100644
index 2193f2b98..000000000
--- a/cla-backend-go/github/protected_branch_test.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-package github
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/go-openapi/swag"
-
- "github.com/golang/mock/gomock"
-
- "github.com/bmizerany/assert"
- githubsdk "github.com/google/go-github/v33/github"
-)
-
-// TestMergeStatusChecks tests the functionality of where we enable/disable checks
-func TestMergeStatusChecks(t *testing.T) {
-
- testCases := []struct {
- Name string
- currentChecks *githubsdk.RequiredStatusChecks
- expectedChecks *githubsdk.RequiredStatusChecks
- enableContexts []string
- disableContexts []string
- }{
- {
- Name: "all empty",
- expectedChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{},
- },
- },
- {
- Name: "empty state enable",
- expectedChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"EasyCLA"},
- },
- enableContexts: []string{"EasyCLA"},
- },
- {
- Name: "preserve existing enable more",
- currentChecks: &githubsdk.RequiredStatusChecks{
- Contexts: []string{"travis-ci"},
- },
- expectedChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"travis-ci", "EasyCLA"},
- },
- enableContexts: []string{"EasyCLA"},
- },
- {
- Name: "preserve existing disable some",
- currentChecks: &githubsdk.RequiredStatusChecks{
- Contexts: []string{"travis-ci", "EasyCLA"},
- },
- expectedChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"travis-ci"},
- },
- disableContexts: []string{"EasyCLA"},
- },
- {
- Name: "add and remove in same operation",
- currentChecks: &githubsdk.RequiredStatusChecks{
- Contexts: []string{"travis-ci", "DCO", "EasyCLA"},
- },
- expectedChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"travis-ci", "EasyCLA", "CodeQL"},
- },
- enableContexts: []string{"CodeQL"},
- disableContexts: []string{"DCO"},
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.Name, func(tt *testing.T) {
- result := mergeStatusChecks(tc.currentChecks, tc.enableContexts, tc.disableContexts)
- assert.Equal(tt, tc.expectedChecks, result)
- })
- }
-}
-
-func TestEnableBranchProtection(t *testing.T) {
- owner := "johnenable"
- repo := "johnsrepoenable"
- branchName := defaultBranchName
-
- testCases := []struct {
- Name string
- Checks []string
- Protection *githubsdk.Protection
- ProtectionRequest *githubsdk.ProtectionRequest
- Err error
- }{
- {
- Name: "success",
- Checks: []string{"easyCLA"},
- Protection: &githubsdk.Protection{},
- ProtectionRequest: &githubsdk.ProtectionRequest{
- EnforceAdmins: true,
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"easyCLA"},
- },
- },
- },
- {
- Name: "preserve existing checks",
- Checks: []string{"easyCLA"},
- Protection: &githubsdk.Protection{
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: false,
- Contexts: []string{"circle/ci"},
- },
- },
- ProtectionRequest: &githubsdk.ProtectionRequest{
- EnforceAdmins: true,
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"circle/ci", "easyCLA"},
- },
- },
- },
- {
- Name: "preserve existing settings",
- Checks: []string{"easyCLA"},
- Protection: &githubsdk.Protection{
- RequiredPullRequestReviews: &githubsdk.PullRequestReviewsEnforcement{
- RequireCodeOwnerReviews: true,
- RequiredApprovingReviewCount: 2,
- DismissalRestrictions: &githubsdk.DismissalRestrictions{
- Users: []*githubsdk.User{
- {Login: swag.String("alex")},
- },
- Teams: []*githubsdk.Team{
- {Slug: swag.String("alpha")},
- },
- },
- },
- Restrictions: &githubsdk.BranchRestrictions{
- Users: []*githubsdk.User{
- {Login: swag.String("john")},
- },
- Teams: []*githubsdk.Team{
- {Slug: swag.String("easyCLA-Team")},
- },
- },
- RequireLinearHistory: &githubsdk.RequireLinearHistory{Enabled: true},
- AllowForcePushes: &githubsdk.AllowForcePushes{Enabled: true},
- AllowDeletions: &githubsdk.AllowDeletions{Enabled: true},
- },
- ProtectionRequest: &githubsdk.ProtectionRequest{
- RequiredPullRequestReviews: &githubsdk.PullRequestReviewsEnforcementRequest{
- RequireCodeOwnerReviews: true,
- RequiredApprovingReviewCount: 2,
- DismissalRestrictionsRequest: &githubsdk.DismissalRestrictionsRequest{
- Users: &[]string{"alex"},
- Teams: &[]string{"alpha"},
- },
- },
- Restrictions: &githubsdk.BranchRestrictionsRequest{
- Users: []string{"john"},
- Teams: []string{"easyCLA-Team"},
- Apps: []string{},
- },
- EnforceAdmins: true,
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: true,
- Contexts: []string{"easyCLA"},
- },
- RequireLinearHistory: swag.Bool(true),
- AllowForcePushes: swag.Bool(true),
- AllowDeletions: swag.Bool(true),
- },
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.Name, func(tt *testing.T) {
- ctrl := gomock.NewController(tt)
- // Assert that Bar() is invoked.
- defer ctrl.Finish()
-
- m := NewMockRepositories(ctrl)
- m.
- EXPECT().
- GetBranchProtection(gomock.Any(), owner, repo, branchName).
- Return(tc.Protection, nil, nil)
- m.
- EXPECT().
- UpdateBranchProtection(gomock.Any(), owner, repo, branchName, tc.ProtectionRequest).
- Return(nil, nil, nil)
-
- branchProtectionRepo := NewBranchProtectionRepository(m)
- err := branchProtectionRepo.EnableBranchProtection(context.Background(), owner, repo, branchName, true, tc.Checks, nil)
- if err != nil {
- tt.Errorf("enable branch proteciton failed : %v", err)
- }
- })
- }
-
-}
-
-func TestNonBlockingRateLimitRepositories_GetBranchProtection(t *testing.T) {
- owner := "johnblocking"
- repo := "johnsrepoblocking"
- branchName := defaultBranchName
-
- t.Run("no limit reached", func(tt *testing.T) {
- ctrl := gomock.NewController(tt)
- defer ctrl.Finish()
-
- protection := &githubsdk.Protection{
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: false,
- Contexts: []string{"circle/ci"},
- },
- }
-
- m := NewMockRepositories(ctrl)
- m.
- EXPECT().
- GetBranchProtection(gomock.Any(), owner, repo, branchName).
- Return(protection, nil, nil)
-
- nonBlockLimitRepo := NewBranchProtectionRepository(m, EnableNonBlockingLimiter())
- p, err := nonBlockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
- if err != nil {
- tt.Errorf("no error expected : %v", err)
- }
- assert.Equal(tt, protection, p)
- })
-
- t.Run("limit reached", func(tt *testing.T) {
- ctrl := gomock.NewController(tt)
- defer ctrl.Finish()
-
- protection := &githubsdk.Protection{
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: false,
- Contexts: []string{"circle/ci"},
- },
- }
-
- m := NewMockRepositories(ctrl)
- m.
- EXPECT().
- GetBranchProtection(gomock.Any(), owner, repo, branchName).
- Return(protection, nil, nil).AnyTimes()
-
- nonBlockLimitRepo := NewBranchProtectionRepository(m, EnableNonBlockingLimiter())
- // call it 100 times in loop to make it fail
- var expectedErr error
- for i := 0; i < 100; i++ {
- _, err := nonBlockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
- if err != nil {
- expectedErr = err
- break
- }
- }
-
- if expectedErr == nil {
- tt.Fatalf("no error returned")
- return
- }
-
- if !errors.Is(expectedErr, ErrRateLimited) {
- tt.Fatalf("was expecting ErrRateLimited got : %v", expectedErr)
- return
- }
- })
-}
-
-func TestBlockingRateLimitRepositories_GetBranchProtection(t *testing.T) {
- owner := "john"
- repo := "johnsrepo"
- branchName := defaultBranchName
-
- t.Run("no limit reached", func(tt *testing.T) {
- ctrl := gomock.NewController(tt)
- defer ctrl.Finish()
-
- protection := &githubsdk.Protection{
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: false,
- Contexts: []string{"circle/ci"},
- },
- }
-
- m := NewMockRepositories(ctrl)
- m.
- EXPECT().
- GetBranchProtection(gomock.Any(), owner, repo, branchName).
- Return(protection, nil, nil)
-
- blockLimitRepo := NewBranchProtectionRepository(m, EnableBlockingLimiter())
- p, err := blockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
- if err != nil {
- tt.Errorf("no error expected : %v", err)
- }
- assert.Equal(tt, protection, p)
- })
-
- t.Run("limit reached", func(tt *testing.T) {
- ctrl := gomock.NewController(tt)
- defer ctrl.Finish()
-
- protection := &githubsdk.Protection{
- RequiredStatusChecks: &githubsdk.RequiredStatusChecks{
- Strict: false,
- Contexts: []string{"circle/ci"},
- },
- }
-
- m := NewMockRepositories(ctrl)
- m.
- EXPECT().
- GetBranchProtection(gomock.Any(), owner, repo, branchName).
- Return(protection, nil, nil).AnyTimes()
-
- blockLimitRepo := NewBranchProtectionRepository(m, EnableBlockingLimiter())
-
- // call it 100 times in loop to make it fail
- var expectedErr error
- start := time.Now()
- for i := 0; i < 10; i++ {
- _, err := blockLimitRepo.GetProtectedBranch(context.Background(), owner, repo, branchName)
- if err != nil {
- expectedErr = err
- break
- }
- }
- elapsed := time.Since(start)
-
- if expectedErr != nil {
- tt.Fatalf("no error was expected got : %v", expectedErr)
- return
- }
-
- if elapsed < 4*time.Second {
- tt.Fatalf("is rate limit enabled")
- }
-
- })
-}
diff --git a/cla-backend-go/repositories/handlers.go b/cla-backend-go/repositories/handlers.go
index 365b7bfd4..a25b81ad7 100644
--- a/cla-backend-go/repositories/handlers.go
+++ b/cla-backend-go/repositories/handlers.go
@@ -25,7 +25,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if !claUser.IsAuthorizedForProject(params.ProjectSFID) {
return github_repositories.NewGetProjectGithubRepositoriesForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Add GitHub Repository with Project scope of %s",
+ Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Add GitHub CombinedRepository with Project scope of %s",
claUser.LFUsername, params.ProjectSFID),
})
}
@@ -44,7 +44,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if !claUser.IsAuthorizedForProject(params.ProjectSFID) {
return github_repositories.NewAddProjectGithubRepositoryForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Add GitHub Repository with Project scope of %s",
+ Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Add GitHub CombinedRepository with Project scope of %s",
claUser.LFUsername, params.ProjectSFID),
})
}
@@ -75,7 +75,7 @@ func Configure(api *operations.ClaAPI, service Service, eventService events.Serv
if !claUser.IsAuthorizedForProject(params.ProjectSFID) {
return github_repositories.NewDeleteProjectGithubRepositoryForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete GitHub Repository with Project scope of %s",
+ Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete GitHub CombinedRepository with Project scope of %s",
claUser.LFUsername, params.ProjectSFID),
})
}
diff --git a/cla-backend-go/repositories/mock/mock_repository.go b/cla-backend-go/repositories/mock/mock_repository.go
index 07bca37b7..88862f212 100644
--- a/cla-backend-go/repositories/mock/mock_repository.go
+++ b/cla-backend-go/repositories/mock/mock_repository.go
@@ -54,6 +54,21 @@ func (mr *MockRepositoryMockRecorder) AddGithubRepository(ctx, externalProjectID
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubRepository", reflect.TypeOf((*MockRepository)(nil).AddGithubRepository), ctx, externalProjectID, projectSFID, input)
}
+// UpdateGithubRepository mocks base method
+func (m *MockRepository) UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "UpdateGithubRepository", ctx, repositoryID, input)
+ ret0, _ := ret[0].(*models.GithubRepository)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// UpdateGithubRepository indicates an expected call of UpdateGithubRepository
+func (mr *MockRepositoryMockRecorder) UpdateGithubRepository(ctx, repositoryID, input interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubRepository", reflect.TypeOf((*MockRepository)(nil).UpdateGithubRepository), ctx, repositoryID, input)
+}
+
// UpdateClaGroupID mocks base method
func (m *MockRepository) UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
@@ -82,6 +97,20 @@ func (mr *MockRepositoryMockRecorder) EnableRepository(ctx, repositoryID interfa
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepository", reflect.TypeOf((*MockRepository)(nil).EnableRepository), ctx, repositoryID)
}
+// EnableRepositoryWithCLAGroupID mocks base method
+func (m *MockRepository) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "EnableRepositoryWithCLAGroupID", ctx, repositoryID, claGroupID)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// EnableRepositoryWithCLAGroupID indicates an expected call of EnableRepositoryWithCLAGroupID
+func (mr *MockRepositoryMockRecorder) EnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockRepository)(nil).EnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
+}
+
// DisableRepository mocks base method
func (m *MockRepository) DisableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
@@ -139,6 +168,21 @@ func (mr *MockRepositoryMockRecorder) GetRepository(ctx, repositoryID interface{
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepository", reflect.TypeOf((*MockRepository)(nil).GetRepository), ctx, repositoryID)
}
+// GetRepositoryByName mocks base method
+func (m *MockRepository) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetRepositoryByName", ctx, repositoryName)
+ ret0, _ := ret[0].(*models.GithubRepository)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRepositoryByName indicates an expected call of GetRepositoryByName
+func (mr *MockRepositoryMockRecorder) GetRepositoryByName(ctx, repositoryName interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByName", reflect.TypeOf((*MockRepository)(nil).GetRepositoryByName), ctx, repositoryName)
+}
+
// GetRepositoryByGithubID mocks base method
func (m *MockRepository) GetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
@@ -200,7 +244,7 @@ func (mr *MockRepositoryMockRecorder) GetCLAGroupRepositoriesGroupByOrgs(ctx, pr
}
// ListProjectRepositories mocks base method
-func (m *MockRepository) ListProjectRepositories(ctx context.Context, externalProjectID, projectSFID string, enabled bool) (*models.ListGithubRepositories, error) {
+func (m *MockRepository) ListProjectRepositories(ctx context.Context, externalProjectID, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListProjectRepositories", ctx, externalProjectID, projectSFID, enabled)
ret0, _ := ret[0].(*models.ListGithubRepositories)
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index fbca307a3..e5c1a9ce0 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -54,15 +54,6 @@ func (mr *MockServiceMockRecorder) AddGithubRepository(ctx, externalProjectID, i
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubRepository", reflect.TypeOf((*MockService)(nil).AddGithubRepository), ctx, externalProjectID, input)
}
-// GetRepositoryByName mocks base method
-func (m *MockService) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoryByName", ctx, repositoryName)
- ret0, _ := ret[0].(*models.GithubRepository)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
// EnableRepository mocks base method
func (m *MockService) EnableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
@@ -71,6 +62,12 @@ func (m *MockService) EnableRepository(ctx context.Context, repositoryID string)
return ret0
}
+// EnableRepository indicates an expected call of EnableRepository
+func (mr *MockServiceMockRecorder) EnableRepository(ctx, repositoryID interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepository", reflect.TypeOf((*MockService)(nil).EnableRepository), ctx, repositoryID)
+}
+
// EnableRepositoryWithCLAGroupID mocks base method
func (m *MockService) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
@@ -79,10 +76,10 @@ func (m *MockService) EnableRepositoryWithCLAGroupID(ctx context.Context, reposi
return ret0
}
-// EnableRepository indicates an expected call of EnableRepository
-func (mr *MockServiceMockRecorder) EnableRepository(ctx, repositoryID interface{}) *gomock.Call {
+// EnableRepositoryWithCLAGroupID indicates an expected call of EnableRepositoryWithCLAGroupID
+func (mr *MockServiceMockRecorder) EnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepository", reflect.TypeOf((*MockService)(nil).EnableRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockService)(nil).EnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
}
// DisableRepository mocks base method
@@ -123,7 +120,7 @@ func (m *MockService) ListProjectRepositories(ctx context.Context, externalProje
}
// ListProjectRepositories indicates an expected call of ListProjectRepositories
-func (mr *MockServiceMockRecorder) ListProjectRepositories(ctx, externalProjectID interface{}, enabled *bool) *gomock.Call {
+func (mr *MockServiceMockRecorder) ListProjectRepositories(ctx, externalProjectID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectRepositories", reflect.TypeOf((*MockService)(nil).ListProjectRepositories), ctx, externalProjectID, enabled)
}
@@ -143,7 +140,7 @@ func (mr *MockServiceMockRecorder) GetRepository(ctx, repositoryID interface{})
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepository", reflect.TypeOf((*MockService)(nil).GetRepository), ctx, repositoryID)
}
-// GetRepository mocks base method
+// GetRepositoryByProjectSFID mocks base method
func (m *MockService) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRepositoryByProjectSFID", ctx, projectSFID, enabled)
@@ -152,12 +149,27 @@ func (m *MockService) GetRepositoryByProjectSFID(ctx context.Context, projectSFI
return ret0, ret1
}
-// GetRepository indicates an expected call of GetRepository
-func (mr *MockServiceMockRecorder) GetRepositoryByProjectSFID(ctx, projectSFID interface{}, enabled *bool) *gomock.Call {
+// GetRepositoryByProjectSFID indicates an expected call of GetRepositoryByProjectSFID
+func (mr *MockServiceMockRecorder) GetRepositoryByProjectSFID(ctx, projectSFID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByProjectSFID", reflect.TypeOf((*MockService)(nil).GetRepositoryByProjectSFID), ctx, projectSFID, enabled)
}
+// GetRepositoryByName mocks base method
+func (m *MockService) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetRepositoryByName", ctx, repositoryName)
+ ret0, _ := ret[0].(*models.GithubRepository)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRepositoryByName indicates an expected call of GetRepositoryByName
+func (mr *MockServiceMockRecorder) GetRepositoryByName(ctx, repositoryName interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByName", reflect.TypeOf((*MockService)(nil).GetRepositoryByName), ctx, repositoryName)
+}
+
// DisableRepositoriesByProjectID mocks base method
func (m *MockService) DisableRepositoriesByProjectID(ctx context.Context, projectID string) (int, error) {
m.ctrl.T.Helper()
@@ -255,3 +267,18 @@ func (mr *MockGithubOrgRepoMockRecorder) GetGithubOrganization(ctx, githubOrgani
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganization", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGithubOrganization), ctx, githubOrganizationName)
}
+
+// GetGithubOrganizations mocks base method
+func (m *MockGithubOrgRepo) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetGithubOrganizations", ctx, projectSFID)
+ ret0, _ := ret[0].(*models.GithubOrganizations)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetGithubOrganizations indicates an expected call of GetGithubOrganizations
+func (mr *MockGithubOrgRepoMockRecorder) GetGithubOrganizations(ctx, projectSFID interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizations", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGithubOrganizations), ctx, projectSFID)
+}
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index d16381a84..5d5638325 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -48,7 +48,7 @@ const (
// ErrRepositoryDoesNotExist ...
var ErrRepositoryDoesNotExist = errors.New("repository does not exist")
-// Repository defines functions of Repositories
+// Repository defines functions of V3Repositories
type Repository interface {
AddGithubRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
@@ -168,7 +168,7 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
"repositoryURL": repositoryURL,
}
- log.WithFields(f).Debugf("updating Repository : %s... ", repositoryID)
+ log.WithFields(f).Debugf("updating CombinedRepository : %s... ", repositoryID)
repoModel, repoErr := r.GetRepository(ctx, repositoryID)
if repoErr != nil {
@@ -177,7 +177,7 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
}
if repoModel == nil {
- log.WithFields(f).Warnf("Repository does not exist for repo: %s ", repositoryID)
+ log.WithFields(f).Warnf("CombinedRepository does not exist for repo: %s ", repositoryID)
return nil, ErrRepositoryDoesNotExist
}
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index c45018913..1c0c9eeea 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1579,6 +1579,9 @@ paths:
in: path
type: string
required: true
+ - name: branchName
+ in: query
+ type: string
responses:
'200':
description: 'Success'
@@ -3836,6 +3839,8 @@ definitions:
github-repository-branch-protection-input:
type: object
properties:
+ branch_name:
+ type: string
enforce_admin:
type: boolean
default: false
diff --git a/cla-backend-go/tests/github_v4_test.go b/cla-backend-go/tests/github_v4_test.go
index 1465288f6..29eba9348 100644
--- a/cla-backend-go/tests/github_v4_test.go
+++ b/cla-backend-go/tests/github_v4_test.go
@@ -9,6 +9,8 @@ import (
"strconv"
"testing"
+ "github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
+
ini "github.com/communitybridge/easycla/cla-backend-go/init"
"github.com/spf13/viper"
@@ -46,10 +48,14 @@ func TestGetRepositoryIDFromName(t *testing.T) {
assert.Fail(t, fmt.Sprintf("unable to convert installation ID to string: %s", config.GitHub.TestOrganizationInstallationID), int64Err)
}
+ branchProtectionRepoV4, err := branch_protection.NewBranchProtectionRepositoryV4(installationID)
+ if err != nil {
+ assert.Fail(t, fmt.Sprintf("initializing branch protection v4 repo failed : %v", err))
+ }
expectedValue := config.GitHub.TestRepositoryID
- actualValue, err := github.GetRepositoryIDFromName(ctx, installationID, config.GitHub.TestOrganization, config.GitHub.TestRepository)
+ actualValue, err := branchProtectionRepoV4.GetRepositoryIDFromName(ctx, config.GitHub.TestOrganization, config.GitHub.TestRepository)
if err != nil {
assert.Fail(t, fmt.Sprintf("unable to create GitHub v4 client from installation ID: %d", installationID), err)
}
- assert.Equal(t, expectedValue, actualValue, "Repository ID Lookup")
+ assert.Equal(t, expectedValue, actualValue, "CombinedRepository ID Lookup")
}
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index c219dca8e..4f22d383f 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -42,6 +42,9 @@ const EasyCLA500InternalServerError = "EasyCLA - 500 Internal Server Error"
// GitHubBotName is the name of the GitHub bot
const GitHubBotName = "EasyCLA"
+// GithubBranchProtectionPatternAll is Github Branch Protection Pattern that matches all branches
+const GithubBranchProtectionPatternAll = "**/**"
+
// TheLinuxFoundation is the name of the super parent for many Salesforce Foundations/Project Groups
const TheLinuxFoundation = "The Linux Foundation"
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index dcf6f6bf5..c7bc874bd 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -223,13 +223,13 @@ func (a *autoEnableServiceProvider) NotifyCLAManagerForRepos(claGroupID string,
// autoEnabledRepositoryEmailContent prepares the email for autoEnabled repositories
func autoEnabledRepositoryEmailContent(claGroupModel *models.ClaGroup, orgName string, managers []*models.ClaManagerUser, repos []*models.GithubRepository) (string, string, []string) {
claGroupName := claGroupModel.ProjectName
- subject := fmt.Sprintf("EasyCLA: Auto-Enable Repository for CLA Group: %s", claGroupName)
- repoPronounUpper := "Repository"
+ subject := fmt.Sprintf("EasyCLA: Auto-Enable CombinedRepository for CLA Group: %s", claGroupName)
+ repoPronounUpper := "CombinedRepository"
repoPronoun := "repository"
pronoun := "this " + repoPronoun
repoWasHere := repoPronoun + " was"
if len(repos) > 1 {
- repoPronounUpper = "Repositories"
+ repoPronounUpper = "V3Repositories"
repoPronoun = "repositories"
pronoun = "these " + repoPronoun
repoWasHere = repoPronoun + " were"
@@ -248,7 +248,7 @@ func autoEnabledRepositoryEmailContent(claGroupModel *models.ClaGroup, orgName s
Since auto-enable was configured within EasyCLA for GitHub Organization, the %s will now start enforcing
CLA checks.
Please verify the repository settings to ensure EasyCLA is a required check for merging Pull Requests.
-See: GitHub Repository -> Settings -> Branches -> Branch Protection Rules -> Add/Edit the default branch,
+See: GitHub CombinedRepository -> Settings -> Branches -> Branch Protection Rules -> Add/Edit the default branch,
and confirm that 'Require status checks to pass before merging' is enabled and that EasyCLA is a required check.
Additionally, consider selecting the 'Include administrators' option to enforce all configured restrictions for
contributors, maintainers, and administrators.
diff --git a/cla-backend-go/v2/dynamo_events/github_organization.go b/cla-backend-go/v2/dynamo_events/github_organization.go
index 5b99176be..e4c973822 100644
--- a/cla-backend-go/v2/dynamo_events/github_organization.go
+++ b/cla-backend-go/v2/dynamo_events/github_organization.go
@@ -6,8 +6,9 @@ package dynamo_events
import (
"context"
+ "github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
+
"github.com/aws/aws-lambda-go/events"
- githubutils "github.com/communitybridge/easycla/cla-backend-go/github"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -153,14 +154,12 @@ func (s *service) enableBranchProtectionForGithubOrg(ctx context.Context, newGit
}
log.WithFields(f).Debugf("creating a new GitHub client object for org: %s...", newGitHubOrg.OrganizationName)
- gitHubClient, clientErr := githubutils.NewGithubAppClient(newGitHubOrg.OrganizationInstallationID)
- if clientErr != nil {
- log.WithFields(f).WithError(clientErr).Warnf("unable to create a new GitHub app client using the installation ID: %d", newGitHubOrg.OrganizationInstallationID)
- return clientErr
+ branchProtectionRepo, err := branch_protection.NewBranchProtectionRepository(newGitHubOrg.OrganizationInstallationID, branch_protection.EnableBlockingLimiter())
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("initializing branch protection repository failed")
+ return err
}
- branchProtectionRepo := githubutils.NewBranchProtectionRepository(gitHubClient.Repositories, githubutils.EnableBlockingLimiter())
-
var eg errgroup.Group
// a pool of 5 concurrent workers
var workerTokens = make(chan struct{}, 5)
@@ -176,16 +175,10 @@ func (s *service) enableBranchProtectionForGithubOrg(ctx context.Context, newGit
}()
log.WithFields(f).Debugf("enabling branch protection for repository: %s", repo.RepositoryName)
- log.WithFields(f).Debugf("looking up the default branch for the GitHub repository: %s...", repo.RepositoryName)
- defaultBranch, branchErr := branchProtectionRepo.GetDefaultBranchForRepo(ctx, newGitHubOrg.OrganizationName, repo.RepositoryName)
- if branchErr != nil {
- return branchErr
- }
-
log.WithFields(f).Debugf("enabling branch protection on the default branch %s for the GitHub repository: %s...",
- defaultBranch, repo.RepositoryName)
+ utils.GithubBranchProtectionPatternAll, repo.RepositoryName)
return branchProtectionRepo.EnableBranchProtection(ctx, newGitHubOrg.OrganizationName, repo.RepositoryName,
- defaultBranch, true, []string{utils.GitHubBotName}, []string{})
+ utils.GithubBranchProtectionPatternAll, true, []string{utils.GitHubBotName}, []string{})
})
}
diff --git a/cla-backend-go/v2/dynamo_events/github_repository.go b/cla-backend-go/v2/dynamo_events/github_repository.go
index 9fa9852e0..abc27f7d7 100644
--- a/cla-backend-go/v2/dynamo_events/github_repository.go
+++ b/cla-backend-go/v2/dynamo_events/github_repository.go
@@ -6,7 +6,8 @@ package dynamo_events
import (
"context"
- "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
+
"github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/sirupsen/logrus"
@@ -110,25 +111,17 @@ func (s *service) EnableBranchProtectionServiceHandler(event events.DynamoDBEven
log.WithFields(f).Debug("branch protection is enabled for this organization")
ctx := context.Background()
- log.WithFields(f).Debug("creating a new GitHub client object...")
- gitHubClient, clientErr := github.NewGithubAppClient(gitHubOrg.OrganizationInstallationID)
- if clientErr != nil {
- return clientErr
- }
-
- branchProtectionRepository := github.NewBranchProtectionRepository(gitHubClient.Repositories, github.EnableBlockingLimiter())
-
- log.WithFields(f).Debug("looking up the default branch for the GitHub repository...")
- defaultBranch, branchErr := branchProtectionRepository.GetDefaultBranchForRepo(ctx, gitHubOrg.OrganizationName, newRepoModel.RepositoryName)
- if branchErr != nil {
- return branchErr
+ branchProtectionRepository, err := branch_protection.NewBranchProtectionRepository(gitHubOrg.OrganizationInstallationID, branch_protection.EnableBlockingLimiter())
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("initializing branch protection repository failed")
+ return err
}
log.WithFields(f).Debugf("enabling branch protection on th default branch %s for the GitHub repository: %s...",
- defaultBranch, newRepoModel.RepositoryName)
+ utils.GithubBranchProtectionPatternAll, newRepoModel.RepositoryName)
return branchProtectionRepository.EnableBranchProtection(ctx,
parentOrgName, newRepoModel.RepositoryName,
- defaultBranch, true, []string{utils.GitHubBotName}, []string{})
+ utils.GithubBranchProtectionPatternAll, true, []string{utils.GitHubBotName}, []string{})
}
log.WithFields(f).Debug("github organization branch protection is not enabled - no action required")
diff --git a/cla-backend-go/v2/metrics/repository.go b/cla-backend-go/v2/metrics/repository.go
index 7876ec48e..7fcc31042 100644
--- a/cla-backend-go/v2/metrics/repository.go
+++ b/cla-backend-go/v2/metrics/repository.go
@@ -756,7 +756,7 @@ func (repo *repo) calculateMetrics() (*Metrics, error) {
return nil, err
}
- log.Debug("Calculating Repository metrics...")
+ log.Debug("Calculating CombinedRepository metrics...")
// calculate github repositories count
// increment project repositories count
err = repo.processRepositoriesTable(metrics)
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 66766b0a2..9ca7c850e 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -9,6 +9,8 @@ import (
"fmt"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
+
"github.com/sirupsen/logrus"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -42,7 +44,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Get GitHub Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Get GitHub V3Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewGetProjectGithubRepositoriesForbidden().WithPayload(
@@ -94,7 +96,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Add GitHub Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Add GitHub V3Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewAddProjectGithubRepositoryForbidden().WithPayload(
@@ -172,7 +174,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Delete GitHub Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Delete GitHub V3Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewDeleteProjectGithubRepositoryForbidden().WithPayload(
@@ -230,14 +232,21 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Query Protected Branch GitHub Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Query Protected Branch GitHub V3Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewGetProjectGithubRepositoryBranchProtectionForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
}
- protectedBranch, err := service.GetProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID)
+ var branchName string
+ if params.BranchName == nil || *params.BranchName == "" {
+ branchName = branch_protection.DefaultBranchName
+ } else {
+ branchName = *params.BranchName
+ }
+
+ protectedBranch, err := service.GetProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, branchName)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
msg := fmt.Sprintf("unable to locatate branch protection projectSFID: %s, repository: %s", params.ProjectSFID, params.RepositoryID)
@@ -284,14 +293,14 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Update Protected Branch GitHub Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Update Protected Branch GitHub V3Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewUpdateProjectGithubRepositoryBranchProtectionForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
}
- protectedBranch, err := service.UpdateProtectedBranch(ctx, params.RepositoryID, params.ProjectSFID, params.GithubRepositoryBranchProtectionInput)
+ protectedBranch, err := service.UpdateProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, params.GithubRepositoryBranchProtectionInput)
if err != nil {
log.Warnf("update protected branch failed for repo %s : %v", params.RepositoryID, err)
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index f42da1796..f37f8a1f5 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -9,12 +9,12 @@ import (
"fmt"
"strconv"
- "github.com/sirupsen/logrus"
+ "github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
- "github.com/go-openapi/swag"
- githubsdk "github.com/google/go-github/v33/github"
+ "github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ "github.com/go-openapi/swag"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -29,7 +29,7 @@ import (
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
)
-// Service contains functions of GitHub Repositories service
+// Service contains functions of GitHub V3Repositories service
type Service interface {
AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error)
EnableRepository(ctx context.Context, repositoryID string) error
@@ -38,7 +38,7 @@ type Service interface {
GetRepository(ctx context.Context, repositoryID string) (*v1Models.GithubRepository, error)
GetRepositoryByName(ctx context.Context, repositoryName string) (*v1Models.GithubRepository, error)
DisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
- GetProtectedBranch(ctx context.Context, projectSFID, repositoryID string) (*v2Models.GithubRepositoryBranchProtection, error)
+ GetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error)
UpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error)
}
@@ -346,12 +346,13 @@ func (s *service) GetRepositoryByName(ctx context.Context, repositoryName string
return s.repo.GetRepositoryByName(ctx, repositoryName)
}
-func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositoryID string) (*v2Models.GithubRepositoryBranchProtection, error) {
+func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error) {
f := logrus.Fields{
"functionName": "repositories.service.GetProtectedBranch",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
+ "branchName": branchName,
}
githubRepository, err := s.getGithubRepo(ctx, projectSFID, repositoryID)
@@ -362,15 +363,14 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
githubOrgName := githubRepository.RepositoryOrganizationName
githubRepoName := githubRepository.RepositoryName
- githubRepoName = github.CleanGithubRepoName(githubRepoName)
+ githubRepoName = branch_protection.CleanGithubRepoName(githubRepoName)
- githubClient, err := s.getGithubClientForOrgName(ctx, githubOrgName)
+ branchProtectionRepository, err := s.getBranchProtectionRepositoryForOrgName(ctx, githubOrgName)
if err != nil {
return nil, err
}
- branchProtectionRepository := github.NewBranchProtectionRepository(githubClient.Repositories, github.EnableNonBlockingLimiter())
- owner, branchName, err := s.getGithubOwnerBranchName(ctx, branchProtectionRepository, githubOrgName, githubRepoName)
+ owner, err := s.getGithubOwner(ctx, branchProtectionRepository, githubOrgName, githubRepoName)
if err != nil {
return nil, err
}
@@ -381,7 +381,7 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
branchProtection, err := branchProtectionRepository.GetProtectedBranch(ctx, owner, githubRepoName, branchName)
if err != nil {
- if errors.Is(err, github.ErrBranchNotProtected) {
+ if errors.Is(err, branch_protection.ErrBranchNotProtected) {
return result, nil
}
log.WithFields(f).WithError(err).Warnf("getting the github protected branch for owner : %s, repo : %s and branch : %s failed : %v", owner, githubRepoName, branchName, err)
@@ -389,7 +389,7 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
}
result.ProtectionEnabled = true
- if github.IsEnforceAdminEnabled(branchProtection) {
+ if branch_protection.IsEnforceAdminEnabled(branchProtection) {
result.EnforceAdmin = true
}
@@ -417,22 +417,26 @@ func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, reposi
githubOrgName := githubRepository.RepositoryOrganizationName
githubRepoName := githubRepository.RepositoryName
- githubRepoName = github.CleanGithubRepoName(githubRepoName)
+ githubRepoName = branch_protection.CleanGithubRepoName(githubRepoName)
- githubClient, err := s.getGithubClientForOrgName(ctx, githubOrgName)
+ branchProtectionRepository, err := s.getBranchProtectionRepositoryForOrgName(ctx, githubOrgName)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem locating github client for organization name")
return nil, err
}
- branchProtectionRepository := github.NewBranchProtectionRepository(githubClient.Repositories, github.EnableNonBlockingLimiter())
- owner, branchName, err := s.getGithubOwnerBranchName(ctx, branchProtectionRepository, githubOrgName, githubRepoName)
+ branchName := input.BranchName
+ if branchName == "" {
+ branchName = branch_protection.DefaultBranchName
+ }
+
+ owner, err := s.getGithubOwner(ctx, branchProtectionRepository, githubOrgName, githubRepoName)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem locating github owner branch name")
return nil, err
}
f["owner"] = owner
- f["branchName"] = branchName
+ f["branchName"] = input.BranchName
var requiredChecks []string
var disabledChecks []string
@@ -468,7 +472,7 @@ func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, reposi
return nil, err
}
- return s.GetProtectedBranch(ctx, projectSFID, repositoryID)
+ return s.GetProtectedBranch(ctx, projectSFID, repositoryID, branchName)
}
func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID string) (*v1Models.GithubRepository, error) {
@@ -500,7 +504,7 @@ func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID s
return githubRepository, nil
}
-func (s *service) getGithubClientForOrgName(ctx context.Context, githubOrgName string) (*githubsdk.Client, error) {
+func (s *service) getBranchProtectionRepositoryForOrgName(ctx context.Context, githubOrgName string) (*branch_protection.BranchProtectionRepository, error) {
f := logrus.Fields{
"functionName": "repositories.service.getGitHubClientForOrgName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -513,39 +517,29 @@ func (s *service) getGithubClientForOrgName(ctx context.Context, githubOrgName s
return nil, err
}
- githubClient, err := github.NewGithubAppClient(githubOrg.OrganizationInstallationID)
+ branchProtectionRepo, err := branch_protection.NewBranchProtectionRepository(githubOrg.OrganizationInstallationID, branch_protection.EnableNonBlockingLimiter())
if err != nil {
- log.WithFields(f).Warnf("creating the github client for installation id %d failed, error: %v", githubOrg.OrganizationInstallationID, err)
return nil, err
}
-
- return githubClient, nil
+ return branchProtectionRepo, nil
}
-func (s *service) getGithubOwnerBranchName(ctx context.Context, branchProtectionRepository *github.BranchProtectionRepository, githubOrgName, githubRepoName string) (string, string, error) {
+func (s *service) getGithubOwner(ctx context.Context, branchProtectionRepository *branch_protection.BranchProtectionRepository, githubOrgName, githubRepoName string) (string, error) {
owner, err := branchProtectionRepository.GetOwnerName(ctx, githubOrgName, githubRepoName)
if err != nil {
log.Warnf("getting the owner name for org : %s and repo : %s failed : %v", githubOrgName, githubRepoName, err)
- return "", "", err
+ return "", err
}
if owner == "" {
log.Warnf("GitHub returned empty owner name for org : %s and repo : %s", githubOrgName, githubRepoName)
- return "", "", fmt.Errorf("empty owner name")
- }
-
- log.Debugf("getGitHubOwnerBranchName : owner of the repo : %s found : %s", owner, githubRepoName)
- branchName, err := branchProtectionRepository.GetDefaultBranchForRepo(ctx, owner, githubRepoName)
- if err != nil {
- log.Warnf("getting default GitHub branch failed for owner : %s and repo : %s : %v", owner, githubRepoName, err)
- return "", "", err
+ return "", fmt.Errorf("empty owner name")
}
-
- return owner, branchName, nil
+ return owner, nil
}
// getRequiredProtectedBranchCheckStatus
-func (s *service) getRequiredProtectedBranchCheckStatus(protectedBranch *githubsdk.Protection, requiredChecks []string) []*v2Models.GithubRepositoryBranchProtectionStatusChecks {
+func (s *service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *branch_protection.BranchProtectionRule, requiredChecks []string) []*v2Models.GithubRepositoryBranchProtectionStatusChecks {
f := logrus.Fields{
"functionName": "repositories.service.getRequiredProtectedBranchCheckStatus",
}
@@ -560,11 +554,11 @@ func (s *service) getRequiredProtectedBranchCheckStatus(protectedBranch *githubs
})
resultMap[rc] = true
}
- if protectedBranch.RequiredStatusChecks == nil || len(protectedBranch.RequiredStatusChecks.Contexts) == 0 {
+ if len(branchProtectionRule.RequiredStatusCheckContexts) == 0 {
return result
}
- for _, existingCheck := range protectedBranch.RequiredStatusChecks.Contexts {
+ for _, existingCheck := range branchProtectionRule.RequiredStatusCheckContexts {
if !resultMap[existingCheck] {
continue
}
@@ -576,7 +570,6 @@ func (s *service) getRequiredProtectedBranchCheckStatus(protectedBranch *githubs
}
}
}
-
return result
}
From 043e33980c89181003c7713c329960cb0c8ba8fc Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 19 Mar 2021 01:08:39 +0300
Subject: [PATCH 0168/1276] Feature/lf name events (#2799)
---
.../cmd/dynamo_events_lambda/main.go | 3 +-
cla-backend-go/cmd/server.go | 6 +-
cla-backend-go/events/event_data.go | 39 ++++++-
cla-backend-go/events/service.go | 28 +++++
cla-backend-go/project/handlers.go | 3 +-
cla-backend-go/signatures/handlers.go | 2 +-
cla-backend-go/signatures/models.go | 6 +
cla-backend-go/signatures/repository.go | 103 ++++++++++++++++--
cla-backend-go/signatures/service.go | 36 +++---
cla-backend-go/v2/company/service.go | 2 +-
cla-backend-go/v2/signatures/handlers.go | 2 +-
11 files changed, 198 insertions(+), 32 deletions(-)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index d7a0624c8..8cdcfbe47 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -80,7 +80,6 @@ func init() {
usersRepo := users.NewRepository(awsSession, stage)
userRepo := user.NewDynamoRepository(awsSession, stage)
companyRepo := company.NewRepository(awsSession, stage)
- signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo)
projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
repositoriesRepo := repositories.NewRepository(awsSession, stage)
gerritRepo := gerrits.NewRepository(awsSession, stage)
@@ -120,6 +119,8 @@ func init() {
projectClaGroupRepo,
})
+ signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService)
+
usersService := users.NewService(usersRepo, eventsService)
companyService := company.NewService(companyRepo, configFile.CorporateConsoleV1URL, userRepo, usersService)
v2CompanyService := v2Company.NewService(companyService, signaturesRepo, projectRepo, usersRepo, companyRepo, projectClaGroupRepo, eventsService)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index a56c20175..ad3a6d9e8 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -236,10 +236,9 @@ func server(localMode bool) http.Handler {
templateRepo := template.NewRepository(awsSession, stage)
approvalListRepo := approval_list.NewRepository(awsSession, stage)
v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
- signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo)
+ eventsRepo := events.NewRepository(awsSession, stage)
projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
- eventsRepo := events.NewRepository(awsSession, stage)
metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, projectClaGroupRepo)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
claManagerReqRepo := cla_manager.NewRepository(awsSession, stage)
@@ -252,6 +251,9 @@ func server(localMode bool) http.Handler {
projectClaGroupRepo,
})
+ // Signature repository handler
+ signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService)
+
// Initialize the external platform services - these are external APIs that
// we download the swagger specification, generate the models, and have
//client helper functions
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 1bd7802a1..122d27850 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -80,6 +80,13 @@ type SignatureProjectInvalidatedEventData struct {
InvalidatedCount int
}
+//SignatureInvalidatedApprovalRejectionEventData . . .
+type SignatureInvalidatedApprovalRejectionEventData struct {
+ GHUsername string
+ Email string
+ SignatureID string
+}
+
// UserCreatedEventData . . .
type UserCreatedEventData struct{}
@@ -359,25 +366,25 @@ type ClaManagerRoleDeletedData struct {
// GetEventDetailsString . . .
func (ed *CLAGroupEnrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
return fmt.Sprintf("%s (%s/%s) enabled the the project %s (%s) from the CLA Group %s (%s).",
- args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
+ args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
}
// GetEventDetailsString . . .
func (ed *CLAGroupUnenrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
return fmt.Sprintf("%s (%s/%s) unenrolled the the project %s (%s) from the CLA Group %s (%s).",
- args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
+ args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLAEnabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
return fmt.Sprintf("%s (%s/%s) enabled the CLA Service for the project %s (%s)",
- args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+ args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLADisabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
return fmt.Sprintf("%s (%s/%s) disabled the CLA Service for the project %s (%s)",
- args.UserName, args.UserModel.LfUsername, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+ args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
}
// GetEventDetailsString . . .
@@ -697,6 +704,18 @@ func (ed *SignatureProjectInvalidatedEventData) GetEventDetailsString(args *LogE
return data, true
}
+// GetEventDetailsString . . .
+func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ reason := "No reason"
+ if ed.Email != "" {
+ reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
+ } else if ed.GHUsername != "" {
+ reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
+ }
+ data := fmt.Sprintf("Signature ID: %s invalidated (approved set to false) due to %s ", ed.SignatureID, reason)
+ return data, true
+}
+
// GetEventDetailsString . . .
func (ed *ContributorNotifyCompanyAdminData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s notified Company Admin: %s by Email: %s for Company ID: %s, Name: %s.",
@@ -1479,6 +1498,18 @@ func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogE
return data, true
}
+// GetEventSummaryString . . .
+func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ reason := "No reason"
+ if ed.Email != "" {
+ reason = fmt.Sprintf("Email: %s approval removal ", ed.Email)
+ } else if ed.GHUsername != "" {
+ reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
+ }
+ data := fmt.Sprintf("Signature ID: %s invalidated (approved set to false) due to %s ", ed.SignatureID, reason)
+ return data, true
+}
+
// GetEventSummaryString . . .
func (ed *ContributorNotifyCompanyAdminData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s notified the company admin %s by the email address %s",
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index b2ea502ce..8db6e86ec 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -275,6 +275,27 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
return nil
}
+func (s *service) loadLFUser(ctx context.Context, args *LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "v1.events.service.LFUser",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if args == nil {
+ return errors.New(("unable to load lf user data - args is nil"))
+ }
+
+ if args.LfUsername != "" {
+ lfUser, lfErr := user_service.GetClient().GetUserByUsername(args.LfUsername)
+ if lfErr != nil || lfUser == nil {
+ log.WithFields(f).Warnf("unable to fetch user by username: %s ", args.LfUsername)
+ return nil
+ }
+ args.LFUser = lfUser
+ }
+ return nil
+}
+
func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
"functionName": "v1.events.service.loadUser",
@@ -398,6 +419,13 @@ func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
return err
}
+ log.WithFields(f).Debug("loading LF user details...")
+ err = s.loadLFUser(ctx, args)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to load LF user details...")
+ return err
+ }
+
return nil
}
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index d0973c947..1d9ea2c6a 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -237,7 +237,8 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
// Invalidate project signatures
log.WithFields(f).Debug("Invalidating signatures")
- howMany, err = signatureService.InvalidateProjectRecords(ctx, params.ProjectID, claGroupModel.ProjectName)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) due to CLA Group/Project: %s deletion", params.ProjectID)
+ howMany, err = signatureService.InvalidateProjectRecords(ctx, params.ProjectID, note)
if err != nil {
return project.NewDeleteProjectByIDBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
}
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index 50dfa8ebf..1afbb2642 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -346,7 +346,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
api.SignaturesGetProjectCompanyEmployeeSignaturesHandler = signatures.GetProjectCompanyEmployeeSignaturesHandlerFunc(func(params signatures.GetProjectCompanyEmployeeSignaturesParams, claUser *user.CLAUser) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- projectSignatures, err := service.GetProjectCompanyEmployeeSignatures(ctx, params)
+ projectSignatures, err := service.GetProjectCompanyEmployeeSignatures(ctx, params, nil)
if err != nil {
log.Warnf("error retrieving employee project signatures for project: %s, company: %s, error: %+v",
params.ProjectID, params.CompanyID, err)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 6684e0be9..7cc53f4d2 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -10,3 +10,9 @@ type SignatureCompanyID struct {
CompanySFID string
CompanyName string
}
+
+//ApprovalCriteria struct representing approval criteria values
+type ApprovalCriteria struct {
+ UserEmail string
+ GitHubUsername string
+}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 2569cf9c2..501de32db 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -9,6 +9,7 @@ import (
"fmt"
"sort"
"strings"
+ "sync"
"github.com/sirupsen/logrus"
@@ -19,6 +20,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/company"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
@@ -51,7 +53,7 @@ type SignatureRepository interface {
GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string) ([]models.GithubOrg, error)
AddGithubOrganizationToWhitelist(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
- InvalidateProjectRecord(ctx context.Context, signatureID string, projectName string) error
+ InvalidateProjectRecord(ctx context.Context, signatureID, note string) error
GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error)
@@ -61,12 +63,12 @@ type SignatureRepository interface {
CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error)
GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error)
- GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, pageSize int64) (*models.Signatures, error)
+ GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria, pageSize int64) (*models.Signatures, error)
GetCompanySignatures(ctx context.Context, params signatures.GetCompanySignaturesParams, pageSize int64, loadACL bool) (*models.Signatures, error)
GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error)
GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams, pageSize int64) (*models.Signatures, error)
ProjectSignatures(ctx context.Context, projectID string) (*models.Signatures, error)
- UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList) (*models.Signature, error)
+ UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error)
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
RemoveCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
@@ -87,16 +89,18 @@ type repository struct {
dynamoDBClient *dynamodb.DynamoDB
companyRepo company.IRepository
usersRepo users.UserRepository
+ eventsService events.Service
signatureTableName string
}
// NewRepository creates a new instance of the whitelist service
-func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository) SignatureRepository {
+func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service) SignatureRepository {
return repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
companyRepo: companyRepo,
usersRepo: usersRepo,
+ eventsService: eventsService,
signatureTableName: fmt.Sprintf("cla-%s-signatures", stage),
}
}
@@ -1320,7 +1324,7 @@ func (repo repository) ProjectSignatures(ctx context.Context, projectID string)
}
// InvalidateProjectRecord invalidates the specified project record by setting the signature_approved flag to false
-func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID string, projectName string) error {
+func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID, note string) error {
f := logrus.Fields{
"functionName": "InvalidateProjectRecord",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1339,7 +1343,6 @@ func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID
updateExpression = updateExpression + " #A = :a,"
expressionAttributeNames["#S"] = aws.String("note")
- note := fmt.Sprintf("Signature invalidated (approved set to false) due to CLA Group/Project: %s deletion", projectName)
expressionAttributeValues[":s"] = &dynamodb.AttributeValue{S: aws.String(note)}
updateExpression = updateExpression + " #S = :s"
@@ -1365,7 +1368,7 @@ func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID
}
// GetProjectCompanyEmployeeSignatures returns a list of employee signatures for the specified project and specified company
-func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, pageSize int64) (*models.Signatures, error) {
+func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria, pageSize int64) (*models.Signatures, error) {
f := logrus.Fields{
"functionName": "GetProjectCompanyEmployeeSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1383,6 +1386,16 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
filter := expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true))).
And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true))))
+ if criteria.GitHubUsername != "" {
+ log.WithFields(f).Debugf("adding Githubusername criteria filter for :%s ", criteria.GitHubUsername)
+ filter = filter.And(expression.Name("user_github_username").Equal(expression.Value(criteria.GitHubUsername)))
+ }
+
+ if criteria.UserEmail != "" {
+ log.WithFields(f).Debugf("adding useremail criteria filter for : %s ", criteria.UserEmail)
+ filter = filter.And(expression.Name("user_email").Equal(expression.Value(criteria.UserEmail)))
+ }
+
// Use the nice builder to create the expression
expr, err := expression.NewBuilder().WithKeyCondition(condition).WithFilter(filter).WithProjection(buildProjection()).Build()
if err != nil {
@@ -1931,7 +1944,7 @@ func (repo repository) RemoveCLAManager(ctx context.Context, signatureID, claMan
}
// UpdateApprovalList updates the specified project/company signature with the updated approval list information
-func (repo repository) UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList) (*models.Signature, error) { // nolint
+func (repo repository) UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
f := logrus.Fields{
"functionName": "UpdateApprovalList",
"projectID": projectID,
@@ -1968,6 +1981,11 @@ func (repo repository) UpdateApprovalList(ctx context.Context, projectID, compan
haveAdditions := false
updateExpression := ""
+ employeeSignatureParams := signatures.GetProjectCompanyEmployeeSignaturesParams{
+ ProjectID: projectID,
+ CompanyID: companyID,
+ }
+
// If we have an add or remove email list...we need to run an update for this column
if params.AddEmailApprovalList != nil || params.RemoveEmailApprovalList != nil {
columnName := "email_whitelist"
@@ -1988,6 +2006,40 @@ func (repo repository) UpdateApprovalList(ctx context.Context, projectID, compan
expressionAttributeValues[":e"] = attrList
updateExpression = updateExpression + " #E = :e, "
}
+
+ // if email removal update signature approvals
+ if params.RemoveEmailApprovalList != nil {
+ var wg sync.WaitGroup
+ wg.Add(len(params.RemoveEmailApprovalList))
+ for _, email := range params.RemoveEmailApprovalList {
+ go func(email string) {
+ defer wg.Done()
+ criteria := &ApprovalCriteria{
+ UserEmail: email,
+ }
+ log.WithFields(f).Debugf("Updating signature records for emailApprovalList: %+v ", params.RemoveEmailApprovalList)
+ signs, appErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
+ if appErr != nil {
+ log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", appErr)
+ return
+ }
+ log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) due to email approval list removal : %+v", params.RemoveEmailApprovalList)
+ signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
+ if signErr != nil {
+ log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
+ return
+ }
+ //Log Event
+ eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
+ SignatureID: signs.Signatures[0].SignatureID,
+ Email: email,
+ }
+ repo.eventsService.LogEventWithContext(ctx, eventArgs)
+ }(email)
+ }
+ wg.Wait()
+ }
}
if params.AddDomainApprovalList != nil || params.RemoveDomainApprovalList != nil {
@@ -2030,6 +2082,41 @@ func (repo repository) UpdateApprovalList(ctx context.Context, projectID, compan
expressionAttributeValues[":g"] = attrList
updateExpression = updateExpression + " #G = :g, "
}
+ if params.RemoveGithubUsernameApprovalList != nil {
+ // if email removal update signature approvals
+ if params.RemoveGithubUsernameApprovalList != nil {
+ var wg sync.WaitGroup
+ wg.Add(len(params.RemoveGithubUsernameApprovalList))
+ for _, ghUsername := range params.RemoveGithubUsernameApprovalList {
+ go func(ghUsername string) {
+ defer wg.Done()
+ criteria := &ApprovalCriteria{
+ GitHubUsername: ghUsername,
+ }
+ log.WithFields(f).Debugf("Updating signature records for ghUsernameApporvalList: %+v ", params.RemoveGithubUsernameApprovalList)
+ signs, ghUserErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
+ if sigErr != nil {
+ log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", ghUserErr)
+ return
+ }
+ log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) due to ghUsernames approval list removal : %+v", params.RemoveGithubUsernameApprovalList)
+ signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
+ if signErr != nil {
+ log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
+ return
+ }
+ // Log Event
+ eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
+ SignatureID: signs.Signatures[0].SignatureID,
+ GHUsername: ghUsername,
+ }
+ repo.eventsService.LogEventWithContext(ctx, eventArgs)
+ }(ghUsername)
+ }
+ wg.Wait()
+ }
+ }
}
if params.AddGithubOrgApprovalList != nil || params.RemoveGithubOrgApprovalList != nil {
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index a4b66037e..227140675 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -15,7 +15,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/events"
-
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/LF-Engineering/lfx-kit/auth"
@@ -40,11 +39,11 @@ type SignatureService interface {
CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error)
GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
GetProjectCompanySignatures(ctx context.Context, params signatures.GetProjectCompanySignaturesParams) (*models.Signatures, error)
- GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams) (*models.Signatures, error)
+ GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria) (*models.Signatures, error)
GetCompanySignatures(ctx context.Context, params signatures.GetCompanySignaturesParams) (*models.Signatures, error)
GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error)
GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams) (*models.Signatures, error)
- InvalidateProjectRecords(ctx context.Context, projectID string, projectName string) (int, error)
+ InvalidateProjectRecords(ctx context.Context, projectID, note string) (int, error)
GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string, githubAccessToken string) ([]models.GithubOrg, error)
AddGithubOrganizationToWhitelist(ctx context.Context, signatureID string, whiteListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error)
@@ -142,7 +141,7 @@ func (s service) GetProjectCompanySignatures(ctx context.Context, params signatu
}
// GetProjectCompanyEmployeeSignatures returns the list of employee signatures associated with the specified project
-func (s service) GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams) (*models.Signatures, error) {
+func (s service) GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria) (*models.Signatures, error) {
const defaultPageSize int64 = 10
var pageSize = defaultPageSize
@@ -150,7 +149,7 @@ func (s service) GetProjectCompanyEmployeeSignatures(ctx context.Context, params
pageSize = *params.PageSize
}
- projectSignatures, err := s.repo.GetProjectCompanyEmployeeSignatures(ctx, params, pageSize)
+ projectSignatures, err := s.repo.GetProjectCompanyEmployeeSignatures(ctx, params, criteria, pageSize)
if err != nil {
return nil, err
}
@@ -424,7 +423,18 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
return nil, userErr
}
- updatedSig, err := s.repo.UpdateApprovalList(ctx, claGroupModel.ProjectID, companyModel.CompanyID, params)
+ eventArgs := &events.LogEventArgs{
+ EventType: events.InvalidatedSignature,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ClaGroupModel: claGroupModel,
+ CompanyID: companyModel.CompanyID,
+ CompanyModel: companyModel,
+ LfUsername: userModel.LfUsername,
+ UserID: userModel.UserID,
+ UserModel: userModel,
+ ProjectSFID: claGroupModel.ProjectExternalID,
+ }
+ updatedSig, err := s.repo.UpdateApprovalList(ctx, claGroupModel.ProjectID, companyModel.CompanyID, params, eventArgs)
if err != nil {
return updatedSig, err
}
@@ -445,11 +455,11 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
}
// Disassociate project signatures
-func (s service) InvalidateProjectRecords(ctx context.Context, projectID string, projectName string) (int, error) {
+func (s service) InvalidateProjectRecords(ctx context.Context, projectID, note string) (int, error) {
f := logrus.Fields{
"functionName": "InvalidateProjectRecords",
"projectID": projectID,
- "projectName": projectName}
+ }
result, err := s.repo.ProjectSignatures(ctx, projectID)
if err != nil {
@@ -464,14 +474,14 @@ func (s service) InvalidateProjectRecords(ctx context.Context, projectID string,
len(result.Signatures), projectID))
for _, signature := range result.Signatures {
// Do this in parallel, as we could have a lot to invalidate
- go func(sigID, projName string) {
+ go func(sigID, projectID string) {
defer wg.Done()
- updateErr := s.repo.InvalidateProjectRecord(ctx, sigID, projName)
+ updateErr := s.repo.InvalidateProjectRecord(ctx, sigID, note)
if updateErr != nil {
- log.WithFields(f).Warnf("Unable to update signature: %s with project name: %s, error: %v",
- sigID, projName, updateErr)
+ log.WithFields(f).Warnf("Unable to update signature: %s with project ID: %s, error: %v",
+ sigID, projectID, updateErr)
}
- }(signature.SignatureID, projectName)
+ }(signature.SignatureID, projectID)
}
// Wait until all the workers are done
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index f1fadcb39..6872628fa 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1567,7 +1567,7 @@ func (s *service) getAllCompanyProjectEmployeeSignatures(ctx context.Context, co
CompanyID: companyID,
ProjectID: claGroup.ProjectID,
}
- sigs, err := s.signatureRepo.GetProjectCompanyEmployeeSignatures(ctx, params, HugePageSize)
+ sigs, err := s.signatureRepo.GetProjectCompanyEmployeeSignatures(ctx, params, nil, HugePageSize)
if err != nil {
return nil, err
}
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 10bb85a56..1181cda1c 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -550,7 +550,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
CompanyID: companyModel.CompanyID, // internal company id
NextKey: params.NextKey,
PageSize: params.PageSize,
- })
+ }, nil)
if err != nil {
log.WithFields(f).WithError(err).Warnf("error retrieving employee project signatures for project: %s, company: %s, error: %+v",
params.ProjectSFID, params.CompanyID, err)
From b9c120d630f00f025df1aec187346e160e8cffdc Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 18 Mar 2021 16:01:10 -0700
Subject: [PATCH 0169/1276] Updated Company and SFID RegEx on v2 APIs (#2801)
- Resolves issue with company with name: Swisscom (Schweiz) AG
Signed-off-by: David Deal
---
cla-backend-go/swagger/cla.v2.yaml | 32 ++++++++++++------------------
1 file changed, 13 insertions(+), 19 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 1c0c9eeea..1110543c1 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3556,8 +3556,7 @@ parameters:
in: query
type: string
required: false
- pattern: '^([\w\d\s\-\,\./]+){2,255}$'
- #$ref: './common/properties/company-name.yaml'
+ pattern: '[^<>]*' # allow everything except greater than and less than symbols
userName:
name: userName
description: The optional user name filter
@@ -3593,12 +3592,14 @@ parameters:
description: the Salesforce ID of the Foundation
in: query
type: string
+ pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
signingEntityName:
name: signingEntityName
description: The signing entity name of a Company (Salesforce and EasyCLA)
in: query
type: string
required: true
+ pattern: '[^<>]*' # allow everything except greater than and less than symbols
companyID:
name: companyID
description: The internal company ID representing signing entity name instance (EasyCLA)
@@ -3610,8 +3611,7 @@ parameters:
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
+ pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
minLength: 5
maxLength: 255
path-foundationSFID:
@@ -3620,8 +3620,7 @@ parameters:
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
+ pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
minLength: 5
maxLength: 255
path-projectSFID:
@@ -3630,8 +3629,7 @@ parameters:
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
+ pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
minLength: 5
maxLength: 255
path-userID:
@@ -3656,27 +3654,23 @@ parameters:
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
- minLength: 5
- maxLength: 255
+ pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
path-companySFID:
name: companySFID
description: salesforce id of the company
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
- minLength: 5
- maxLength: 255
+ pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
path-companyName:
name: companyName
description: the company name
in: path
type: string
required: true
- pattern: '^([\w\d\s\-\,\./]+){2,255}$'
+ pattern: '[^<>]*' # allow everything except greater than and less than symbols
+ minLength: 2
+ maxLength: 100
path-signingEntityName:
name: signingEntityName
type: string
@@ -3693,8 +3687,7 @@ parameters:
in: path
type: string
required: true
- # \w - Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^(\w)([\w\-.])+$'
+ pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
minLength: 5
maxLength: 255
companySFID:
@@ -3702,6 +3695,7 @@ parameters:
description: salesforce id of the company
in: query
type: string
+ pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
gerritHost:
name: gerritHost
description: host of the gerrit server
From 2446d45e766cd62202fa9e08acd30aee4bc07da4 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 19 Mar 2021 02:01:29 +0300
Subject: [PATCH 0170/1276] [#2781]Feature/Signature Invalidation upon Approval
removal (#2786)
---
cla-backend-go/events/event_data.go | 7 ++++++-
cla-backend-go/events/service.go | 21 ---------------------
cla-backend-go/project/handlers.go | 3 ++-
cla-backend-go/signatures/repository.go | 17 +++++++++++++----
cla-backend-go/signatures/service.go | 4 +++-
cla-backend-go/utils/cla_user.go | 25 +++++++++++++++++++++++++
6 files changed, 49 insertions(+), 28 deletions(-)
create mode 100644 cla-backend-go/utils/cla_user.go
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 122d27850..cc7c273ed 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -5,6 +5,9 @@ package events
import (
"fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
)
// EventData returns event data string which is used for event logging and containsPII field
@@ -85,6 +88,8 @@ type SignatureInvalidatedApprovalRejectionEventData struct {
GHUsername string
Email string
SignatureID string
+ CLAManager *models.User
+ CLAGroupID string
}
// UserCreatedEventData . . .
@@ -712,7 +717,7 @@ func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventDetailsString(
} else if ed.GHUsername != "" {
reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
}
- data := fmt.Sprintf("Signature ID: %s invalidated (approved set to false) due to %s ", ed.SignatureID, reason)
+ data := fmt.Sprintf("Signature ID: %s invalidated by %s (approved set to false) due to %s ", utils.GetBestUsername(ed.CLAManager), ed.SignatureID, reason)
return data, true
}
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 8db6e86ec..a4cd66efe 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -355,27 +355,6 @@ func (s *service) loadUser(ctx context.Context, args *LogEventArgs) error {
return nil
}
-func (s *service) loadLFUser(ctx context.Context, args *LogEventArgs) error {
- f := logrus.Fields{
- "functionName": "v1.events.service.loadLFUser",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- }
-
- if args.LFUser != nil {
- return nil
- }
-
- if args.LfUsername != "" {
- lfUser, lfUserErr := user_service.GetClient().GetUserByUsername(args.LfUsername)
- if lfUserErr != nil || lfUser == nil {
- log.WithFields(f).Warnf("unable to fetch LF User by username: %s", args.LfUsername)
- return nil
- }
- args.LFUser = lfUser
- }
- return nil
-}
-
// loadDetails fetches and sets additional information into the data model required to fill out the event log entry
func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index 1d9ea2c6a..35f9acf67 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -237,7 +237,8 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
// Invalidate project signatures
log.WithFields(f).Debug("Invalidating signatures")
- note := fmt.Sprintf("Signature invalidated (approved set to false) due to CLA Group/Project: %s deletion", params.ProjectID)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to CLA Group/Project: %s deletion", claUser.LFUsername, params.ProjectID)
+
howMany, err = signatureService.InvalidateProjectRecords(ctx, params.ProjectID, note)
if err != nil {
return project.NewDeleteProjectByIDBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(err))
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 501de32db..9318de921 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -68,7 +68,7 @@ type SignatureRepository interface {
GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error)
GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams, pageSize int64) (*models.Signatures, error)
ProjectSignatures(ctx context.Context, projectID string) (*models.Signatures, error)
- UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error)
+ UpdateApprovalList(ctx context.Context, claManager *models.User, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error)
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
RemoveCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
@@ -1944,7 +1944,8 @@ func (repo repository) RemoveCLAManager(ctx context.Context, signatureID, claMan
}
// UpdateApprovalList updates the specified project/company signature with the updated approval list information
-func (repo repository) UpdateApprovalList(ctx context.Context, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
+func (repo repository) UpdateApprovalList(ctx context.Context, claManager *models.User, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
+
f := logrus.Fields{
"functionName": "UpdateApprovalList",
"projectID": projectID,
@@ -2024,17 +2025,22 @@ func (repo repository) UpdateApprovalList(ctx context.Context, projectID, compan
return
}
log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
- note := fmt.Sprintf("Signature invalidated (approved set to false) due to email approval list removal : %+v", params.RemoveEmailApprovalList)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to email approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveEmailApprovalList)
+
signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
if signErr != nil {
log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
return
}
+
//Log Event
eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
SignatureID: signs.Signatures[0].SignatureID,
Email: email,
+ CLAManager: claManager,
+ CLAGroupID: signs.Signatures[0].ProjectID,
}
+
repo.eventsService.LogEventWithContext(ctx, eventArgs)
}(email)
}
@@ -2100,16 +2106,19 @@ func (repo repository) UpdateApprovalList(ctx context.Context, projectID, compan
return
}
log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
- note := fmt.Sprintf("Signature invalidated (approved set to false) due to ghUsernames approval list removal : %+v", params.RemoveGithubUsernameApprovalList)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to ghUsernames approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveGithubUsernameApprovalList)
signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
if signErr != nil {
log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
return
}
+
// Log Event
eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
SignatureID: signs.Signatures[0].SignatureID,
GHUsername: ghUsername,
+ CLAManager: claManager,
+ CLAGroupID: signs.Signatures[0].ProjectID,
}
repo.eventsService.LogEventWithContext(ctx, eventArgs)
}(ghUsername)
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 227140675..d0ed0542f 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -434,7 +434,9 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
}
- updatedSig, err := s.repo.UpdateApprovalList(ctx, claGroupModel.ProjectID, companyModel.CompanyID, params, eventArgs)
+
+ updatedSig, err := s.repo.UpdateApprovalList(ctx, userModel, claGroupModel.ProjectID, companyModel.CompanyID, params, eventArgs)
+
if err != nil {
return updatedSig, err
}
diff --git a/cla-backend-go/utils/cla_user.go b/cla-backend-go/utils/cla_user.go
new file mode 100644
index 000000000..8fc062b19
--- /dev/null
+++ b/cla-backend-go/utils/cla_user.go
@@ -0,0 +1,25 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+)
+
+// GetBestUsername gets best username of CLA User
+func GetBestUsername(user *models.User) string {
+ if user.Username != "" {
+ return user.Username
+ }
+
+ if user.GithubUsername != "" {
+ return user.GithubUsername
+ }
+
+ if user.LfUsername != "" {
+ return user.LfUsername
+ }
+
+ return "User Name Unknown"
+}
From 853d27e841c71138e85250a3422d6072ede06248 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 19 Mar 2021 02:38:06 +0300
Subject: [PATCH 0171/1276] [#2795] Feature/Approval Email to Contributor
(#2802)
- Updated email content with gerrit steps
Signed-off-by: wanyaland
---
cla-backend-go/signatures/service.go | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index d0ed0542f..0a384ef4f 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -844,7 +844,13 @@ func sendRequestAccessEmailToContributorRecipient(authUser *auth.User, companyMo
This is a notification email from EasyCLA regarding the project %s.
You have been %s %s the Approval List of %s for %s by CLA Manager %s. This means that %s.
-
If you had previously submitted a pull request to EasyCLA Test Group that had failed, you can now go back to it, re-click the “Not Covered” button in the EasyCLA message in your pull request, and then follow these steps
+
If you are a GitHub user and If you had previously submitted a pull request to EasyCLA Test Group that had failed, you can now go back to it, re-click the “Not Covered” button in the EasyCLA message in your pull request, and then follow these steps
+
+
Select “Corporate Contributor”.
+
Select your company from the organization drop down list
+
Click Proceed
+
+
If you are a Gerrit user and if you had previously submitted a pull request to EasyCLA Test Group that had failed, then navigate to Agreements Settings page on Gerrit, click on "New Contributor Agreement" link under Agreements section, select the radio button corresponding to Corporate CLA, click on "Please review the agreement" link, and then follow these steps
Select “Corporate Contributor”.
Select your company from the organization drop down list
From 520fc7b546738d6ef674d6d3f3fd9ac08da51303 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 18 Mar 2021 17:11:28 -0700
Subject: [PATCH 0172/1276] Resolves 2796 - Set Enable Services = CLA for
Project Parent (#2803)
- Added logic to set the enabled services for parent with the appropriate configuration is set
- Added new configuration for this flag (cla-enable-services-for-parent-{stage})
Signed-off-by: David Deal
---
cla-backend-go/config/config.go | 3 +++
cla-backend-go/config/ssm.go | 10 ++++++++
cla-backend-go/v2/cla_groups/helpers.go | 32 +++++++++++++++++++++++--
3 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index b0541c7e7..74fe8086b 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -24,6 +24,9 @@ type Config struct {
// API GW
APIGatewayURL string `json:"api_gateway_url"`
+ // EnableCLAServiceForParent is a configuration flag to indicate if we should set the enable_services=[CLA] attribute on the parent project object in the project service when a child project is associated with a CLA group. This determines the v2 project console experience/behavior."
+ EnableCLAServiceForParent bool `json:"enable_cla_service_for_parent"`
+
// SFDC
// GitHub
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index ea3d85c22..683b794a4 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -93,6 +93,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-lfx-metrics-report-sqs-region-%s", stage),
fmt.Sprintf("cla-lfx-metrics-report-sqs-url-%s", stage),
fmt.Sprintf("cla-lfx-metrics-report-enabled-%s", stage),
+ fmt.Sprintf("cla-enable-services-for-parent-%s", stage),
}
// For each key to lookup
@@ -208,6 +209,15 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
} else {
config.MetricsReport.Enabled = boolVal
}
+ case fmt.Sprintf("cla-enable-services-for-parent-%s", stage):
+ boolVal, err := strconv.ParseBool(resp.value)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to convert %s value to a boolean - setting value to false in the configuration",
+ fmt.Sprintf("cla-enable-services-for-parent-%s", stage))
+ config.EnableCLAServiceForParent = false
+ } else {
+ config.EnableCLAServiceForParent = boolVal
+ }
}
}
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index cb5555221..1b96b6234 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -10,6 +10,8 @@ import (
"strings"
"sync"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
@@ -495,10 +497,10 @@ func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, cla
// Execute as a go routine
go func(psClient *v2ProjectService.Client, claGroupID, projectSFID string) {
defer wg.Done()
+ log.WithFields(f).Debugf("enabling project CLA service for project: %s...", projectSFID)
enableProjectErr := psClient.EnableCLA(projectSFID)
if enableProjectErr != nil {
- log.WithFields(f).WithError(enableProjectErr).
- Warnf("unable to enable CLA service for project: %s, error: %+v", projectSFID, enableProjectErr)
+ log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", projectSFID, enableProjectErr)
errorList = append(errorList, enableProjectErr)
} else {
log.WithFields(f).Debugf("enabled CLA service for project: %s", projectSFID)
@@ -510,6 +512,32 @@ func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, cla
LfUsername: authUser.UserName,
EventData: &events.ProjectServiceCLAEnabledData{},
})
+
+ // If we should enable the CLA Service for the parent
+ if config.GetConfig().EnableCLAServiceForParent {
+ log.WithFields(f).Debugf("enable parent project CLA service when child is enrolled flag is enabled")
+ parentProjectSFID, parentLookupErr := psc.GetParentProject(projectSFID)
+ if parentLookupErr != nil || parentProjectSFID == "" {
+ log.WithFields(f).WithError(parentLookupErr).Warnf("unable to lookup parent project SFID for project: %s", projectSFID)
+ } else {
+ log.WithFields(f).Debugf("enabling parent project CLA service for project SFID: %s...", parentProjectSFID)
+ enableProjectErr := psClient.EnableCLA(parentProjectSFID)
+ if enableProjectErr != nil {
+ log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", parentProjectSFID, enableProjectErr)
+ errorList = append(errorList, enableProjectErr)
+ } else {
+ log.WithFields(f).Debugf("enabled CLA service for parent project: %s", parentProjectSFID)
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ProjectServiceCLAEnabled,
+ ProjectID: parentProjectSFID,
+ CLAGroupID: claGroupID,
+ LfUsername: authUser.UserName,
+ EventData: &events.ProjectServiceCLAEnabledData{},
+ })
+ }
+ }
+ }
}
}(psc, claGroupID, projectSFID)
}
From 309e3b7c4a2f8477cf7c9854b78836b830191ab3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 18 Mar 2021 17:35:29 -0700
Subject: [PATCH 0173/1276] Added Additional Project Service Logging (#2804)
Signed-off-by: David Deal
---
cla-backend-go/v2/project-service/client.go | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 54ecb21cc..a8e438beb 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -36,14 +36,15 @@ var (
projectServiceClient *Client
// Short term cache - only for the lifetime of this lambda
projectServiceModes = make(map[string]*models.ProjectOutputDetailed)
+ apiGWHost string
)
// InitClient initializes the user_service client
func InitClient(APIGwURL string) {
- APIGwURL = strings.ReplaceAll(APIGwURL, "https://", "")
+ apiGWHost = strings.ReplaceAll(APIGwURL, "https://", "")
projectServiceClient = &Client{
cl: client.NewHTTPClientWithConfig(strfmt.Default, &client.TransportConfig{
- Host: APIGwURL,
+ Host: apiGWHost,
BasePath: "project-service",
Schemes: []string{"https"},
}),
@@ -70,6 +71,7 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
f := logrus.Fields{
"functionName": "v2.project-service.client.GetProject",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
// Lookup in cache first
@@ -106,6 +108,7 @@ func (pmm *Client) GetProjectByName(projectName string) (*models.ProjectListSear
f := logrus.Fields{
"functionName": "v2.project-service.client.GetProjectByName",
"projectName": projectName,
+ "apiGWHost": apiGWHost,
}
tok, err := token.GetToken()
if err != nil {
@@ -130,6 +133,7 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
f := logrus.Fields{
"functionName": "v2.project-service.client.GetParentProject",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
// Lookup in cache first
@@ -167,6 +171,7 @@ func (pmm *Client) IsTheLinuxFoundation(projectSFID string) (bool, error) {
f := logrus.Fields{
"functionName": "v2.project-service.client.IsTheLinuxFoundation",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
log.WithFields(f).Debug("querying project...")
@@ -190,6 +195,7 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
f := logrus.Fields{
"functionName": "v2.project-service.client.IsParentTheLinuxFoundation",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
log.WithFields(f).Debug("querying project...")
@@ -223,6 +229,7 @@ func (pmm *Client) EnableCLA(projectSFID string) error {
f := logrus.Fields{
"functionName": "v2.project-service.client.EnableCLA",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
tok, err := token.GetToken()
@@ -273,6 +280,7 @@ func (pmm *Client) DisableCLA(projectSFID string) error {
f := logrus.Fields{
"functionName": "v2.project-service.client.DisableCLA",
"projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
tok, err := token.GetToken()
From eb7becafb593a44603a55e828e597c648bffaad3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 19 Mar 2021 10:14:27 -0700
Subject: [PATCH 0174/1276] Added New Platform API Gateway Key (#2805)
Signed-off-by: David Deal
---
cla-backend-go/cmd/server.go | 8 ++++----
cla-backend-go/config/config.go | 4 +++-
cla-backend-go/config/ssm.go | 3 +++
3 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index ad3a6d9e8..ca56957f6 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -257,10 +257,10 @@ func server(localMode bool) http.Handler {
// Initialize the external platform services - these are external APIs that
// we download the swagger specification, generate the models, and have
//client helper functions
- user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
- project_service.InitClient(configFile.APIGatewayURL)
- organization_service.InitClient(configFile.APIGatewayURL, eventsService)
- acs_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
+ user_service.InitClient(configFile.PlatformAPIGatewayURL, configFile.AcsAPIKey)
+ project_service.InitClient(configFile.PlatformAPIGatewayURL)
+ organization_service.InitClient(configFile.PlatformAPIGatewayURL, eventsService)
+ acs_service.InitClient(configFile.PlatformAPIGatewayURL, configFile.AcsAPIKey)
usersService := users.NewService(usersRepo, eventsService)
healthService := health.New(Version, Commit, Branch, BuildDate)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 74fe8086b..704982d4e 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -21,8 +21,10 @@ type Config struct {
// Auth0Platform config
Auth0Platform Auth0Platform `json:"auth0_platform"`
- // API GW
+ // APIGatewayURL is the API gateway URL - old variable which is set by the old cla-auth0-gateway SSM key
APIGatewayURL string `json:"api_gateway_url"`
+ // PlatformAPIGatewayURL is the platform API gateway URL
+ PlatformAPIGatewayURL string `json:"platform_api_gateway_url"`
// EnableCLAServiceForParent is a configuration flag to indicate if we should set the enable_services=[CLA] attribute on the parent project object in the project service when a child project is associated with a CLA group. This determines the v2 project console experience/behavior."
EnableCLAServiceForParent bool `json:"enable_cla_service_for_parent"`
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 683b794a4..19e5c358a 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -94,6 +94,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-lfx-metrics-report-sqs-url-%s", stage),
fmt.Sprintf("cla-lfx-metrics-report-enabled-%s", stage),
fmt.Sprintf("cla-enable-services-for-parent-%s", stage),
+ fmt.Sprintf("cla-platform-api-gw-%s", stage),
}
// For each key to lookup
@@ -182,6 +183,8 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.Auth0Platform.URL = resp.value
case fmt.Sprintf("cla-auth0-platform-api-gw-%s", stage):
config.APIGatewayURL = resp.value
+ case fmt.Sprintf("cla-platform-api-gw-%s", stage):
+ config.PlatformAPIGatewayURL = resp.value
case fmt.Sprintf("cla-lf-group-client-id-%s", stage):
config.LFGroup.ClientID = resp.value
case fmt.Sprintf("cla-lf-group-client-secret-%s", stage):
From 33ef67907025c7c28cfac7f12e8ddf8b6030e0d5 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 19 Mar 2021 17:05:38 -0700
Subject: [PATCH 0175/1276] Updated Gerrit Query (#2807)
- Updated gerrit query to perform a query instead of a scan
- Updated gerrit query filter - only search gerrits by CLA Group ID - ignore projectSFID
- Updated logging
Signed-off-by: David Deal
---
cla-backend-go/gerrits/repository.go | 46 +++++-----
cla-backend-go/gerrits/service.go | 13 +--
cla-backend-go/v2/gerrits/handlers.go | 116 +++++++++++++++++---------
3 files changed, 111 insertions(+), 64 deletions(-)
diff --git a/cla-backend-go/gerrits/repository.go b/cla-backend-go/gerrits/repository.go
index 57c2c29a8..98b37b1aa 100644
--- a/cla-backend-go/gerrits/repository.go
+++ b/cla-backend-go/gerrits/repository.go
@@ -30,6 +30,7 @@ import (
// errors
var (
ErrGerritNotFound = errors.New("gerrit not found")
+ HugePageSize = int64(10000)
)
// Repository defines functions of V3Repositories
@@ -61,7 +62,7 @@ type repo struct {
// AddGerrit creates a new gerrit instance
func (repo *repo) AddGerrit(ctx context.Context, input *models.Gerrit) (*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.AddGerrit",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
gerritID, err := uuid.NewV4()
@@ -103,7 +104,7 @@ func (repo *repo) AddGerrit(ctx context.Context, input *models.Gerrit) (*models.
// GetGerrit returns the gerrit instances based on the ID
func (repo *repo) GetGerrit(ctx context.Context, gerritID string) (*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.GetGerrit",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritID": gerritID,
}
@@ -137,7 +138,7 @@ func (repo *repo) GetGerrit(ctx context.Context, gerritID string) (*models.Gerri
func (repo repo) GetGerritsByID(ctx context.Context, ID string, IDType string) (*models.GerritList, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.GetGerritsByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ID": ID,
"IDType": IDType,
@@ -200,7 +201,7 @@ func (repo repo) GetGerritsByID(ctx context.Context, ID string, IDType string) (
func (repo repo) GetGerritsByProjectSFID(ctx context.Context, projectSFID string) (*models.GerritList, error) {
f := logrus.Fields{
- "functionName": "GetGerritsByProjectSFID",
+ "functionName": "v1.gerrits.repository.GetGerritsByProjectSFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -259,34 +260,41 @@ func (repo repo) GetGerritsByProjectSFID(ctx context.Context, projectSFID string
}
// GetClaGroupGerrits returns the CLA Group gerrit instances based on the CLA Group ID and the project SFID
-func (repo repo) GetClaGroupGerrits(ctx context.Context, projectID string, projectSFID *string) (*models.GerritList, error) {
+func (repo repo) GetClaGroupGerrits(ctx context.Context, claGroupID string, projectSFID *string) (*models.GerritList, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.GetClaGroupGerrits",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectID": projectID,
+ "claGroupID": claGroupID,
"projectSFID": projectSFID,
}
resultList := make([]*models.Gerrit, 0)
- filter := expression.Name("project_id").Equal(expression.Value(projectID))
- if projectSFID != nil {
- filter = filter.And(expression.Name("project_sfid").Equal(expression.Value(*projectSFID)))
- }
- expr, err := expression.NewBuilder().WithFilter(filter).Build()
+ condition := expression.Key("project_id").Equal(expression.Value(claGroupID))
+ // No reason to add this additional filter for the v2 API
+ //if projectSFID != nil {
+ // filter = filter.And(expression.Name("project_sfid").Equal(expression.Value(*projectSFID)))
+ //}
+
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).Build()
if err != nil {
- log.WithFields(f).Warnf("error building expression for gerrit instances scan, error: %v", err)
+ log.WithFields(f).Warnf("error building expression for gerrit instances query, error: %v", err)
return nil, err
}
+
// Assemble the query input parameters
- scanInput := &dynamodb.ScanInput{
+ queryInput := &dynamodb.QueryInput{
ExpressionAttributeNames: expr.Names(),
ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
FilterExpression: expr.Filter(),
+ ProjectionExpression: expr.Projection(),
TableName: aws.String(repo.tableName),
+ IndexName: aws.String("gerrit-project-id-index"),
+ Limit: aws.Int64(HugePageSize),
}
for {
- results, err := repo.dynamoDBClient.Scan(scanInput)
+ results, err := repo.dynamoDBClient.Query(queryInput)
if err != nil {
log.WithFields(f).WithError(err).Warnf("error retrieving gerrit instances, error: %v", err)
return nil, err
@@ -305,7 +313,7 @@ func (repo repo) GetClaGroupGerrits(ctx context.Context, projectID string, proje
}
if len(results.LastEvaluatedKey) != 0 {
- scanInput.ExclusiveStartKey = results.LastEvaluatedKey
+ queryInput.ExclusiveStartKey = results.LastEvaluatedKey
} else {
break
}
@@ -322,7 +330,7 @@ func (repo repo) GetClaGroupGerrits(ctx context.Context, projectID string, proje
// DeleteGerrit removes the gerrit instance based on the gerrit ID
func (repo *repo) DeleteGerrit(ctx context.Context, gerritID string) error {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.DeleteGerrit",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritID": gerritID,
}
@@ -347,7 +355,7 @@ func (repo *repo) DeleteGerrit(ctx context.Context, gerritID string) error {
func (repo *repo) ExistsByName(ctx context.Context, gerritName string) ([]*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.ExistsByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritName": gerritName,
}
@@ -419,7 +427,7 @@ func (repo *repo) ExistsByName(ctx context.Context, gerritName string) ([]*model
func (repo *repo) ExistsByID(ctx context.Context, gerritID string) ([]*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.repository.ExistsByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritID": gerritID,
}
diff --git a/cla-backend-go/gerrits/service.go b/cla-backend-go/gerrits/service.go
index 180f1fbbf..bb3a73646 100644
--- a/cla-backend-go/gerrits/service.go
+++ b/cla-backend-go/gerrits/service.go
@@ -49,7 +49,7 @@ func NewService(repo Repository, lfg *LFGroup) Service {
func (s service) AddGerrit(ctx context.Context, claGroupID string, projectSFID string, params *models.AddGerritInput, claGroupModel *models.ClaGroup) (*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "gerrits.AddGerrit",
+ "functionName": "v1.gerrits.AddGerrit",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFID": projectSFID,
@@ -164,7 +164,7 @@ func (s service) GetGerritsByProjectSFID(ctx context.Context, projectSFID string
func (s service) GetClaGroupGerrits(ctx context.Context, claGroupID string, projectSFID *string) (*models.GerritList, error) {
f := logrus.Fields{
- "functionName": "gerrits.GetClaGroupGerrits",
+ "functionName": "v1.gerrits.GetClaGroupGerrits",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFID": *projectSFID,
@@ -175,6 +175,7 @@ func (s service) GetClaGroupGerrits(ctx context.Context, claGroupID string, proj
return nil, err
}
+ log.WithFields(f).Debugf("discovered %d gerrits", len(responseModel.List))
// Add the repo list to the response model
for _, gerrit := range responseModel.List {
log.WithFields(f).Debugf("Processing gerrit URL: %s", gerrit.GerritURL)
@@ -221,7 +222,7 @@ func extractGerritHost(gerritHost string, f logrus.Fields) (string, error) {
func (s service) GetGerritRepos(ctx context.Context, gerritHost string) (*models.GerritRepoList, error) {
f := logrus.Fields{
- "functionName": "gerrits.GetGerritRepos",
+ "functionName": "v1.gerrits.GetGerritRepos",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritName": gerritHost,
}
@@ -314,7 +315,7 @@ func buildContributorAgreementDetails(serverInfo *ServerInfo) []*models.GerritRe
// listGerritRepos returns a list of gerrit repositories for the given gerrit host
func listGerritRepos(ctx context.Context, gerritHost string) (map[string]GerritRepoInfo, error) {
f := logrus.Fields{
- "functionName": "gerrits.listGerritRepos",
+ "functionName": "v1.gerrits.listGerritRepos",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritHost": gerritHost,
}
@@ -354,7 +355,7 @@ func listGerritRepos(ctx context.Context, gerritHost string) (map[string]GerritR
// getGerritConfig returns the gerrit configuration for the specified host
func getGerritConfig(ctx context.Context, gerritHost string) (*ServerInfo, error) {
f := logrus.Fields{
- "functionName": "gerrits.getGerritConfig",
+ "functionName": "v1.gerrits.getGerritConfig",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritHost": gerritHost,
}
@@ -394,7 +395,7 @@ func getGerritConfig(ctx context.Context, gerritHost string) (*ServerInfo, error
// getGerritAPIPath returns the path to the API based on the gerrit host
func getGerritAPIPath(ctx context.Context, gerritHost string) (string, error) {
f := logrus.Fields{
- "functionName": "gerrits.getGerritAPIPath",
+ "functionName": "v1.gerrits.getGerritAPIPath",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritHost": gerritHost,
}
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index 364daef5e..af3fcdd8d 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -8,8 +8,12 @@ import (
"fmt"
"strings"
+ "github.com/sirupsen/logrus"
+
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
@@ -22,6 +26,8 @@ import (
"github.com/jinzhu/copier"
)
+const decodeErrorMsg = "unable to decode response as a v2 model"
+
type ProjectService interface { //nolint
GetCLAGroupByID(ctx context.Context, claGroupID string) (*v1Models.ClaGroup, error)
}
@@ -33,35 +39,47 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsDeleteGerritHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "claGroupID": params.ClaGroupID,
+ "gerritID": params.GerritID,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+ log.WithFields(f).Debugf("querying for gerrits using gerrit ID: %s", params.GerritID)
gerrit, err := v1Service.GetGerrit(ctx, params.GerritID)
if err != nil {
+ msg := fmt.Sprintf("unable to locate gerrit by ID: %s", params.GerritID)
+ log.WithFields(f).Warn(msg)
if err == v1Gerrits.ErrGerritNotFound {
- return gerrits.NewDeleteGerritNotFound().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ return gerrits.NewDeleteGerritNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
- return gerrits.NewDeleteGerritInternalServerError().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ return gerrits.NewDeleteGerritInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
}
+
if gerrit.ProjectSFID != params.ProjectSFID || gerrit.ProjectID != params.ClaGroupID {
- return gerrits.NewDeleteGerritBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "EasyCLA - 403 Bad Request - projectSFID or claGroupID does not match with provided gerrit record",
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("projectSFID %s or claGroupID %s does not match with provided gerrit record", params.ProjectSFID, params.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewDeleteGerritBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
+
// verify user have access to the project
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- return gerrits.NewDeleteGerritForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to DeleteGerrit with Project scope of %s",
- authUser.UserName, gerrit.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("user %s does not have access to DeleteGerrit with Project scope of %s",
+ authUser.UserName, gerrit.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewDeleteGerritForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
// delete the gerrit
err = v1Service.DeleteGerrit(ctx, params.GerritID)
if err != nil {
- return gerrits.NewDeleteGerritBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ msg := "unable to delete gerrit instance"
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewDeleteGerritForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
// record the event
@@ -148,39 +166,52 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsListGerritsHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "claGroupID": params.ClaGroupID,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
// verify user have access to the project
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- return gerrits.NewListGerritsForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to ListGerrits with Project scope of %s",
- authUser.UserName, params.ProjectSFID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("user %s does not have access to list gerrits with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewListGerritsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
+ log.WithFields(f).Debug("checking if project CLA Group mapping...")
ok, err := projectsClaGroupsRepo.IsAssociated(params.ProjectSFID, params.ClaGroupID)
if err != nil {
- return gerrits.NewListGerritsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ msg := fmt.Sprintf("unable to determine project CLA group association for project: %s and CLA Group: %s", params.ProjectSFID, params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewListGerritsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
+
if !ok {
- return gerrits.NewListGerritsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "provided cla-group and project are not associated with each other",
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("provided CLA Group %s and project %s are not associated with each other", params.ProjectSFID, params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewListGerritsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
+ log.WithFields(f).Debug("querying for gerrits...")
result, err := v1Service.GetClaGroupGerrits(ctx, params.ClaGroupID, ¶ms.ProjectSFID)
if err != nil {
+ msg := fmt.Sprintf("problem fetching gerrit repositories using CLA Group: %s with project SFID: %s", params.ClaGroupID, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
return gerrits.NewListGerritsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
+ log.WithFields(f).Debugf("discovered %d gerrits", len(result.List))
var response models.GerritList
err = copier.Copy(&response, result)
if err != nil {
- return gerrits.NewListGerritsInternalServerError().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ log.WithFields(f).WithError(err).Warn(decodeErrorMsg)
+ return gerrits.NewListGerritsInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, decodeErrorMsg, err))
}
+
return gerrits.NewListGerritsOK().WithXRequestID(reqID).WithPayload(&response)
})
@@ -189,35 +220,42 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsGetGerritReposHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "gerritHost": params.GerritHost.String(),
+ }
// No specific permissions required
// Validate input
if params.GerritHost == nil {
- return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "missing gerritHost query parameter - expecting gerrit hostname",
- XRequestID: reqID,
- })
+ msg := "missing gerrit host query parameter - expecting gerrit hostname"
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
if len(strings.TrimSpace(params.GerritHost.String())) == 0 {
- return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "invalid gerritHost query parameter - expecting gerrit hostname",
- XRequestID: reqID,
- })
+ msg := "invalid gerritHost query parameter - expecting gerrit hostname"
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
+ log.WithFields(f).Debugf("querying for gerrits using hostname: %s...", params.GerritHost.String())
result, err := v1Service.GetGerritRepos(ctx, params.GerritHost.String())
if err != nil {
- return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ msg := fmt.Sprintf("problem fetching gerrit repositories using gerrit host: %s", params.GerritHost.String())
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewGetGerritReposBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
var response models.GerritRepoList
err = copier.Copy(&response, result)
if err != nil {
- return gerrits.NewAddGerritInternalServerError().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ log.WithFields(f).WithError(err).Warn(decodeErrorMsg)
+ return gerrits.NewAddGerritInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, decodeErrorMsg, err))
}
return gerrits.NewGetGerritReposOK().WithXRequestID(reqID).WithPayload(&response)
From 0d88b3d6d997bd795b96407080d4725c5dec9038 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Tue, 23 Mar 2021 13:03:59 +0200
Subject: [PATCH 0176/1276] remove CLA Manager console link from icla email
Signed-off-by: makkalot
---
cla-backend/cla/models/docusign_models.py | 27 +++++++++++++------
.../cla/tests/unit/test_docusign_models.py | 6 +++++
2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index bea387f4d..836187325 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -2220,14 +2220,25 @@ def document_signed_email_content(icla: bool, project: Project, signature: Signa
recipient_name = "CLA Manager"
subject = f'EasyCLA: CLA Signed for {project.get_project_name()}'
- body = f'''
-
Hello {recipient_name},
-
This is a notification email from EasyCLA regarding the project {project.get_project_name()}.
+ '''
body = append_email_help_sign_off_content(body, project.get_version())
return subject, body
diff --git a/cla-backend/cla/tests/unit/test_docusign_models.py b/cla-backend/cla/tests/unit/test_docusign_models.py
index fc95cefbd..aaa6021aa 100644
--- a/cla-backend/cla/tests/unit/test_docusign_models.py
+++ b/cla-backend/cla/tests/unit/test_docusign_models.py
@@ -826,6 +826,7 @@ def test_document_signed_email_content():
assert "Hello john" in body
assert "EasyCLA regarding the project JohnsProject" in body
assert "The CLA has now been signed." in body
+ assert "alt=\"CCLA Document Link\"" in body
# try with different recipient names
user.set_user_name(None)
@@ -858,7 +859,12 @@ def test_document_signed_email_content():
user=user
)
+ assert "Signed for JohnsProject" in subject
assert "Hello Contributor" in body
+ assert "EasyCLA regarding the project JohnsProject" in body
+ assert "The CLA has now been signed." in body
+ assert "alt=\"ICLA Document Link\"" in body
+ assert "EasyCLA CLA Manager console" not in body
def test_cla_signatory_email_content():
From 12691ddc231f2ca06f41adc6eba1c12dd67bcfb1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 23 Mar 2021 11:25:27 -0700
Subject: [PATCH 0177/1276] Bump elliptic from 6.5.3 to 6.5.4 in
/cla-frontend-contributor-console/edge (#2764)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.../edge/yarn.lock | 32 +++++++++----------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index 726831108..3bc944542 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -396,10 +396,10 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0:
- version "4.11.9"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
- integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+ integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.1.1:
version "5.1.2"
@@ -437,7 +437,7 @@ braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
-brorand@^1.0.1:
+brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
@@ -910,17 +910,17 @@ ecc-jsbn@~0.1.1:
safer-buffer "^2.1.0"
elliptic@^6.0.0, elliptic@^6.5.2:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
- integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
+ integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
- bn.js "^4.4.0"
- brorand "^1.0.1"
+ bn.js "^4.11.9"
+ brorand "^1.1.0"
hash.js "^1.0.0"
- hmac-drbg "^1.0.0"
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.0"
+ hmac-drbg "^1.0.1"
+ inherits "^2.0.4"
+ minimalistic-assert "^1.0.1"
+ minimalistic-crypto-utils "^1.0.1"
emojis-list@^3.0.0:
version "3.0.0"
@@ -1430,7 +1430,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-hmac-drbg@^1.0.0:
+hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
@@ -2026,7 +2026,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
From 6794f1eb129078186e7873a483e011f96fd11a05 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 23 Mar 2021 11:25:56 -0700
Subject: [PATCH 0178/1276] Bump elliptic from 6.5.3 to 6.5.4 in
/cla-frontend-project-console/src (#2763)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/yarn.lock | 34 +++++++++++-----------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 099e00971..610bff4d9 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -491,10 +491,10 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.9"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
- integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9:
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+ integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
body-parser@1.19.0:
version "1.19.0"
@@ -557,7 +557,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
-brorand@^1.0.1:
+brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
@@ -1161,17 +1161,17 @@ electron-to-chromium@^1.3.30:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.185.tgz#36368514eb719632a91435f3c9f57c98b47d81de"
elliptic@^6.0.0:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
- integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
+ integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
- bn.js "^4.4.0"
- brorand "^1.0.1"
+ bn.js "^4.11.9"
+ brorand "^1.1.0"
hash.js "^1.0.0"
- hmac-drbg "^1.0.0"
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.0"
+ hmac-drbg "^1.0.1"
+ inherits "^2.0.4"
+ minimalistic-assert "^1.0.1"
+ minimalistic-crypto-utils "^1.0.1"
emojis-list@^2.0.0:
version "2.1.0"
@@ -1828,7 +1828,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-hmac-drbg@^1.0.0:
+hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
@@ -1935,7 +1935,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2561,7 +2561,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
From 998f7fa320047365793908c2ce73910a1706386a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 23 Mar 2021 11:26:34 -0700
Subject: [PATCH 0179/1276] Bump elliptic from 6.5.3 to 6.5.4 in
/cla-frontend-contributor-console/src (#2761)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.../src/yarn.lock | 34 +++++++++----------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 58f389e40..1cd7df705 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -473,10 +473,10 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.9"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
- integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9:
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+ integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
body-parser@1.19.0:
version "1.19.0"
@@ -539,7 +539,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
-brorand@^1.0.1:
+brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
@@ -1143,17 +1143,17 @@ electron-to-chromium@^1.3.30:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.185.tgz#36368514eb719632a91435f3c9f57c98b47d81de"
elliptic@^6.0.0:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
- integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
+ integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
- bn.js "^4.4.0"
- brorand "^1.0.1"
+ bn.js "^4.11.9"
+ brorand "^1.1.0"
hash.js "^1.0.0"
- hmac-drbg "^1.0.0"
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.0"
+ hmac-drbg "^1.0.1"
+ inherits "^2.0.4"
+ minimalistic-assert "^1.0.1"
+ minimalistic-crypto-utils "^1.0.1"
emojis-list@^2.0.0:
version "2.1.0"
@@ -1805,7 +1805,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-hmac-drbg@^1.0.0:
+hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
@@ -1912,7 +1912,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2506,7 +2506,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
From 8313e5c437d4f166e316317ba136a6085af052c2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 23 Mar 2021 11:27:27 -0700
Subject: [PATCH 0180/1276] Bump elliptic from 6.5.3 to 6.5.4 in
/cla-frontend-corporate-console/src (#2762)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/yarn.lock | 34 ++++++++++----------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index eaafcb25d..cbcf7ced9 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -469,10 +469,10 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.9"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
- integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9:
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
+ integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
body-parser@1.19.0:
version "1.19.0"
@@ -535,7 +535,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
-brorand@^1.0.1:
+brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
@@ -1130,17 +1130,17 @@ electron-to-chromium@^1.3.30:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.185.tgz#36368514eb719632a91435f3c9f57c98b47d81de"
elliptic@^6.0.0:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
- integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
+ integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
- bn.js "^4.4.0"
- brorand "^1.0.1"
+ bn.js "^4.11.9"
+ brorand "^1.1.0"
hash.js "^1.0.0"
- hmac-drbg "^1.0.0"
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.0"
+ hmac-drbg "^1.0.1"
+ inherits "^2.0.4"
+ minimalistic-assert "^1.0.1"
+ minimalistic-crypto-utils "^1.0.1"
emojis-list@^2.0.0:
version "2.1.0"
@@ -1775,7 +1775,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-hmac-drbg@^1.0.0:
+hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
@@ -1870,7 +1870,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2455,7 +2455,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
From 6f083950332ad9c8d2c0768a4d4cf3bba454495b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 23 Mar 2021 12:00:23 -0700
Subject: [PATCH 0181/1276] Bump lxml from 4.6.2 to 4.6.3 in /cla-backend
(#2809)
Bumps [lxml](https://github.com/lxml/lxml) from 4.6.2 to 4.6.3.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.2...lxml-4.6.3)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index ec9c6d220..eec1d6348 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -25,7 +25,7 @@ Jinja2==2.11.3
jmespath==0.9.4
lazy-object-proxy==1.4.3
Logbook==1.5.3
-lxml==4.6.2
+lxml==4.6.3
more-itertools==8.0.2
nose2==0.9.1
oauthlib==3.1.0
From 5a3d63ffebca3a0afd7bbe79247d8d8e745089c4 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 24 Mar 2021 03:06:40 +0300
Subject: [PATCH 0182/1276] [#2792] Feature/Invalidate Contributors (#2810)
- Handled signature invalidations for ghorg and domain approval removals
- Implemented ghorg client for getting users under ghorg
- Handled ICLA and ECLA signature updates
Signed-off-by: wanyaland
---
.../cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/github/github_org.go | 30 +++
cla-backend-go/go.sum | 2 +
cla-backend-go/signatures/models.go | 14 ++
cla-backend-go/signatures/repository.go | 211 +++++++++++++++++-
cla-backend-go/utils/constants.go | 15 ++
7 files changed, 273 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index 8cdcfbe47..01ed9b077 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -119,7 +119,7 @@ func init() {
projectClaGroupRepo,
})
- signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo)
usersService := users.NewService(usersRepo, eventsService)
companyService := company.NewService(companyRepo, configFile.CorporateConsoleV1URL, userRepo, usersService)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index ca56957f6..581e55a9f 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -252,7 +252,7 @@ func server(localMode bool) http.Handler {
})
// Signature repository handler
- signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo)
// Initialize the external platform services - these are external APIs that
// we download the swagger specification, generate the models, and have
diff --git a/cla-backend-go/github/github_org.go b/cla-backend-go/github/github_org.go
index 3ba8774fc..fb8df7581 100644
--- a/cla-backend-go/github/github_org.go
+++ b/cla-backend-go/github/github_org.go
@@ -44,3 +44,33 @@ func GetOrganization(ctx context.Context, organizationName string) (*github.Orga
}
return org, nil
}
+
+//GetOrganizationMembers gets members in organization
+func GetOrganizationMembers(ctx context.Context, orgName string, installationID int64) ([]string, error) {
+ f := logrus.Fields{
+ "functionName": "GetOrganizationMembers",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ client, err := NewGithubAppClient(installationID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to create a github client, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ users, resp, err := client.Organizations.ListMembers(ctx, orgName, nil)
+
+ if resp.StatusCode < 200 || resp.StatusCode > 299 || err != nil {
+ msg := fmt.Sprintf("List Org Members failed for Organization: %s with no success response code %d. error = %s", orgName, resp.StatusCode, err.Error())
+ log.WithFields(f).Warnf(msg)
+ return nil, errors.New(msg)
+ }
+
+ var ghUsernames []string
+ for _, user := range users {
+ log.WithFields(f).Debugf("user :%s found for organization: %s", *user.Login, orgName)
+ ghUsernames = append(ghUsernames, *user.Login)
+ }
+ return ghUsernames, nil
+}
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 02cb0d7bd..19499d7aa 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -95,6 +95,8 @@ github.com/communitybridge/easycla v1.0.118 h1:8yrsOQ+ENUFi4RFl1krRlIxc51lzZNuti
github.com/communitybridge/easycla v1.0.123 h1:Lh5i/9aajrTYItxNpVCmi9T1yyIfnQIOk0tC2Wtslvk=
github.com/communitybridge/easycla v1.0.133 h1:aJulQGLLRISCMsZcCP4aIE8xGtHoBNm/EmA00n3NYVA=
github.com/communitybridge/easycla v1.0.135 h1:Dvn8jX+7BAnpmA+jvdK0n5ajWP8SoH5vvopt7whZDEU=
+github.com/communitybridge/easycla v1.0.145 h1:ikhBSsOeEL2u3/EoyDsufh/j3HkjfFTiXAk1d61GoS8=
+github.com/communitybridge/easycla v2.0.10+incompatible h1:6eRJ5fxrMxRZHBkg8piYo+zHTcSowMrP85nZXzp5mpA=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 7cc53f4d2..dcc2b2e2a 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -16,3 +16,17 @@ type ApprovalCriteria struct {
UserEmail string
GitHubUsername string
}
+
+//ApprovalList ...
+type ApprovalList struct {
+ Criteria string
+ ApprovalList []string
+ Action string
+ ClaGroupID string
+ CompanyID string
+ DomainApprovals []string
+ GHOrgApprovals []string
+ GitHubUsernameApprovals []string
+ EmailApprovals []string
+ GHUsernames []string
+}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 9318de921..edf7a89b5 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -21,6 +21,9 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
+ "github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
@@ -90,17 +93,21 @@ type repository struct {
companyRepo company.IRepository
usersRepo users.UserRepository
eventsService events.Service
+ repositoriesRepo repositories.Repository
+ ghOrgRepo github_organizations.Repository
signatureTableName string
}
// NewRepository creates a new instance of the whitelist service
-func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service) SignatureRepository {
+func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.Repository) SignatureRepository {
return repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
companyRepo: companyRepo,
usersRepo: usersRepo,
eventsService: eventsService,
+ repositoriesRepo: repositoriesRepo,
+ ghOrgRepo: ghOrgRepo,
signatureTableName: fmt.Sprintf("cla-%s-signatures", stage),
}
}
@@ -1975,6 +1982,22 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
companyID, projectID, signed, approved)
}
+ // Get CCLA signature - For Approval List info
+ cclaSignature, err := repo.GetCorporateSignature(ctx, projectID, companyID)
+ if err != nil {
+ msg := "unable to get corporate signature"
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Keep track of existing company approvals
+ approvalList := ApprovalList{
+ DomainApprovals: cclaSignature.DomainApprovalList,
+ GHOrgApprovals: cclaSignature.GithubOrgApprovalList,
+ GitHubUsernameApprovals: cclaSignature.GithubUsernameApprovalList,
+ EmailApprovals: cclaSignature.EmailApprovalList,
+ }
+
// Just grab and use the first one - need to figure out conflict resolution if more than one
sig := sigs.Signatures[0]
expressionAttributeNames := map[string]*string{}
@@ -2049,6 +2072,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if params.AddDomainApprovalList != nil || params.RemoveDomainApprovalList != nil {
+
columnName := "domain_whitelist"
attrList := buildApprovalAttributeList(ctx, sig.DomainApprovalList, params.AddDomainApprovalList, params.RemoveDomainApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
@@ -2067,6 +2091,18 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
expressionAttributeValues[":d"] = attrList
updateExpression = updateExpression + " #D = :d, "
}
+ if params.RemoveDomainApprovalList != nil {
+ var invalidateErr error
+ approvalList.Criteria = utils.EmailDomainCriteria
+ approvalList.ApprovalList = params.RemoveDomainApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
+ if invalidateErr != nil {
+ msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ }
}
if params.AddGithubUsernameApprovalList != nil || params.RemoveGithubUsernameApprovalList != nil {
@@ -2147,6 +2183,57 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
expressionAttributeValues[":go"] = attrList
updateExpression = updateExpression + " #GO = :go, "
}
+
+ if params.RemoveGithubOrgApprovalList != nil {
+ var invalidateErr error
+ approvalList.Criteria = utils.GitHubOrgCriteria
+ approvalList.ApprovalList = params.RemoveGithubOrgApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ // Get repositories by CLAGroup
+ repositories, err := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
+ if err != nil {
+ msg := fmt.Sprintf("unable to fetch repositories for claGroupID: %s ", projectID)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ var ghOrgRepositories []*models.GithubRepository
+ var ghOrgs []*models.GithubOrganization
+ for _, repository := range repositories {
+ // Check for matching organization name in repositories table against approvalList removal GH Orgs
+ if utils.StringInSlice(repository.RepositoryOrganizationName, approvalList.ApprovalList) {
+ ghOrgRepositories = append(ghOrgRepositories, repository)
+ }
+ }
+
+ for _, ghOrgRepo := range ghOrgRepositories {
+ ghOrg, err := repo.ghOrgRepo.GetGithubOrganization(ctx, ghOrgRepo.RepositoryOrganizationName)
+ if err != nil {
+ msg := fmt.Sprintf("unable to get gh org by name: %s ", ghOrgRepo.RepositoryOrganizationName)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ ghOrgs = append(ghOrgs, ghOrg)
+ }
+
+ var ghUsernames []string
+ for _, ghOrg := range ghOrgs {
+ ghOrgUsers, err := github.GetOrganizationMembers(ctx, ghOrg.OrganizationName, ghOrg.OrganizationInstallationID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to fetch ghOrgUsers for org: %s ", ghOrg.OrganizationName)
+ log.WithFields(f).Warnf(msg)
+ return nil, errors.New(msg)
+ }
+ ghUsernames = append(ghUsernames, ghOrgUsers...)
+ }
+ approvalList.GHUsernames = utils.RemoveDuplicates(ghUsernames)
+
+ invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
+ if invalidateErr != nil {
+ msg := fmt.Sprintf("unable to invalidate signatures based on Approval List: %+v ", approvalList)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ }
}
// Ensure at least one value is set for us to update
@@ -2206,6 +2293,128 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return updatedSig.Signatures[0], nil
}
+// invalidateSignatures is a helper function that invalidates signature records based on approval list
+func (repo repository) invalidateSignatures(ctx context.Context, approvalList *ApprovalList, claManager *models.User) error {
+ f := logrus.Fields{
+ "functionName": "invalidateSignatures",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": &approvalList,
+ }
+
+ // Get ICLAs
+ iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil)
+ if err != nil {
+ log.WithFields(f).Warn("unable to get iclas")
+ return err
+ }
+
+ // Get ECLAs
+ companyProjectParams := signatures.GetProjectCompanyEmployeeSignaturesParams{
+ CompanyID: approvalList.CompanyID,
+ ProjectID: approvalList.ClaGroupID,
+ }
+ eclas, err := repo.GetProjectCompanyEmployeeSignatures(ctx, companyProjectParams, nil, int64(10))
+ if err != nil {
+ log.WithFields(f).Warnf("unable to get cclas for company: %s and project: %s ", approvalList.CompanyID, approvalList.ClaGroupID)
+ return err
+ }
+
+ var iclaWg, eclaWg sync.WaitGroup
+
+ //Iterate iclas
+ iclaWg.Add(len(iclas.List))
+ log.WithFields(f).Debug("invalidating signature icla records... ")
+
+ for _, icla := range iclas.List {
+ go func(icla *models.IclaSignature) {
+ defer iclaWg.Done()
+ signature, err := repo.GetSignature(ctx, icla.SignatureID)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to fetch signature for ID: %s ", icla.SignatureID)
+ return
+ }
+ // Grab user record
+ if signature.SignatureReferenceID == "" {
+ log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", signature)
+ return
+ }
+ verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
+ if verifyErr != nil {
+ log.WithFields(f).Warnf("unable to verify user: %s ", signature.SignatureReferenceID)
+ return
+ }
+ }(icla)
+ }
+ iclaWg.Wait()
+
+ log.WithFields(f).Debug("invalidating signature ecla records... ")
+ // Iterate eclas
+ eclaWg.Add(len(eclas.Signatures))
+ for _, ecla := range eclas.Signatures {
+ go func(ecla *models.Signature) {
+ defer eclaWg.Done()
+ // Grab user record
+ if ecla.SignatureReferenceID == "" {
+ log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", ecla)
+ return
+ }
+ verifyErr := repo.verifyUserApprovals(ctx, ecla.SignatureReferenceID, ecla.SignatureID, claManager, approvalList)
+ if verifyErr != nil {
+ log.WithFields(f).Warnf("unable to verify user: %s ", ecla.SignatureReferenceID)
+ return
+ }
+ }(ecla)
+ }
+ eclaWg.Wait()
+
+ return nil
+}
+
+// verify UserApprovals checks user
+func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) error {
+ f := logrus.Fields{
+ "functionName": "verifyUserApprovals",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "userID": userID,
+ }
+
+ user, err := repo.usersRepo.GetUser(userID)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to get user record for ID: %s ", userID)
+ return err
+ }
+
+ if approvalList.Criteria == utils.EmailDomainCriteria {
+ // Handle Domains
+ if utils.StringInSlice(getBestEmail(user), approvalList.DomainApprovals) {
+ if !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) && !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) {
+ //Invalidate record
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.EmailDomainCriteria)
+ err := repo.InvalidateProjectRecord(ctx, signatureID, note)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
+ return err
+ }
+ }
+ }
+ } else if approvalList.Criteria == utils.GitHubOrgCriteria {
+ // Handle GH Org Approvals
+ if utils.StringInSlice(user.GithubUsername, approvalList.GHUsernames) {
+ if !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) && !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) {
+ //Invalidate record
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.GitHubOrgCriteria)
+ err := repo.InvalidateProjectRecord(ctx, signatureID, note)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
// removeColumn is a helper function to remove a given column when we need to zero out the column value - typically the approval list
func (repo repository) removeColumn(ctx context.Context, signatureID, columnName string) (*models.Signature, error) {
f := logrus.Fields{
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 4f22d383f..2336e11db 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -149,3 +149,18 @@ const EmailLabel = "Email Address"
//UserLabel represents the LF/EasyCLA username
const UserLabel = "Username"
+
+//EmailDomainCriteria represents approval based on email domain
+const EmailDomainCriteria = "Email Domain Criteria"
+
+//EmailCriteria represents approvals based on email addresses
+const EmailCriteria = "Email Criteria"
+
+//GitHubOrgCriteria represents approvals based on GH org membership
+const GitHubOrgCriteria = "GitHub Org Criteria"
+
+//AddApprovals is an action for adding approvals
+const AddApprovals = "AddApprovals"
+
+//RemoveApprovals is an action for removing approvals
+const RemoveApprovals = "RemoveApprovals"
From 8a66c9fad0d9382f9c4c3b5fd153bd95e27b7216 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 24 Mar 2021 10:43:32 -0700
Subject: [PATCH 0183/1276] [#2973] Added Gerrit Add/Remove User from Gerrit
Logic (#2811)
- Added GetUsersOfGroup service API
- Added AddUserToGroup service API to add users to a given CLA Group/Gerrit instance
- Added RemoveUserFromGroup service API to remove users to a given CLA Group/Gerrit instance
- Added logging
- Added HTTP API calls to the identity system
- Added event logging
Signed-off-by: David Deal
---
cla-backend-go/cmd/server.go | 9 +-
cla-backend-go/docraptor/client.go | 10 +-
cla-backend-go/events/event_data.go | 56 ++++
cla-backend-go/events/event_types.go | 2 +
cla-backend-go/gerrits/lf_group.go | 312 +++++++++++++++++-
cla-backend-go/gerrits/repository.go | 11 +-
cla-backend-go/gerrits/service.go | 225 ++++++++++++-
cla-backend-go/project/helpers.go | 2 +-
cla-backend-go/project/repository.go | 2 +-
cla-backend-go/signatures/handlers.go | 37 ++-
cla-backend-go/swagger/cla.v2.yaml | 205 ++++++++++++
.../swagger/common/gerrit-group-response.yaml | 38 +++
.../swagger/common/gerrit-user-list.yaml | 10 +
cla-backend-go/utils/errors.go | 19 ++
cla-backend-go/v2/gerrits/handlers.go | 195 ++++++++++-
15 files changed, 1078 insertions(+), 55 deletions(-)
create mode 100644 cla-backend-go/swagger/common/gerrit-group-response.yaml
create mode 100644 cla-backend-go/swagger/common/gerrit-user-list.yaml
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 581e55a9f..4356536f7 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -285,10 +285,11 @@ func server(localMode bool) http.Handler {
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, eventsService, autoEnableService)
gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{
- LfBaseURL: configFile.LFGroup.ClientURL,
- ClientID: configFile.LFGroup.ClientID,
- ClientSecret: configFile.LFGroup.ClientSecret,
- RefreshToken: configFile.LFGroup.RefreshToken,
+ LfBaseURL: configFile.LFGroup.ClientURL,
+ ClientID: configFile.LFGroup.ClientID,
+ ClientSecret: configFile.LFGroup.ClientSecret,
+ RefreshToken: configFile.LFGroup.RefreshToken,
+ EventsService: eventsService,
})
v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, projectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
diff --git a/cla-backend-go/docraptor/client.go b/cla-backend-go/docraptor/client.go
index 419ec26e5..89ffd83da 100644
--- a/cla-backend-go/docraptor/client.go
+++ b/cla-backend-go/docraptor/client.go
@@ -49,7 +49,7 @@ func NewDocraptorClient(key string, testMode bool) (Client, error) {
// CreatePDF accepts an HTML document and returns a PDF
func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
f := logrus.Fields{
- "functionName": "CreatePDF",
+ "functionName": "v1.docraptor.client.CreatePDF",
"claType": claType,
}
@@ -69,9 +69,15 @@ func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
log.WithFields(f).Debug("Generating PDF using docraptor...")
resp, err := http.Post(dc.url, "application/json", bytes.NewBuffer(documentBytes))
if err != nil {
- log.WithFields(f).Warnf("problem with API call to docraptor, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn("problem with API call to docraptor")
return nil, err
}
+ defer func() {
+ closeErr := resp.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
return resp.Body, nil
}
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index cc7c273ed..75e7ab1f7 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -73,6 +73,18 @@ type GerritDeletedEventData struct {
GerritRepositoryName string
}
+// GerritUserAddedEventData . . .
+type GerritUserAddedEventData struct {
+ Username string
+ GroupName string
+}
+
+// GerritUserRemovedEventData . . .
+type GerritUserRemovedEventData struct {
+ Username string
+ GroupName string
+}
+
// GitHubProjectDeletedEventData . . .
type GitHubProjectDeletedEventData struct {
DeletedCount int
@@ -695,6 +707,18 @@ func (ed *GerritDeletedEventData) GetEventDetailsString(args *LogEventArgs) (str
return data, true
}
+// GetEventDetailsString . . .
+func (ed *GerritUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The username %s was add to the gerrit group %s by the user %s.", ed.Username, ed.GroupName, args.UserName)
+ return data, true
+}
+
+// GetEventDetailsString . . .
+func (ed *GerritUserRemovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The username %s was removed from the gerrit group %s by the user %s.", ed.Username, ed.GroupName, args.UserName)
+ return data, true
+}
+
// GetEventDetailsString . . .
func (ed *GitHubProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub Repositories were deleted due to CLA Group/Project: [%s] deletion.",
@@ -1482,6 +1506,38 @@ func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (str
return data, true
}
+// GetEventSummaryString . . .
+func (ed *GerritUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The username %s was add to the gerrit group %s", ed.Username, ed.GroupName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString . . .
+func (ed *GerritUserRemovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The username %s was removed from the gerrit group %s", ed.Username, ed.GroupName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventSummaryString . . .
func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub repositories were deleted due to CLA Group/project deletion",
diff --git a/cla-backend-go/events/event_types.go b/cla-backend-go/events/event_types.go
index d9753be8d..ae6f84e73 100644
--- a/cla-backend-go/events/event_types.go
+++ b/cla-backend-go/events/event_types.go
@@ -40,6 +40,8 @@ const (
GerritRepositoryAdded = "gerrit_repository.added"
GerritRepositoryDeleted = "gerrit_repository.deleted"
+ GerritUserAdded = "gerrit_user.added"
+ GerritUserRemoved = "gerrit_user.deleted"
GitHubOrganizationAdded = "github_organization.added"
GitHubOrganizationDeleted = "github_organization.deleted"
diff --git a/cla-backend-go/gerrits/lf_group.go b/cla-backend-go/gerrits/lf_group.go
index 687401e66..f3b987b72 100644
--- a/cla-backend-go/gerrits/lf_group.go
+++ b/cla-backend-go/gerrits/lf_group.go
@@ -5,11 +5,21 @@ package gerrits
import (
"bytes"
+ "context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
+
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+
+ "github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
)
// constants
@@ -19,10 +29,11 @@ const (
// LFGroup contains access information of lf LDAP group
type LFGroup struct {
- LfBaseURL string
- ClientID string
- ClientSecret string
- RefreshToken string
+ LfBaseURL string
+ ClientID string
+ ClientSecret string
+ RefreshToken string
+ EventsService events.Service
}
// LDAPGroup model
@@ -30,18 +41,24 @@ type LDAPGroup struct {
Title string `json:"title"`
}
-func (lfg *LFGroup) getAccessToken() (string, error) {
+func (lfg *LFGroup) getAccessToken(ctx context.Context) (string, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.lf_group.getAccessToken",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
requestBody, err := json.Marshal(map[string]string{
"grant_type": "refresh_token",
"refresh_token": lfg.RefreshToken,
"scope": "manage_groups",
})
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem encoding access token request")
return "", err
}
OauthURL := fmt.Sprintf("%s/oauth2/token", lfg.LfBaseURL)
req, err := http.NewRequest("POST", OauthURL, bytes.NewBuffer(requestBody))
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating a new request to URL: %s", OauthURL)
return "", err
}
req.SetBasicAuth(lfg.ClientID, lfg.ClientSecret)
@@ -52,32 +69,53 @@ func (lfg *LFGroup) getAccessToken() (string, error) {
}
res, err := client.Do(req)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem sending a request to URL: %s", OauthURL)
return "", err
}
- defer res.Body.Close()
+
+ defer func() {
+ closeErr := res.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
+
body, err := ioutil.ReadAll(res.Body)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem reading the response from URL: %s", OauthURL)
return "", err
}
+
var out struct {
AccessToken string `json:"access_token"`
}
+
err = json.Unmarshal(body, &out)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem unmarshalling the response from URL: %s", OauthURL)
return "", err
}
+
return out.AccessToken, nil
}
// GetGroup returns LF LDAP group
-func (lfg *LFGroup) GetGroup(groupID string) (*LDAPGroup, error) {
- accessToken, err := lfg.getAccessToken()
+func (lfg *LFGroup) GetGroup(ctx context.Context, groupID string) (*LDAPGroup, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.lf_group.GetGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "groupID": groupID,
+ }
+
+ accessToken, err := lfg.getAccessToken(ctx)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading access token")
return nil, err
}
getGroupURL := fmt.Sprintf("%s/rest/auth0/og/%s", lfg.LfBaseURL, groupID)
req, err := http.NewRequest("GET", getGroupURL, nil)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating a new request to URL: %s", getGroupURL)
return nil, err
}
req.Header.Add("Content-Type", "application/json")
@@ -88,17 +126,273 @@ func (lfg *LFGroup) GetGroup(groupID string) (*LDAPGroup, error) {
}
res, err := client.Do(req)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem invoking request to URL: %s", getGroupURL)
return nil, err
}
- defer res.Body.Close()
+
+ defer func() {
+ closeErr := res.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
+
body, err := ioutil.ReadAll(res.Body)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem reading the response from URL: %s", getGroupURL)
return nil, err
}
+
var out LDAPGroup
err = json.Unmarshal(body, &out)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem unmarshalling the response from URL: %s", getGroupURL)
return nil, err
}
+
return &out, nil
}
+
+// GetUsersOfGroup returns a list of members from a group
+func (lfg *LFGroup) GetUsersOfGroup(ctx context.Context, authUser *auth.User, claGroupID, groupName string) (*v2Models.GerritGroupResponse, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.lf_group.GetUsersOfGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "groupName": groupName,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("getting users of group...")
+
+ // Fetch a token for authorization
+ accessToken, err := lfg.getAccessToken(ctx)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading access token")
+ return nil, err
+ }
+
+ // Build the URL path - can take the groupName or numeric value
+ // API Docs: https://confluence.linuxfoundation.org/display/IPM/Drupal+Identity+REST+for+Auth0
+ url := fmt.Sprintf("%s/rest/auth0/og/%s", lfg.LfBaseURL, groupName)
+
+ // Setup the request
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating a new request to URL: %s", url)
+ return nil, err
+ }
+ req.Header.Add("Content-Type", "application/json")
+ req.Header.Add("Authorization", "Bearer "+accessToken)
+ client := http.Client{
+ Timeout: DefaultHTTPTimeout,
+ }
+
+ // Invoke the request
+ resp, err := client.Do(req)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem invoking request to URL: %s", url)
+ return nil, err
+ }
+
+ // Cleanup after
+ defer func() {
+ closeErr := resp.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
+
+ // Check the response code to see how it went - response payload is undefined - just looking for a successful response status code
+ if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
+ log.WithFields(f).Debugf("successfully fetched members from group: %s", groupName)
+
+ var result v2Models.GerritGroupResponse
+ //err = json.NewDecoder(resp.Body).Decode(&result)
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem reading response for url: %s", url)
+ return nil, err
+ }
+
+ log.WithFields(f).Debugf("response body: %+v", string(body))
+ err = json.Unmarshal(body, &result)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem unmarshalling response for url: %s", url)
+ return nil, err
+ }
+ log.WithFields(f).Debugf("response body: %+v", result)
+
+ return &result, nil
+ }
+
+ log.WithFields(f).Warnf("error fetching users from group: %s - response status: %d", groupName, resp.StatusCode)
+ return nil, nil
+}
+
+// AddUserToGroup adds the specified user to the group
+func (lfg *LFGroup) AddUserToGroup(ctx context.Context, authUser *auth.User, claGroupID, groupName, userName string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.lf_group.AddUserToGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "groupName": groupName,
+ "userName": userName,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("adding user to group...")
+
+ // Fetch a token for authorization
+ accessToken, err := lfg.getAccessToken(ctx)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading access token")
+ return err
+ }
+
+ // Build the URL path - can take the groupName or numeric value
+ // API Docs: https://confluence.linuxfoundation.org/display/IPM/Drupal+Identity+REST+for+Auth0
+ url := fmt.Sprintf("%s/rest/auth0/og/%s", lfg.LfBaseURL, groupName)
+
+ // Build the request payload
+ payload := map[string]interface{}{
+ "username": userName,
+ }
+ payloadBytes, err := json.Marshal(payload)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to encode payload for the request to URL: %s", url)
+ return err
+ }
+
+ // Setup the request
+ req, err := http.NewRequest("PUT", url, bytes.NewBuffer(payloadBytes))
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating a new request to URL: %s", url)
+ return err
+ }
+ req.Header.Add("Content-Type", "application/json")
+ req.Header.Add("Authorization", "Bearer "+accessToken)
+ client := http.Client{
+ Timeout: DefaultHTTPTimeout,
+ }
+
+ // Invoke the request
+ resp, err := client.Do(req)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem invoking request to URL: %s", url)
+ return err
+ }
+
+ // Cleanup after
+ defer func() {
+ closeErr := resp.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
+
+ // Check the response code to see how it went - response payload is undefined - just looking for a successful response status code
+ if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
+ log.WithFields(f).Debugf("successfully added user: %s to group: %s", userName, groupName)
+ // Create a log event indicating our success
+ lfg.EventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.GerritUserAdded,
+ LfUsername: authUser.UserName,
+ UserName: authUser.UserName,
+ CLAGroupID: claGroupID,
+ EventData: &events.GerritUserAddedEventData{
+ Username: userName,
+ GroupName: groupName,
+ },
+ })
+ } else {
+ log.WithFields(f).Warnf("error adding added user: %s to group: %s - response status: %d", userName, groupName, resp.StatusCode)
+ }
+
+ return nil
+}
+
+// RemoveUserFromGroup removes the specified user from the group
+func (lfg *LFGroup) RemoveUserFromGroup(ctx context.Context, authUser *auth.User, claGroupID, groupName, userName string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.lf_group.RemoveUserFromGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "groupName": groupName,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("removing user from group...")
+
+ // Fetch a token for authorization
+ accessToken, err := lfg.getAccessToken(ctx)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading access token")
+ return err
+ }
+
+ // Build the URL path - can take the groupName or numeric value
+ // API Docs: https://confluence.linuxfoundation.org/display/IPM/Drupal+Identity+REST+for+Auth0
+ url := fmt.Sprintf("%s/rest/auth0/og/%s", lfg.LfBaseURL, groupName)
+
+ // Build the request payload
+ payload := map[string]interface{}{
+ "username": userName,
+ }
+ payloadBytes, err := json.Marshal(payload)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to encode payload for the request to URL: %s", url)
+ return err
+ }
+
+ // Setup the request
+ req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(payloadBytes))
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem creating a new request to URL: %s", url)
+ return err
+ }
+ req.Header.Add("Content-Type", "application/json")
+ req.Header.Add("Authorization", "Bearer "+accessToken)
+ client := http.Client{
+ Timeout: DefaultHTTPTimeout,
+ }
+
+ // Invoke the request
+ resp, err := client.Do(req)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem invoking request to URL: %s", url)
+ return err
+ }
+
+ // Cleanup after
+ defer func() {
+ closeErr := resp.Body.Close()
+ if closeErr != nil {
+ log.WithFields(f).WithError(closeErr).Warn("error closing response body")
+ }
+ }()
+
+ // Check the response code to see how it went - response payload is undefined - just looking for a successful response status code
+ if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
+ log.WithFields(f).Debugf("successfully removed user: %s from group: %s", userName, groupName)
+ // Create a log event indicating our success
+ lfg.EventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.GerritUserRemoved,
+ LfUsername: authUser.UserName,
+ UserName: authUser.UserName,
+ CLAGroupID: claGroupID,
+ EventData: &events.GerritUserRemovedEventData{
+ Username: userName,
+ GroupName: groupName,
+ },
+ })
+ } else {
+ log.WithFields(f).Warnf("error removing user: %s from group: %s - response status: %d", userName, groupName, resp.StatusCode)
+ }
+
+ return nil
+}
diff --git a/cla-backend-go/gerrits/repository.go b/cla-backend-go/gerrits/repository.go
index 98b37b1aa..e053af72e 100644
--- a/cla-backend-go/gerrits/repository.go
+++ b/cla-backend-go/gerrits/repository.go
@@ -39,7 +39,7 @@ type Repository interface {
GetGerrit(ctx context.Context, gerritID string) (*models.Gerrit, error)
GetGerritsByID(ctx context.Context, ID string, IDType string) (*models.GerritList, error)
GetGerritsByProjectSFID(ctx context.Context, projectSFID string) (*models.GerritList, error)
- GetClaGroupGerrits(ctx context.Context, projectID string, projectSFID *string) (*models.GerritList, error)
+ GetClaGroupGerrits(ctx context.Context, claGroupID string) (*models.GerritList, error)
ExistsByName(ctx context.Context, gerritName string) ([]*models.Gerrit, error)
DeleteGerrit(ctx context.Context, gerritID string) error
}
@@ -259,21 +259,16 @@ func (repo repo) GetGerritsByProjectSFID(ctx context.Context, projectSFID string
return &models.GerritList{List: resultList}, nil
}
-// GetClaGroupGerrits returns the CLA Group gerrit instances based on the CLA Group ID and the project SFID
-func (repo repo) GetClaGroupGerrits(ctx context.Context, claGroupID string, projectSFID *string) (*models.GerritList, error) {
+// GetClaGroupGerrits returns the CLA Group gerrit instances based on the CLA Group ID
+func (repo repo) GetClaGroupGerrits(ctx context.Context, claGroupID string) (*models.GerritList, error) {
f := logrus.Fields{
"functionName": "v1.gerrits.repository.GetClaGroupGerrits",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
- "projectSFID": projectSFID,
}
resultList := make([]*models.Gerrit, 0)
condition := expression.Key("project_id").Equal(expression.Value(claGroupID))
- // No reason to add this additional filter for the v2 API
- //if projectSFID != nil {
- // filter = filter.And(expression.Name("project_sfid").Equal(expression.Value(*projectSFID)))
- //}
expr, err := expression.NewBuilder().WithKeyCondition(condition).Build()
if err != nil {
diff --git a/cla-backend-go/gerrits/service.go b/cla-backend-go/gerrits/service.go
index bb3a73646..41606da14 100644
--- a/cla-backend-go/gerrits/service.go
+++ b/cla-backend-go/gerrits/service.go
@@ -11,6 +11,8 @@ import (
"net/url"
"strings"
+ "github.com/LF-Engineering/lfx-kit/auth"
+
"github.com/go-openapi/strfmt"
"github.com/go-resty/resty/v2"
@@ -19,6 +21,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
@@ -28,10 +31,15 @@ type Service interface {
AddGerrit(ctx context.Context, claGroupID string, projectSFID string, input *models.AddGerritInput, claGroupModel *models.ClaGroup) (*models.Gerrit, error)
GetGerrit(ctx context.Context, gerritID string) (*models.Gerrit, error)
GetGerritsByProjectSFID(ctx context.Context, projectSFID string) (*models.GerritList, error)
- GetClaGroupGerrits(ctx context.Context, claGroupID string, projectSFID *string) (*models.GerritList, error)
+ GetClaGroupGerrits(ctx context.Context, claGroupID string) (*models.GerritList, error)
GetGerritRepos(ctx context.Context, gerritName string) (*models.GerritRepoList, error)
DeleteClaGroupGerrits(ctx context.Context, claGroupID string) (int, error)
DeleteGerrit(ctx context.Context, gerritID string) error
+ GetUsersOfGroup(ctx context.Context, authUser *auth.User, claGroupID, claType string) (*v2Models.GerritGroupResponse, error)
+ AddUserToGroup(ctx context.Context, authUser *auth.User, claGroupID, userName, claType string) error
+ AddUsersToGroup(ctx context.Context, authUser *auth.User, claGroupID string, userNameList []string, claType string) error
+ RemoveUserFromGroup(ctx context.Context, authUser *auth.User, claGroupID, userName, claType string) error
+ RemoveUsersFromGroup(ctx context.Context, authUser *auth.User, claGroupID string, userNameList []string, claType string) error
}
type service struct {
@@ -49,7 +57,7 @@ func NewService(repo Repository, lfg *LFGroup) Service {
func (s service) AddGerrit(ctx context.Context, claGroupID string, projectSFID string, params *models.AddGerritInput, claGroupModel *models.ClaGroup) (*models.Gerrit, error) {
f := logrus.Fields{
- "functionName": "v1.gerrits.AddGerrit",
+ "functionName": "v1.gerrits.service.AddGerrit",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFID": projectSFID,
@@ -122,7 +130,7 @@ func (s service) AddGerrit(ctx context.Context, claGroupID string, projectSFID s
var groupNameCcla, groupNameIcla string
if params.GroupIDIcla != "" {
- group, err := s.lfGroup.GetGroup(params.GroupIDIcla)
+ group, err := s.lfGroup.GetGroup(ctx, params.GroupIDIcla)
if err != nil {
message := fmt.Sprintf("unable to get LDAP ICLA Group: %s", params.GroupIDIcla)
log.WithFields(f).WithError(err).Warnf(message)
@@ -131,7 +139,7 @@ func (s service) AddGerrit(ctx context.Context, claGroupID string, projectSFID s
groupNameIcla = group.Title
}
if params.GroupIDCcla != "" {
- group, err := s.lfGroup.GetGroup(params.GroupIDCcla)
+ group, err := s.lfGroup.GetGroup(ctx, params.GroupIDCcla)
if err != nil {
message := fmt.Sprintf("unable to get LDAP CCLA Group: %s", params.GroupIDCcla)
log.WithFields(f).WithError(err).Warnf(message)
@@ -162,14 +170,13 @@ func (s service) GetGerritsByProjectSFID(ctx context.Context, projectSFID string
return s.repo.GetGerritsByProjectSFID(ctx, projectSFID)
}
-func (s service) GetClaGroupGerrits(ctx context.Context, claGroupID string, projectSFID *string) (*models.GerritList, error) {
+func (s service) GetClaGroupGerrits(ctx context.Context, claGroupID string) (*models.GerritList, error) {
f := logrus.Fields{
- "functionName": "v1.gerrits.GetClaGroupGerrits",
+ "functionName": "v1.gerrits.service.GetClaGroupGerrits",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
- "projectSFID": *projectSFID,
}
- responseModel, err := s.repo.GetClaGroupGerrits(ctx, claGroupID, projectSFID)
+ responseModel, err := s.repo.GetClaGroupGerrits(ctx, claGroupID)
if err != nil {
log.WithFields(f).Warnf("problem getting CLA Group gerrits, error: %+v", err)
return nil, err
@@ -222,7 +229,7 @@ func extractGerritHost(gerritHost string, f logrus.Fields) (string, error) {
func (s service) GetGerritRepos(ctx context.Context, gerritHost string) (*models.GerritRepoList, error) {
f := logrus.Fields{
- "functionName": "v1.gerrits.GetGerritRepos",
+ "functionName": "v1.gerrits.service.GetGerritRepos",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gerritName": gerritHost,
}
@@ -243,12 +250,19 @@ func (s service) GetGerritRepos(ctx context.Context, gerritHost string) (*models
}
func (s service) DeleteClaGroupGerrits(ctx context.Context, claGroupID string) (int, error) {
- gerrits, err := s.repo.GetClaGroupGerrits(ctx, claGroupID, nil)
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.DeleteClaGroupGerrits",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ }
+
+ gerrits, err := s.repo.GetClaGroupGerrits(ctx, claGroupID)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem fetching gerrits for CLA Group: %s", claGroupID)
return 0, err
}
if len(gerrits.List) > 0 {
- log.Debugf(fmt.Sprintf("Deleting gerrits for cla-group :%s ", claGroupID))
+ log.WithFields(f).Debugf(fmt.Sprintf("Deleting gerrits for cla-group :%s ", claGroupID))
for _, gerrit := range gerrits.List {
err = s.repo.DeleteGerrit(ctx, gerrit.GerritID.String())
if err != nil {
@@ -256,6 +270,7 @@ func (s service) DeleteClaGroupGerrits(ctx context.Context, claGroupID string) (
}
}
}
+
return len(gerrits.List), nil
}
@@ -263,6 +278,194 @@ func (s service) DeleteGerrit(ctx context.Context, gerritID string) error {
return s.repo.DeleteGerrit(ctx, gerritID)
}
+// GetUsersOfGroup
+func (s service) GetUsersOfGroup(ctx context.Context, authUser *auth.User, claGroupID, claType string) (*v2Models.GerritGroupResponse, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.GetUsersOfGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("querying for CLA Group gerrits...")
+ g, gerritErr := s.GetClaGroupGerrits(ctx, claGroupID)
+ if gerritErr != nil {
+ log.WithFields(f).WithError(gerritErr).Warnf("unable to locate gerrits associated with CLA Group ID: %s", claGroupID)
+ return nil, gerritErr
+ }
+
+ // Just load the first one...
+ if len(g.List) > 0 {
+ gerritModel := g.List[0]
+ var ldapGroupName string
+ switch claType {
+ case utils.ClaTypeICLA:
+ ldapGroupName = gerritModel.GroupNameIcla
+ case utils.ClaTypeECLA:
+ ldapGroupName = gerritModel.GroupNameCcla
+ default:
+ return nil, &utils.InvalidCLAType{
+ CLAType: claType,
+ }
+ }
+
+ log.WithFields(f).Debugf("querying for members of gerrit group: %s...", ldapGroupName)
+ g, gerritErr := s.lfGroup.GetUsersOfGroup(ctx, authUser, claGroupID, ldapGroupName)
+ if gerritErr != nil {
+ log.WithFields(f).WithError(gerritErr).Warnf("unable to locate gerrits associated with CLA Group ID: %s", claGroupID)
+ return nil, gerritErr
+ }
+ return g, nil
+ }
+
+ return nil, nil
+}
+
+// AddUserToGroup adds the specified user to the group
+func (s service) AddUserToGroup(ctx context.Context, authUser *auth.User, claGroupID, userName, claType string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.AddUserToGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "userName": userName,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("querying for CLA Group gerrits...")
+ g, gerritErr := s.GetClaGroupGerrits(ctx, claGroupID)
+ if gerritErr != nil {
+ log.WithFields(f).WithError(gerritErr).Warnf("unable to locate gerrits associated with CLA Group ID: %s", claGroupID)
+ return gerritErr
+ }
+
+ for _, gerritModel := range g.List {
+ var ldapGroupName string
+ switch claType {
+ case utils.ClaTypeICLA:
+ ldapGroupName = gerritModel.GroupNameIcla
+ case utils.ClaTypeECLA:
+ ldapGroupName = gerritModel.GroupNameCcla
+ default:
+ return &utils.InvalidCLAType{
+ CLAType: claType,
+ }
+ }
+ log.WithFields(f).Debugf("LDAP group name: %s", ldapGroupName)
+ addErr := s.lfGroup.AddUserToGroup(ctx, authUser, claGroupID, ldapGroupName, userName)
+ if addErr != nil {
+ log.WithFields(f).WithError(addErr).Warnf("unable to add user %s to group: %s for CLA Group: %s", userName, ldapGroupName, claGroupID)
+ return gerritErr
+ }
+ log.WithFields(f).Debugf("added user %s to group: %s for CLA Group: %s", userName, ldapGroupName, claGroupID)
+
+ // Log Event
+ }
+
+ return nil
+}
+
+// AddUsersToGroup adds the specified users to the group
+func (s service) AddUsersToGroup(ctx context.Context, authUser *auth.User, claGroupID string, userNameList []string, claType string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.AddUsersToGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "userNameList": strings.Join(userNameList, ","),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ var errorList []error
+ for _, userName := range userNameList {
+ err := s.AddUserToGroup(ctx, authUser, claGroupID, userName, claType)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("encountered an error when adding username: %s to the CLA Group: %s", userName, claGroupID)
+ errorList = append(errorList, err)
+ }
+ }
+
+ if len(errorList) > 0 {
+ log.WithFields(f).Warnf("encountered %d errors when adding %d users to the CLA Group: %s", len(errorList), len(userNameList), claGroupID)
+ return errorList[0]
+ }
+
+ return nil
+}
+
+// RemoveUserFromGroup removes the specified user from the group
+func (s service) RemoveUserFromGroup(ctx context.Context, authUser *auth.User, claGroupID, userName, claType string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.RemoveUserFromGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "userName": userName,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ log.WithFields(f).Debug("querying for CLA Group gerrits...")
+ g, gerritErr := s.GetClaGroupGerrits(ctx, claGroupID)
+ if gerritErr != nil {
+ log.WithFields(f).WithError(gerritErr).Warnf("unable to locate gerrits associated with CLA Group ID: %s", claGroupID)
+ return gerritErr
+ }
+
+ for _, gerritModel := range g.List {
+ var ldapGroupName string
+ switch claType {
+ case utils.ClaTypeICLA:
+ ldapGroupName = gerritModel.GroupNameIcla
+ case utils.ClaTypeECLA:
+ ldapGroupName = gerritModel.GroupNameCcla
+ default:
+ return &utils.InvalidCLAType{
+ CLAType: claType,
+ }
+ }
+ log.WithFields(f).Debugf("LDAP group name: %s", ldapGroupName)
+ addErr := s.lfGroup.RemoveUserFromGroup(ctx, authUser, claGroupID, ldapGroupName, userName)
+ if addErr != nil {
+ log.WithFields(f).WithError(addErr).Warnf("unable to remove user %s from group: %s for CLA Group: %s", userName, ldapGroupName, claGroupID)
+ return gerritErr
+ }
+ log.WithFields(f).Debugf("removed user %s from group: %s for CLA Group: %s", userName, ldapGroupName, claGroupID)
+
+ // Log Event
+ }
+
+ return nil
+}
+
+// RemoveUsersFromGroup removes the specified users from the group
+func (s service) RemoveUsersFromGroup(ctx context.Context, authUser *auth.User, claGroupID string, userNameList []string, claType string) error {
+ f := logrus.Fields{
+ "functionName": "v1.gerrits.service.RemoveUsersFromGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "userNameList": strings.Join(userNameList, ","),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ }
+
+ var errorList []error
+ for _, userName := range userNameList {
+ err := s.RemoveUserFromGroup(ctx, authUser, claGroupID, userName, claType)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("encountered an error when removing username: %s from the CLA Group: %s", userName, claGroupID)
+ errorList = append(errorList, err)
+ }
+ }
+
+ if len(errorList) > 0 {
+ log.WithFields(f).Warnf("encountered %d errors when removing %d users from the CLA Group: %s", len(errorList), len(userNameList), claGroupID)
+ return errorList[0]
+ }
+
+ return nil
+}
+
// convertModel is a helper function to create a GerritRepoList response model
func convertModel(responseModel map[string]GerritRepoInfo, serverInfo *ServerInfo) *models.GerritRepoList {
var gerritRepos []*models.GerritRepo
diff --git a/cla-backend-go/project/helpers.go b/cla-backend-go/project/helpers.go
index 06a63329a..1269ac8c2 100644
--- a/cla-backend-go/project/helpers.go
+++ b/cla-backend-go/project/helpers.go
@@ -92,7 +92,7 @@ func (s service) fillRepoInfo(ctx context.Context, project *models.ClaGroup) {
defer wg.Done()
var err error
var gerritsList *models.GerritList
- gerritsList, err = s.gerritRepo.GetClaGroupGerrits(ctx, project.ProjectID, nil)
+ gerritsList, err = s.gerritRepo.GetClaGroupGerrits(ctx, project.ProjectID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get gerrit instances for cla group ID: %s.", project.ProjectID)
return
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 7f83486d5..2b637d371 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -861,7 +861,7 @@ func (repo *repo) buildCLAGroupModel(ctx context.Context, dbModel DBProjectModel
defer wg.Done()
var err error
var gerritsList *models.GerritList
- gerritsList, err = repo.gerritRepo.GetClaGroupGerrits(ctx, dbModel.ProjectID, nil)
+ gerritsList, err = repo.gerritRepo.GetClaGroupGerrits(ctx, dbModel.ProjectID)
if err != nil {
log.Warnf("buildCLAGroupModel - unable to load Gerrit repositories by project ID: %s, error: %+v",
dbModel.ProjectID, err)
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index 1afbb2642..6cfc1b0d2 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -29,25 +29,30 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
api.SignaturesGetSignedICLADocumentHandler = signatures.GetSignedICLADocumentHandlerFunc(func(params signatures.GetSignedICLADocumentParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+
+ f := logrus.Fields{
+ "functionName": "v1.signatures.handler.SignaturesGetSignedICLADocumentHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": params.ClaGroupID,
+ "userID": params.UserID,
+ }
+
+ log.WithFields(f).Debug("querying for individual signature...")
signatureModel, sigErr := service.GetIndividualSignature(ctx, params.ClaGroupID, params.UserID)
if sigErr != nil {
- msg := fmt.Sprintf("EasyCLA - 500 Internal Server Error - error retrieving signature using ClaGroupID: %s, userID: %s, error: %+v",
+ msg := fmt.Sprintf("error retrieving signature using ClaGroupID: %s, userID: %s, error: %+v",
params.ClaGroupID, params.UserID, sigErr)
- log.Warn(msg)
- return signatures.NewGetSignedICLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: msg,
- })
+ log.WithFields(f).WithError(sigErr).Warn(msg)
+ return signatures.NewGetSignedICLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ToV1ErrorResponse(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, sigErr)))
}
if signatureModel == nil {
- msg := fmt.Sprintf("EasyCLA - 404 Not Found - - error retrieving signature using claGroupID: %s, userID: %s",
+ msg := fmt.Sprintf("error retrieving signature using claGroupID: %s, userID: %s",
params.ClaGroupID, params.UserID)
- log.Warn(msg)
- return signatures.NewGetSignedICLADocumentNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: msg,
- })
+ log.WithFields(f).Warn(msg)
+ return signatures.NewGetSignedICLADocumentNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ToV1ErrorResponse(utils.ErrorResponseNotFound(reqID, msg)))
}
downloadURL := fmt.Sprintf("contract-group/%s/icla/%s/%s.pdf",
@@ -55,13 +60,11 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
log.Debugf("Retrieving PDF from path: %s", downloadURL)
downloadLink, s3Err := utils.GetDownloadLink(downloadURL)
if s3Err != nil {
- msg := fmt.Sprintf("EasyCLA - 500 Internal Server Error - unable to locate PDF from source using ClaGroupID: %s, userID: %s, s3 error: %+v",
+ msg := fmt.Sprintf("unable to locate PDF from source using ClaGroupID: %s, userID: %s, s3 error: %+v",
params.ClaGroupID, params.UserID, s3Err)
log.Warn(msg)
- return signatures.NewGetSignedICLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "500",
- Message: msg,
- })
+ return signatures.NewGetSignedICLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(
+ utils.ToV1ErrorResponse(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, sigErr)))
}
return middleware.ResponderFunc(func(rw http.ResponseWriter, p runtime.Producer) {
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 1110543c1..8d6aacfd9 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -2906,6 +2906,202 @@ paths:
tags:
- gerrits
+ /cla-group/{claGroupID}/project/{projectSFID}/gerrits/icla/user:
+ get:
+ summary: Get Gerrit ICLA Users
+ description: Gets the authorized individual CLA users from a gerrit instance for the CLA Group/Projecct
+ operationId: getGerritICLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gerrit-group-response'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+ put:
+ summary: Add Gerrit ICLA Users
+ description: Adds one or more individual CLA users to the gerrit CLA Group/project
+ operationId: addGerritICLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ - in: body
+ name: add-gerrit-user-input
+ schema:
+ $ref: '#/definitions/add-gerrit-user-input'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+ delete:
+ summary: Remove Gerrit ICLA Users
+ description: Removes one or more individual CLA users from a gerrit instance for the CLA Group/Project
+ operationId: removeGerritICLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ - in: body
+ name: remove-gerrit-user-input
+ schema:
+ $ref: '#/definitions/remove-gerrit-user-input'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+
+ /cla-group/{claGroupID}/project/{projectSFID}/gerrits/ecla/user:
+ get:
+ summary: Get Gerrit ECLA Users
+ description: Gets the authorized employee CLA users from a gerrit instance for the CLA Group/Projecct
+ operationId: getGerritECLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gerrit-group-response'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+ put:
+ summary: Add Gerrit ECLA Users
+ description: Adds one or more employee CLA users to a gerrit instance for the CLA Group/Project
+ operationId: addGerritECLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ - in: body
+ name: add-gerrit-user-input
+ schema:
+ $ref: '#/definitions/add-gerrit-user-input'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+ delete:
+ summary: Remove Gerrit ECLA Users
+ description: Removes one or more employee CLA users from a gerrit instance for the project
+ operationId: removeGerritECLAUser
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-projectSFID"
+ - in: body
+ name: remove-gerrit-user-input
+ schema:
+ $ref: '#/definitions/remove-gerrit-user-input'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gerrits
+
/company/{companyID}:
get:
summary: Get Company By Internal ID
@@ -3906,6 +4102,15 @@ definitions:
add-gerrit-input:
$ref: './common/add-gerrit-input.yaml'
+ gerrit-group-response:
+ $ref: './common/gerrit-group-response.yaml'
+
+ add-gerrit-user-input:
+ $ref: './common/gerrit-user-list.yaml'
+
+ remove-gerrit-user-input:
+ $ref: './common/gerrit-user-list.yaml'
+
gerrit-repo:
$ref: './common/gerrit-repo.yaml'
diff --git a/cla-backend-go/swagger/common/gerrit-group-response.yaml b/cla-backend-go/swagger/common/gerrit-group-response.yaml
new file mode 100644
index 000000000..da4fac5d6
--- /dev/null
+++ b/cla-backend-go/swagger/common/gerrit-group-response.yaml
@@ -0,0 +1,38 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ title:
+ type: string
+ title: gerrit group title
+ description: the gerrit group title
+ nid:
+ type: string
+ 'type':
+ type: string
+ title: gerrit type
+ description: the gerrit type
+ members:
+ type: array
+ items:
+ type: object
+ properties:
+ mail:
+ type: string
+ description: the name member mail address
+ example: 'apache+servicesreleng@mail.linuxfoundation.org'
+ minLength: 2
+ maxLength: 255
+ uid:
+ type: string
+ description: the member id
+ example: '255863'
+ minLength: 2
+ maxLength: 255
+ username:
+ type: string
+ description: the member username
+ example: 'lfservices_releng'
+ minLength: 2
+ maxLength: 255
diff --git a/cla-backend-go/swagger/common/gerrit-user-list.yaml b/cla-backend-go/swagger/common/gerrit-user-list.yaml
new file mode 100644
index 000000000..fb5fd9f11
--- /dev/null
+++ b/cla-backend-go/swagger/common/gerrit-user-list.yaml
@@ -0,0 +1,10 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: array
+items:
+ type: string
+ description: the user's user name
+ example: 'elonmusk'
+ minLength: 1
+ maxLength: 50
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index ca6b930e1..a54444e1d 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -347,3 +347,22 @@ func (e *CLAManagerError) Error() string {
func (e *CLAManagerError) Unwrap() error {
return e.Err
}
+
+// InvalidCLAType is an error model for invalid CLA types, usually the CLA type is one of: utils.{ClaTypeICLA,ClaTypeECLA,ClaTypeCCLA}
+type InvalidCLAType struct {
+ CLAType string
+ Err error
+}
+
+// Error is an error string function for CLA Group not found errors
+func (e *InvalidCLAType) Error() string {
+ if e.Err == nil {
+ return fmt.Sprintf("invalid CLA type: %s", e.CLAType)
+ }
+ return fmt.Sprintf("invalid CLA type: %s, %+v", e.CLAType, e.Err)
+}
+
+// Unwrap method returns its contained error
+func (e *InvalidCLAType) Unwrap() error {
+ return e.Err
+}
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index af3fcdd8d..0f5de712b 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -33,7 +33,7 @@ type ProjectService interface { //nolint
}
// Configure the Gerrit api
-func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectService ProjectService, eventService events.Service, projectsClaGroupsRepo projects_cla_groups.Repository) {
+func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectService ProjectService, eventService events.Service, projectsClaGroupsRepo projects_cla_groups.Repository) { // nolint
api.GerritsDeleteGerritHandler = gerrits.DeleteGerritHandlerFunc(
func(params gerrits.DeleteGerritParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
@@ -197,7 +197,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
}
log.WithFields(f).Debug("querying for gerrits...")
- result, err := v1Service.GetClaGroupGerrits(ctx, params.ClaGroupID, ¶ms.ProjectSFID)
+ result, err := v1Service.GetClaGroupGerrits(ctx, params.ClaGroupID)
if err != nil {
msg := fmt.Sprintf("problem fetching gerrit repositories using CLA Group: %s with project SFID: %s", params.ClaGroupID, params.ProjectSFID)
log.WithFields(f).Warn(msg)
@@ -260,6 +260,197 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
return gerrits.NewGetGerritReposOK().WithXRequestID(reqID).WithPayload(&response)
})
+
+ api.GerritsGetGerritICLAUserHandler = gerrits.GetGerritICLAUserHandlerFunc(func(params gerrits.GetGerritICLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsGetGerritICLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to get gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewGetGerritICLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("getting user list to gerrit...")
+ responseModel, err := v1Service.GetUsersOfGroup(ctx, authUser, params.ClaGroupID, utils.ClaTypeICLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem getting user list of CLA Group %s", params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewGetGerritICLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewGetGerritICLAUserOK().WithXRequestID(reqID).WithPayload(responseModel)
+ })
+
+ api.GerritsGetGerritECLAUserHandler = gerrits.GetGerritECLAUserHandlerFunc(func(params gerrits.GetGerritECLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsGetGerritECLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to get gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewGetGerritECLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("getting user list to gerrit...")
+ responseModel, err := v1Service.GetUsersOfGroup(ctx, authUser, params.ClaGroupID, utils.ClaTypeECLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem getting user list of CLA Group %s", params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewGetGerritECLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewGetGerritECLAUserOK().WithXRequestID(reqID).WithPayload(responseModel)
+ })
+
+ api.GerritsAddGerritICLAUserHandler = gerrits.AddGerritICLAUserHandlerFunc(func(params gerrits.AddGerritICLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsAddGerritICLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ "gerritUsers": strings.Join(params.AddGerritUserInput, ","),
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to add gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewAddGerritICLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("adding user list to gerrit...")
+ err := v1Service.AddUsersToGroup(ctx, authUser, params.ClaGroupID, params.AddGerritUserInput, utils.ClaTypeICLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem adding user list %s to CLA Group %s", strings.Join(params.AddGerritUserInput, ","), params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewAddGerritICLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewAddGerritICLAUserOK().WithXRequestID(reqID)
+ })
+
+ api.GerritsRemoveGerritICLAUserHandler = gerrits.RemoveGerritICLAUserHandlerFunc(func(params gerrits.RemoveGerritICLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsRemoveGerritICLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ "gerritUsers": strings.Join(params.RemoveGerritUserInput, ","),
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to remove gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewRemoveGerritICLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("removing user list from gerrit...")
+ err := v1Service.RemoveUsersFromGroup(ctx, authUser, params.ClaGroupID, params.RemoveGerritUserInput, utils.ClaTypeICLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem removing user list %s to CLA Group %s", strings.Join(params.RemoveGerritUserInput, ","), params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewRemoveGerritICLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewRemoveGerritICLAUserOK().WithXRequestID(reqID)
+ })
+
+ api.GerritsAddGerritECLAUserHandler = gerrits.AddGerritECLAUserHandlerFunc(func(params gerrits.AddGerritECLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsAddGerritECLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ "gerritUsers": strings.Join(params.AddGerritUserInput, ","),
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to add gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewAddGerritECLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("adding user list to gerrit...")
+ err := v1Service.AddUsersToGroup(ctx, authUser, params.ClaGroupID, params.AddGerritUserInput, utils.ClaTypeECLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem adding user list %s to CLA Group %s", strings.Join(params.AddGerritUserInput, ","), params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewAddGerritECLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewAddGerritECLAUserOK().WithXRequestID(reqID)
+ })
+
+ api.GerritsRemoveGerritECLAUserHandler = gerrits.RemoveGerritECLAUserHandlerFunc(func(params gerrits.RemoveGerritECLAUserParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.gerrits.handlers.GerritsRemoveGerritECLAUserHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
+ "claGroupID": params.ClaGroupID,
+ "projectSFID": params.ProjectSFID,
+ "gerritUsers": strings.Join(params.RemoveGerritUserInput, ","),
+ }
+
+ // verify user have access to the project
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to remove gerrit users with Project scope of %s", authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Warn(msg)
+ return gerrits.NewRemoveGerritECLAUserForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("removing user list from gerrit...")
+ err := v1Service.RemoveUsersFromGroup(ctx, authUser, params.ClaGroupID, params.RemoveGerritUserInput, utils.ClaTypeECLA)
+ if err != nil {
+ msg := fmt.Sprintf("problem removing user list %s to CLA Group %s", strings.Join(params.RemoveGerritUserInput, ","), params.ClaGroupID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gerrits.NewRemoveGerritECLAUserInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gerrits.NewRemoveGerritECLAUserOK().WithXRequestID(reqID)
+ })
+
}
type codedResponse interface {
From ddb717fdd8f3ac11faf28cd26df6b8f1ec2597c5 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 25 Mar 2021 01:28:34 +0300
Subject: [PATCH 0184/1276] [#2793] Feature/Invalidate Gerrit Contributors
(#2813)
- Invalidate gerrit contributors based on email|domain removals from approval lists
Signed-off-by: wanyaland
---
.../cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 18 ++--
cla-backend-go/signatures/models.go | 1 +
cla-backend-go/signatures/repository.go | 99 ++++++++++++++++++-
4 files changed, 110 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index 01ed9b077..8dbf6d764 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -119,7 +119,7 @@ func init() {
projectClaGroupRepo,
})
- signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo, gerritService)
usersService := users.NewService(usersRepo, eventsService)
companyService := company.NewService(companyRepo, configFile.CorporateConsoleV1URL, userRepo, usersService)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 4356536f7..a7d5a4bf4 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -251,8 +251,16 @@ func server(localMode bool) http.Handler {
projectClaGroupRepo,
})
+ gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{
+ LfBaseURL: configFile.LFGroup.ClientURL,
+ ClientID: configFile.LFGroup.ClientID,
+ ClientSecret: configFile.LFGroup.ClientSecret,
+ RefreshToken: configFile.LFGroup.RefreshToken,
+ EventsService: eventsService,
+ })
+
// Signature repository handler
- signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo, gerritService)
// Initialize the external platform services - these are external APIs that
// we download the swagger specification, generate the models, and have
@@ -284,13 +292,7 @@ func server(localMode bool) http.Handler {
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, eventsService, autoEnableService)
- gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{
- LfBaseURL: configFile.LFGroup.ClientURL,
- ClientID: configFile.LFGroup.ClientID,
- ClientSecret: configFile.LFGroup.ClientSecret,
- RefreshToken: configFile.LFGroup.RefreshToken,
- EventsService: eventsService,
- })
+
v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, projectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
sessionStore, err := dynastore.New(dynastore.Path("/"), dynastore.HTTPOnly(), dynastore.TableName(configFile.SessionStoreTableName), dynastore.DynamoDB(dynamodb.New(awsSession)))
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index dcc2b2e2a..aad7ee21e 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -29,4 +29,5 @@ type ApprovalList struct {
GitHubUsernameApprovals []string
EmailApprovals []string
GHUsernames []string
+ GerritICLAECLAs []string
}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index edf7a89b5..1e0e5e014 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -11,6 +11,7 @@ import (
"strings"
"sync"
+ "github.com/LF-Engineering/lfx-kit/auth"
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/users"
@@ -21,6 +22,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gerrits"
"github.com/communitybridge/easycla/cla-backend-go/github"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
@@ -95,11 +97,12 @@ type repository struct {
eventsService events.Service
repositoriesRepo repositories.Repository
ghOrgRepo github_organizations.Repository
+ gerritService gerrits.Service
signatureTableName string
}
// NewRepository creates a new instance of the whitelist service
-func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.Repository) SignatureRepository {
+func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.Repository, gerritService gerrits.Service) SignatureRepository {
return repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
@@ -108,6 +111,7 @@ func NewRepository(awsSession *session.Session, stage string, companyRepo compan
eventsService: eventsService,
repositoriesRepo: repositoriesRepo,
ghOrgRepo: ghOrgRepo,
+ gerritService: gerritService,
signatureTableName: fmt.Sprintf("cla-%s-signatures", stage),
}
}
@@ -2010,6 +2014,35 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
CompanyID: companyID,
}
+ authUser := auth.User{
+ Email: claManager.LfEmail,
+ UserName: claManager.LfUsername,
+ }
+
+ log.WithFields(f).Debug("aggregating ICLA and ECLA gerrit users...")
+ gerritIclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeICLA)
+
+ if err != nil {
+ msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectID, utils.ClaTypeICLA)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ gerritEclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeECLA)
+
+ if err != nil {
+ msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectID, utils.ClaTypeECLA)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Keep track of gerrit users under a give CLA Group
+ var gerritICLAECLAs []string
+
+ for _, member := range append(gerritEclaUsers.Members, gerritIclaUsers.Members...) {
+ gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ }
+
// If we have an add or remove email list...we need to run an update for this column
if params.AddEmailApprovalList != nil || params.RemoveEmailApprovalList != nil {
columnName := "email_whitelist"
@@ -2065,6 +2098,26 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
repo.eventsService.LogEventWithContext(ctx, eventArgs)
+
+ //update gerrit permissions
+ gerritUser, err := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
+ if err != nil || gerritUser == nil {
+ msg := fmt.Sprintf("unable to get gerrit user by email : %s ", email)
+ log.WithFields(f).Warn(msg)
+ return
+ }
+ iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, gerritUser.LfUsername, utils.ClaTypeICLA)
+ if iclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", gerritUser.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return
+ }
+ eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, gerritUser.LfUsername, utils.ClaTypeECLA)
+ if eclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", gerritUser.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return
+ }
}(email)
}
wg.Wait()
@@ -2096,6 +2149,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Criteria = utils.EmailDomainCriteria
approvalList.ApprovalList = params.RemoveDomainApprovalList
approvalList.Action = utils.RemoveApprovals
+ approvalList.GerritICLAECLAs = gerritICLAECLAs
invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
if invalidateErr != nil {
msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
@@ -2370,6 +2424,30 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
return nil
}
+// getGerritUsersByEmail searches gerrit instances for users with given email
+func (repo repository) getGerritUserByEmail(ctx context.Context, email string, gerritICLAECLAs []string) (*models.User, error) {
+ f := logrus.Fields{
+ "functionName": "getGerritUserByEmailDomain",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "emai": email,
+ }
+
+ log.WithFields(f).Debugf("checking gerrit user for email: %s ", email)
+ if email != "" {
+ claUser, err := repo.usersRepo.GetUserByEmail(email)
+ if err != nil {
+ msg := fmt.Sprintf("unable to get easyclauser by email: %s ", email)
+ log.WithFields(f).Warn(msg)
+ return nil, err
+ }
+ if utils.StringInSlice(claUser.LfUsername, gerritICLAECLAs) {
+ return claUser, nil
+ }
+ }
+
+ return nil, nil
+}
+
// verify UserApprovals checks user
func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) error {
f := logrus.Fields{
@@ -2384,6 +2462,11 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
return err
}
+ authUser := auth.User{
+ Email: claManager.LfEmail,
+ UserName: claManager.LfUsername,
+ }
+
if approvalList.Criteria == utils.EmailDomainCriteria {
// Handle Domains
if utils.StringInSlice(getBestEmail(user), approvalList.DomainApprovals) {
@@ -2395,6 +2478,20 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
return err
}
+
+ log.WithFields(f).Debugf("removing gerrit user:%s from claGroup: %s ...", user.LfUsername, approvalList.ClaGroupID)
+ iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeICLA)
+ if iclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return iclaErr
+ }
+ eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
+ if eclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return eclaErr
+ }
}
}
} else if approvalList.Criteria == utils.GitHubOrgCriteria {
From 73956bfb1faf7c028c07384a55f5ed42b52df2e1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 24 Mar 2021 16:30:02 -0700
Subject: [PATCH 0185/1276] Resolved [#2814] CCLA Signature Signed On Date Not
Showing (#2815)
- Added/updated signedOn date logic to use docusign date if available,
if missing, use the created date (generally close to the same value)
Signed-off-by: David Deal
---
.github/ISSUE_TEMPLATE/bug_report.md | 8 ++++++++
cla-backend-go/signatures/converters.go | 9 ++++++++-
cla-backend-go/utils/utils.go | 19 +++++++++++++++++++
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 3bbf4d3cd..8f020ed2c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -32,12 +32,20 @@ If applicable, add screenshots to help explain your problem.
Please complete the following information:
* Environment:
+ - [ ] ALL
- [ ] DEV
- [ ] STAGING
- [ ] PROD
* Browser:
- [ ] Chrome/Brave
- [ ] Firefox
+ - [ ] Opera
+ - [ ] Vivaldi
+ - [ ] LibreWolf
+ - [ ] SRware Iron
+ - [ ] Dissenter
+ - [ ] Slimjet
+ - [ ] Midori
- [ ] Edge
- [ ] Lynx
* Version: v1.0.XX
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index 3639a6b13..21821850e 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -56,6 +56,13 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
claType = utils.ClaTypeICLA
}
+ // Use the signedOn field if possible, for older signatures that are missing it, use the date created value as the default/fallback
+ signedOn := dbSignature.DateCreated
+ if dbSignature.SignedOn != "" {
+ signedOn = dbSignature.SignedOn
+ }
+ signedOn = utils.FormatTimeString(signedOn)
+
sig := &models.Signature{
SignatureID: dbSignature.SignatureID,
ClaType: claType,
@@ -81,7 +88,7 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
UserName: dbSignature.UserName,
UserLFID: dbSignature.UserLFUsername,
UserGHID: dbSignature.UserGithubUsername,
- SignedOn: dbSignature.SignedOn,
+ SignedOn: signedOn,
SignatoryName: dbSignature.SignatoryName,
UserDocusignName: dbSignature.UserDocusignName,
UserDocusignDateSigned: dbSignature.UserDocusignDateSigned,
diff --git a/cla-backend-go/utils/utils.go b/cla-backend-go/utils/utils.go
index b4f84a098..22a3e8e0c 100644
--- a/cla-backend-go/utils/utils.go
+++ b/cla-backend-go/utils/utils.go
@@ -12,6 +12,9 @@ import (
"time"
"unicode/utf8"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/sirupsen/logrus"
+
"github.com/gofrs/uuid"
"github.com/aws/aws-sdk-go/aws"
@@ -40,6 +43,22 @@ func TimeToString(t time.Time) string {
return t.UTC().Format(time.RFC3339)
}
+// FormatTimeString converts the time string into the "standard" RFC3339 format
+func FormatTimeString(timeStr string) string {
+ f := logrus.Fields{
+ "functionName": "utils.utils.FormatTimeString",
+ "timeStr": timeStr,
+ }
+
+ t, err := ParseDateTime(timeStr)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to convert the time string: %s into a standard form.", timeStr)
+ return timeStr
+ }
+
+ return t.UTC().Format(time.RFC3339)
+}
+
// CurrentTime returns the current UTC time and current Time in the RFC3339 format
func CurrentTime() (time.Time, string) {
t := time.Now().UTC()
From a55cf95d2b80e830440a43b57d2d8b8332925c2c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 24 Mar 2021 16:44:35 -0700
Subject: [PATCH 0186/1276] Bump rsa from 4.0 to 4.1 in /cla-backend (#2812)
Bumps [rsa](https://github.com/sybrenstuvel/python-rsa) from 4.0 to 4.1.
- [Release notes](https://github.com/sybrenstuvel/python-rsa/releases)
- [Changelog](https://github.com/sybrenstuvel/python-rsa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sybrenstuvel/python-rsa/compare/version-4.0...version-4.1)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index eec1d6348..82e845d8c 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -46,7 +46,7 @@ python-dateutil==2.8.0
python-jose==3.0.1
requests==2.22.0
requests-oauthlib==1.2.0
-rsa==4.0
+rsa==4.1
s3transfer==0.2.1
sentinels==1.0.0
six==1.13.0
From a9b1caa53d9a3527edb24329a635b23153084f79 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 24 Mar 2021 17:44:25 -0700
Subject: [PATCH 0187/1276] Resolved Foundation Query Issues (#2816)
- When searching by foundation, we need to first consult the Project CLA
Group Mapping table entries for the CLA Groups - then load each CLA
group. Previous logic was only looking at the CLA Group table foundation
SFID (v1) value.
Signed-off-by: David Deal
---
cla-backend-go/project/repository.go | 5 ++-
cla-backend-go/project/service.go | 3 +-
cla-backend-go/v2/cla_groups/service.go | 52 +++++++++++++------------
3 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 2b637d371..24c9d31dd 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -306,8 +306,9 @@ func (repo *repo) GetClaGroupsByFoundationSFID(ctx context.Context, foundationSF
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"foundationSFID": foundationSFID,
"loadRepoDetails": loadRepoDetails,
- "tableName": repo.claGroupTable}
- log.WithFields(f).Debugf("loading project by foundation SFID")
+ "tableName": repo.claGroupTable,
+ }
+ log.WithFields(f).Debugf("loading CLA Group by foundation SFID - using foundation_sfid field...")
// This is the key we want to match
condition := expression.Key("foundation_sfid").Equal(expression.Value(foundationSFID))
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index 3819eae79..570e72639 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -341,7 +341,7 @@ func (s service) SignedAtFoundationLevel(ctx context.Context, foundationSFID str
if pcgErr != nil {
return false, pcgErr
}
- log.WithFields(f).Debugf("loaded %d CLA Group entries", len(entries))
+ log.WithFields(f).Debugf("loaded %d CLA Group entries signed at foundation level...", len(entries))
// Check for number of claGroups for foundation
foundationLevelCLAGroup := false
@@ -352,6 +352,7 @@ func (s service) SignedAtFoundationLevel(ctx context.Context, foundationSFID str
}
}
+ log.WithFields(f).Debugf("returning %t for signed at foundation level for: %s", foundationLevelCLAGroup, foundationSFID)
return foundationLevelCLAGroup, nil
}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index cfd2b74e5..d7891a9c1 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -13,8 +13,6 @@ import (
"github.com/aws/aws-sdk-go/aws"
- "golang.org/x/sync/errgroup"
-
"github.com/LF-Engineering/lfx-kit/auth"
v1ClaManager "github.com/communitybridge/easycla/cla-backend-go/cla_manager"
"github.com/communitybridge/easycla/cla-backend-go/events"
@@ -444,17 +442,22 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
} else if sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup {
log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
- projectCLAGroups, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
+ projectCLAGroupMappings, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
if lookupErr != nil {
log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
return nil, &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
}
- log.WithFields(f).Debugf("discovered %d projects based on foundation SFID...", len(projectCLAGroups))
+ log.WithFields(f).Debugf("discovered %d projects based on foundation SFID...", len(projectCLAGroupMappings))
claGroupsMap := map[string]bool{}
+ type CLAGroupResult struct {
+ claGroupModel *v1Models.ClaGroup
+ Error error
+ }
+ claGroupResultChannel := make(chan *CLAGroupResult, len(projectCLAGroupMappings))
+
// Load these CLA Group records in parallel
- var eg errgroup.Group
- for _, projectCLAGroup := range projectCLAGroups {
+ for _, projectCLAGroup := range projectCLAGroupMappings {
// ensure that following goroutine gets a copy of projectSFID
projectCLAGroupClaGroupID := projectCLAGroup.ClaGroupID
// No need to re-process the same CLA group
@@ -465,34 +468,35 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
// Add entry into our map - so we know not to re-process this CLA Group
claGroupsMap[projectCLAGroupClaGroupID] = true
- // Invoke the go routine - any errors will be handled below
- eg.Go(func() error {
+ // Load each CLA Group - save results to our channel
+ go func(ctx context.Context, projectCLAGroupClaGroupID string) {
log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroupClaGroupID)
claGroupModel, claGroupLookupErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroupClaGroupID)
if claGroupLookupErr != nil {
log.WithFields(f).Warnf("problem locating project by id: %s, error: %+v", projectCLAGroupClaGroupID, claGroupLookupErr)
- return &utils.SFProjectNotFound{ProjectSFID: projectCLAGroupClaGroupID, Err: claGroupLookupErr}
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: nil,
+ Error: &utils.SFProjectNotFound{ProjectSFID: projectCLAGroupClaGroupID, Err: claGroupLookupErr},
+ }
}
- v1ClaGroups.Projects = append(v1ClaGroups.Projects, *claGroupModel)
- return nil
- })
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: claGroupModel,
+ Error: nil,
+ }
+ }(ctx, projectCLAGroupClaGroupID)
}
- // Wait for the go routines to finish
+ // Wait for the go routines to finish and load up the results
log.WithFields(f).Debug("waiting for CLA Groups to load...")
- if loadErr := eg.Wait(); loadErr != nil {
- return nil, loadErr
- }
-
- v1CLAGroupsData, v1ClaGroupErr := s.v1ProjectService.GetClaGroupsByFoundationSFID(ctx, projectOrFoundationSFID, false)
- if v1ClaGroupErr != nil {
- log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", v1ClaGroupErr)
- return nil, &utils.CLAGroupNotFound{CLAGroupID: projectOrFoundationSFID, Err: v1ClaGroupErr}
+ for range projectCLAGroupMappings {
+ response := <-claGroupResultChannel
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warnf("unable to load CLA Group")
+ return nil, response.Error
+ }
+ v1ClaGroups.Projects = append(v1ClaGroups.Projects, *response.claGroupModel)
}
-
- v1ClaGroups = v1CLAGroupsData
-
} else {
msg := fmt.Sprintf("unsupported foundation/project SFID type: %s", sfProjectModelDetails.ProjectType)
log.WithFields(f).Warn(msg)
From b7df8276d1f3e588acd6799cb5b74308150d20a3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Mar 2021 09:25:51 -0700
Subject: [PATCH 0188/1276] Resolved #2816 Issue with Foundation Query and
Unique CLA Groups (#2819)
- Resolved issue of looping over unique CLA Groups when querying for
foundations. Bug caused timeout/channel blocking due to a mismatch of
channels vs looping iterations.
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/helpers.go | 24 ++++++++++++++++++++++++
cla-backend-go/v2/cla_groups/service.go | 19 ++++++-------------
2 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 1b96b6234..57edc2ded 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -680,3 +680,27 @@ func isFoundationIDInList(foundationSFID string, projectsSFIDs []string) bool {
}
return false
}
+
+// getUniqueCLAGroupIDs logic to extract the unique
+func getUniqueCLAGroupIDs(projectCLAGroupMappings []*projects_cla_groups.ProjectClaGroup) []string {
+ // to ensure we get the distinct count
+ claGroupsMap := map[string]bool{}
+ for _, projectCLAGroupModel := range projectCLAGroupMappings {
+ // ensure that following goroutine gets a copy of projectSFID
+ projectCLAGroupClaGroupID := projectCLAGroupModel.ClaGroupID
+ // No need to re-process the same CLA group
+ if _, ok := claGroupsMap[projectCLAGroupClaGroupID]; ok {
+ continue
+ }
+
+ // Add entry into our map - so we know not to re-process this CLA Group
+ claGroupsMap[projectCLAGroupClaGroupID] = true
+ }
+
+ keys := make([]string, 0, len(claGroupsMap))
+ for k := range claGroupsMap {
+ keys = append(keys, k)
+ }
+
+ return keys
+}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index d7891a9c1..b9d5ebaf8 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -449,24 +449,17 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
}
log.WithFields(f).Debugf("discovered %d projects based on foundation SFID...", len(projectCLAGroupMappings))
- claGroupsMap := map[string]bool{}
+ // Determine how many CLA Groups we have - we could have many and possibly return duplicates, we use this loop
+ uniqueCLAGroupList := getUniqueCLAGroupIDs(projectCLAGroupMappings)
+
type CLAGroupResult struct {
claGroupModel *v1Models.ClaGroup
Error error
}
- claGroupResultChannel := make(chan *CLAGroupResult, len(projectCLAGroupMappings))
+ claGroupResultChannel := make(chan *CLAGroupResult, len(uniqueCLAGroupList))
// Load these CLA Group records in parallel
- for _, projectCLAGroup := range projectCLAGroupMappings {
- // ensure that following goroutine gets a copy of projectSFID
- projectCLAGroupClaGroupID := projectCLAGroup.ClaGroupID
- // No need to re-process the same CLA group
- if _, ok := claGroupsMap[projectCLAGroupClaGroupID]; ok {
- continue
- }
-
- // Add entry into our map - so we know not to re-process this CLA Group
- claGroupsMap[projectCLAGroupClaGroupID] = true
+ for _, projectCLAGroupClaGroupID := range uniqueCLAGroupList {
// Load each CLA Group - save results to our channel
go func(ctx context.Context, projectCLAGroupClaGroupID string) {
@@ -489,7 +482,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
// Wait for the go routines to finish and load up the results
log.WithFields(f).Debug("waiting for CLA Groups to load...")
- for range projectCLAGroupMappings {
+ for range uniqueCLAGroupList {
response := <-claGroupResultChannel
if response.Error != nil {
log.WithFields(f).WithError(response.Error).Warnf("unable to load CLA Group")
From 2a2fccf3327a0ec3789b28a78e10405b18b2973b Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 26 Mar 2021 00:18:42 +0300
Subject: [PATCH 0189/1276] [#LFX-3147] Bug/ Update Approval List (#2820)
- Resolved error when getting gerrit users for approval list update
Signed-off-by: wanyaland
---
cla-backend-go/signatures/repository.go | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 1e0e5e014..8977e0c1a 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2019,6 +2019,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
UserName: claManager.LfUsername,
}
+ // Keep track of gerrit users under a give CLA Group
+ var gerritICLAECLAs []string
+
log.WithFields(f).Debug("aggregating ICLA and ECLA gerrit users...")
gerritIclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeICLA)
@@ -2028,6 +2031,12 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return nil, errors.New(msg)
}
+ if gerritIclaUsers != nil {
+ for _, member := range gerritIclaUsers.Members {
+ gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ }
+ }
+
gerritEclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeECLA)
if err != nil {
@@ -2036,11 +2045,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return nil, errors.New(msg)
}
- // Keep track of gerrit users under a give CLA Group
- var gerritICLAECLAs []string
-
- for _, member := range append(gerritEclaUsers.Members, gerritIclaUsers.Members...) {
- gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ if gerritEclaUsers != nil {
+ for _, member := range gerritEclaUsers.Members {
+ gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ }
}
// If we have an add or remove email list...we need to run an update for this column
From da4768e8e0d2a74248c7d46b461ca7a2e0f915fa Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Mar 2021 16:51:00 -0700
Subject: [PATCH 0190/1276] Resolved CLA Group Event Query NextKey Issue
(#2821)
- Updated next key logic, added support for CLA Group Next Key
Signed-off-by: David Deal
---
cla-backend-go/events/repository.go | 287 +++++++++++++++---------
cla-backend-go/swagger/cla.v2.yaml | 8 +-
cla-backend/cla/models/dynamo_models.py | 11 +
3 files changed, 197 insertions(+), 109 deletions(-)
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 544d3f14b..5d8cec251 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -71,6 +71,7 @@ type Repository interface {
type repository struct {
stage string
dynamoDBClient *dynamodb.DynamoDB
+ eventsTable string
}
// NewRepository creates a new instance of the event repository
@@ -78,6 +79,7 @@ func NewRepository(awsSession *session.Session, stage string) Repository {
return &repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
+ eventsTable: fmt.Sprintf("cla-%s-events", stage),
}
}
@@ -89,7 +91,7 @@ func toDateFormat(t time.Time) string {
// Create event will create event in database.
func (repo *repository) CreateEvent(event *models.Event) error {
f := logrus.Fields{
- "functionName": "events.repository.CreateEvent",
+ "functionName": "v1.events.repository.CreateEvent",
}
if event.UserID == "" {
@@ -107,7 +109,7 @@ func (repo *repository) CreateEvent(event *models.Event) error {
currentTime, currentTimeString := utils.CurrentTime()
input := &dynamodb.PutItemInput{
Item: map[string]*dynamodb.AttributeValue{},
- TableName: aws.String(fmt.Sprintf("cla-%s-events", repo.stage)),
+ TableName: aws.String(repo.eventsTable),
}
eventDateAndContainsPII := fmt.Sprintf("%s#%t", toDateFormat(currentTime), event.ContainsPII)
@@ -136,6 +138,8 @@ func (repo *repository) CreateEvent(event *models.Event) error {
addAttribute(input.Item, "event_parent_project_name", event.EventParentProjectName)
addAttribute(input.Item, "event_data", event.EventData)
+ // For filtering/searching
+ addAttribute(input.Item, "event_data_lower", strings.ToLower(event.EventData))
addAttribute(input.Item, "event_summary", event.EventSummary)
addAttribute(input.Item, "event_time", currentTimeString)
@@ -212,7 +216,7 @@ func createSearchEventFilter(pk string, sk string, params *eventOps.SearchEvents
filter = addConditionToFilter(filter, filterExpression, &filterAdded)
}
if params.SearchTerm != nil {
- filterExpression := expression.Name("event_data").Contains(*params.SearchTerm)
+ filterExpression := expression.Name("event_data_lower").Contains(strings.ToLower(*params.SearchTerm))
filter = addConditionToFilter(filter, filterExpression, &filterAdded)
}
if filterAdded {
@@ -240,14 +244,17 @@ func addTimeExpression(keyCond expression.KeyConditionBuilder, params *eventOps.
// SearchEvents returns list of events matching with filter criteria.
func (repo *repository) SearchEvents(params *eventOps.SearchEventsParams, pageSize int64) (*models.EventList, error) {
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.SearchEvents",
+ "pageSize": pageSize,
+ }
+
if params.ProjectID == nil {
return nil, errors.New("invalid request. projectID is compulsory")
}
var condition expression.KeyConditionBuilder
var indexName, pk, sk string
builder := expression.NewBuilder().WithProjection(buildProjection())
- // The table we're interested in
- tableName := fmt.Sprintf("cla-%s-events", repo.stage)
switch {
case params.ProjectID != nil:
@@ -276,7 +283,7 @@ func (repo *repository) SearchEvents(params *eventOps.SearchEventsParams, pageSi
KeyConditionExpression: expr.KeyCondition(),
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
- TableName: aws.String(tableName),
+ TableName: aws.String(repo.eventsTable),
IndexName: aws.String(indexName),
Limit: aws.Int64(pageSize), // The maximum number of items to evaluate (not necessarily the number of matching items)
}
@@ -285,71 +292,107 @@ func (repo *repository) SearchEvents(params *eventOps.SearchEventsParams, pageSi
}
if params.NextKey != nil {
- log.Debugf("Received a nextKey, value: %s", *params.NextKey)
- queryInput.ExclusiveStartKey, err = fromString(*params.NextKey)
+ queryInput.ExclusiveStartKey, err = decodeNextKey(*params.NextKey)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem decoding next key value")
return nil, err
}
+ log.WithFields(f).Debugf("received a nextKey, value: %s - decoded: %+v", *params.NextKey, queryInput.ExclusiveStartKey)
}
- var lastEvaluatedKey string
events := make([]*models.Event, 0)
+ var results *dynamodb.QueryOutput
- for ok := true; ok; ok = lastEvaluatedKey != "" {
- results, errQuery := repo.dynamoDBClient.Query(queryInput)
+ for {
+ // Perform the query...
+ var errQuery error
+ results, errQuery = repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
- log.Warnf("error retrieving events. error = %s", errQuery.Error())
+ log.WithFields(f).WithError(errQuery).Warnf("error retrieving events")
return nil, errQuery
}
+ // Build the result models
eventsList, modelErr := buildEventListModels(results)
if modelErr != nil {
+ log.WithFields(f).WithError(modelErr).Warn("error convert event list models")
return nil, modelErr
}
+ // Trim to how many the caller asked for - just in case we go over
events = append(events, eventsList...)
- if len(results.LastEvaluatedKey) != 0 {
- queryInput.ExclusiveStartKey = results.LastEvaluatedKey
+ if int64(len(events)) > pageSize {
+ events = events[:pageSize]
}
- lastEvaluatedKey, err = toString(results.LastEvaluatedKey)
- if err != nil {
- return nil, err
+ log.WithFields(f).Debugf("loaded %d events", len(events))
+
+ // We have more records if last evaluated key has a value
+ log.WithFields(f).Debugf("last evaluated key %+v", results.LastEvaluatedKey)
+ if len(results.LastEvaluatedKey) > 0 {
+ queryInput.ExclusiveStartKey = results.LastEvaluatedKey
+ } else {
+ break
}
+
if int64(len(events)) >= pageSize {
break
}
}
- return &models.EventList{
- Events: events,
- NextKey: lastEvaluatedKey,
- }, nil
+ response := &models.EventList{
+ Events: events,
+ }
+
+ log.WithFields(f).Debugf("returning %d events - last key: %+v", len(events), results.LastEvaluatedKey)
+ if len(results.LastEvaluatedKey) > 0 {
+ log.WithFields(f).Debug("building next key...")
+ encodedString, err := buildNextKey(indexName, events[len(events)-1])
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to build nextKey")
+ }
+ response.NextKey = encodedString
+ log.WithFields(f).Debugf("lastEvaluatedKey encoded is: %s", encodedString)
+ }
+
+ return response, nil
}
// queryEventsTable queries events table on index
func (repo *repository) queryEventsTable(indexName string, condition expression.KeyConditionBuilder, filter *expression.ConditionBuilder, nextKey *string, pageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.queryEventsTable",
+ "functionName": "v1.events.repository.queryEventsTable",
"indexName": indexName,
- "nextKey": aws.StringValue(nextKey),
- "pageSize": aws.Int64Value(pageSize),
- "all": all,
- "searchTerm": aws.StringValue(searchTerm),
+ //"nextKey": aws.StringValue(nextKey),
+ "pageSize": aws.Int64Value(pageSize),
+ "all": all,
+ "searchTerm": aws.StringValue(searchTerm),
}
- log.WithFields(f).Debug("querying events table")
- builder := expression.NewBuilder() // .WithProjection(buildProjection())
+ log.WithFields(f).Debug("querying events table...")
+ builder := expression.NewBuilder().WithKeyCondition(condition)
- // The table we're interested in
- tableName := fmt.Sprintf("cla-%s-events", repo.stage)
+ // If we have a search term...
+ if searchTerm != nil {
+ log.WithFields(f).Debugf("adding searchTerm: %s", *searchTerm)
+ eventLowerFilter := expression.Name("event_data_lower").Contains(strings.ToLower(*searchTerm))
+ if filter == nil {
+ // Create a new filter
+ filter = &eventLowerFilter
+ } else {
+ // Add to existing filter
+ filter.And(eventLowerFilter)
+ }
+ }
- builder = builder.WithKeyCondition(condition)
+ // Add the filter to the builder
if filter != nil {
builder = builder.WithFilter(*filter)
}
+
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem building events query")
return nil, err
}
@@ -359,9 +402,9 @@ func (repo *repository) queryEventsTable(indexName string, condition expression.
ExpressionAttributeValues: expr.Values(),
KeyConditionExpression: expr.KeyCondition(),
ProjectionExpression: expr.Projection(),
- TableName: aws.String(tableName),
+ TableName: aws.String(repo.eventsTable),
IndexName: aws.String(indexName),
- ScanIndexForward: aws.Bool(false),
+ ScanIndexForward: aws.Bool(false), // Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order.
}
if filter != nil {
@@ -369,91 +412,85 @@ func (repo *repository) queryEventsTable(indexName string, condition expression.
}
if all {
- pageSize = aws.Int64(HugePageSize)
+ queryInput.Limit = aws.Int64(HugePageSize)
} else {
if pageSize == nil {
- pageSize = aws.Int64(DefaultPageSize)
+ queryInput.Limit = aws.Int64(DefaultPageSize)
+ } else {
+ if *pageSize > HugePageSize {
+ queryInput.Limit = aws.Int64(HugePageSize)
+ }
+ queryInput.Limit = pageSize
}
}
+ maxResults := *queryInput.Limit
- if searchTerm != nil {
- // since we are filtering data in client side, we should use large pageSize to avoid recursive query
- queryInput.Limit = aws.Int64(HugePageSize)
- } else {
- queryInput.Limit = aws.Int64(*pageSize + 1)
- }
-
- if nextKey != nil && !all {
- // log.Debugf("Received a nextKey, value: %s", *nextKey)
- queryInput.ExclusiveStartKey, err = fromString(*nextKey)
+ // If we have the next key, set the exclusive start key value
+ if nextKey != nil {
+ queryInput.ExclusiveStartKey, err = decodeNextKey(*nextKey)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem decoding next key value")
return nil, err
}
+ log.WithFields(f).Debugf("received a nextKey, value: %s - decoded: %+v", *nextKey, queryInput.ExclusiveStartKey)
}
- // log.WithField("queryInput", *queryInput).Debug("query")
- var lastEvaluatedKey string
events := make([]*models.Event, 0)
+ var results *dynamodb.QueryOutput
- if searchTerm != nil {
- searchTerm = aws.String(strings.ToLower(*searchTerm))
- }
-
- for ok := true; ok; ok = lastEvaluatedKey != "" {
- results, errQuery := repo.dynamoDBClient.Query(queryInput)
+ for {
+ // Perform the query...
+ var errQuery error
+ results, errQuery = repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
- log.WithFields(f).WithError(errQuery).Warnf("error retrieving events. error = %s", errQuery.Error())
+ log.WithFields(f).WithError(errQuery).Warn("error retrieving events")
return nil, errQuery
}
+ // Build the result models
eventsList, modelErr := buildEventListModels(results)
if modelErr != nil {
+ log.WithFields(f).WithError(modelErr).Warn("error convert event list models")
return nil, modelErr
}
- if searchTerm != nil {
- for _, event := range eventsList {
- if !all {
- if int64(len(events)) >= (*pageSize + 1) {
- break
- }
- }
- if strings.Contains(strings.ToLower(event.EventData), *searchTerm) {
- events = append(events, event)
- }
- }
- } else {
- events = append(events, eventsList...)
+
+ // Trim to how many the caller asked for - just in case we go over
+ events = append(events, eventsList...)
+ if int64(len(events)) > maxResults {
+ events = events[:maxResults]
}
+ log.WithFields(f).Debugf("loaded %d events", len(events))
- if len(results.LastEvaluatedKey) != 0 {
+ // We have more records if last evaluated key has a value
+ log.WithFields(f).Debugf("last evaluated key %+v", results.LastEvaluatedKey)
+ if len(results.LastEvaluatedKey) > 0 {
queryInput.ExclusiveStartKey = results.LastEvaluatedKey
} else {
break
}
- if !all {
- if int64(len(events)) >= (*pageSize + 1) {
- break
- }
+ if int64(len(events)) >= maxResults {
+ break
}
}
- if !all {
- if int64(len(events)) > *pageSize {
- events = events[0:*pageSize]
- lastEvaluatedKey, err = buildNextKey(indexName, events[*pageSize-1])
+
+ if len(events) > 0 {
+ response := &models.EventList{
+ Events: events,
+ }
+
+ log.WithFields(f).Debugf("returning %d events - last key: %+v", len(events), results.LastEvaluatedKey)
+ if len(results.LastEvaluatedKey) > 0 {
+ log.WithFields(f).Debug("building next key...")
+ encodedString, err := buildNextKey(indexName, events[len(events)-1])
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to build nextKey. index = %s, event = %#v error = %s", indexName, events[*pageSize-1], err.Error())
+ log.WithFields(f).WithError(err).Warn("unable to build nextKey")
}
- } else {
- events = events[0:int64(len(events))]
+ response.NextKey = encodedString
+ log.WithFields(f).Debugf("lastEvaluatedKey encoded is: %s", encodedString)
}
- }
- if len(events) > 0 {
- return &models.EventList{
- Events: events,
- NextKey: lastEvaluatedKey,
- }, nil
+ return response, nil
}
// Just return an empty response - no events - just an empty list, and no nextKey
@@ -485,14 +522,18 @@ func buildNextKey(indexName string, event *models.Event) (string, error) {
case CompanyIDEventTypeIndex:
nextKey["company_id"] = &dynamodb.AttributeValue{S: aws.String(event.EventCompanyID)}
nextKey["event_type"] = &dynamodb.AttributeValue{S: aws.String(event.EventType)}
+ case EventCLAGroupIDEpochIndex:
+ nextKey["event_cla_group_id"] = &dynamodb.AttributeValue{S: aws.String(event.EventCLAGroupID)}
+ nextKey["event_time_epoch"] = &dynamodb.AttributeValue{N: aws.String(strconv.FormatInt(event.EventTimeEpoch, 10))}
}
- return toString(nextKey)
+
+ return encodeNextKey(nextKey)
}
// GetCompanyFoundationEvents returns the list of events for foundation and company
func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, foundationSFID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.repository.GetCompanyFoundationEvents",
+ "functionName": "v1.events.repository.GetCompanyFoundationEvents",
"companySFID": companySFID,
"companyID": companyID,
"foundationSFID": foundationSFID,
@@ -511,7 +552,7 @@ func (repo *repository) GetCompanyFoundationEvents(companySFID, companyID, found
// GetCompanyClaGroupEvents returns the list of events for cla group and the company
func (repo *repository) GetCompanyClaGroupEvents(companySFID, companyID, claGroupID string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.repository.GetCompanyClaGroupEvents",
+ "functionName": "v1.events.repository.GetCompanyClaGroupEvents",
"companySFID": companySFID,
"companyID": companyID,
"claGroupID": claGroupID,
@@ -530,7 +571,7 @@ func (repo *repository) GetCompanyClaGroupEvents(companySFID, companyID, claGrou
// GetCompanyEvents returns the list of events for given company id and event types
func (repo *repository) GetCompanyEvents(companyID, eventType string, nextKey *string, paramPageSize *int64, all bool) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.repository.GetCompanyEvents",
+ "functionName": "v1.events.repository.GetCompanyEvents",
"companyID": companyID,
"nextKey": utils.StringValue(nextKey),
"paramPageSize": utils.Int64Value(paramPageSize),
@@ -546,7 +587,7 @@ func (repo *repository) GetCompanyEvents(companyID, eventType string, nextKey *s
// GetFoundationEvents returns the list of foundation events
func (repo *repository) GetFoundationEvents(foundationSFID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.repository.GetFoundationEvents",
+ "functionName": "v1.events.repository.GetFoundationEvents",
"foundationSFID": foundationSFID,
"nextKey": utils.StringValue(nextKey),
"paramPageSize": utils.Int64Value(paramPageSize),
@@ -561,7 +602,7 @@ func (repo *repository) GetFoundationEvents(foundationSFID string, nextKey *stri
// GetClaGroupEvents returns the list of cla-group events
func (repo *repository) GetClaGroupEvents(claGroupID string, nextKey *string, paramPageSize *int64, all bool, searchTerm *string) (*models.EventList, error) {
f := logrus.Fields{
- "functionName": "events.repository.GetClaGroupEvents",
+ "functionName": "v1.events.repository.GetClaGroupEvents",
"claGroupID": claGroupID,
"nextKey": utils.StringValue(nextKey),
"paramPageSize": utils.Int64Value(paramPageSize),
@@ -573,8 +614,8 @@ func (repo *repository) GetClaGroupEvents(claGroupID string, nextKey *string, pa
return repo.queryEventsTable(EventCLAGroupIDEpochIndex, keyCondition, nil, nextKey, paramPageSize, all, searchTerm)
}
-// toString encodes the map as a string
-func toString(in map[string]*dynamodb.AttributeValue) (string, error) {
+// encodeNextKey encodes the map as a string
+func encodeNextKey(in map[string]*dynamodb.AttributeValue) (string, error) {
if len(in) == 0 {
return "", nil
}
@@ -585,35 +626,47 @@ func toString(in map[string]*dynamodb.AttributeValue) (string, error) {
return base64.StdEncoding.EncodeToString(b), nil
}
-// fromString converts the string to a map
-func fromString(str string) (map[string]*dynamodb.AttributeValue, error) {
+// decodeNextKey decodes the next key value into a dynamodb attribute value
+func decodeNextKey(str string) (map[string]*dynamodb.AttributeValue, error) {
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.decodeNextKey",
+ }
+
sDec, err := base64.StdEncoding.DecodeString(str)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error decoding string %s", str)
return nil, err
}
+
var m map[string]*dynamodb.AttributeValue
err = json.Unmarshal(sDec, &m)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling string after decoding: %s", sDec)
return nil, err
}
+
return m, nil
}
// buildEventListModel converts the query results to a list event models
func buildEventListModels(results *dynamodb.QueryOutput) ([]*models.Event, error) {
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.buildEventListModels",
+ }
events := make([]*models.Event, 0)
var items []Event
err := dynamodbattribute.UnmarshalListOfMaps(results.Items, &items)
if err != nil {
- log.Warnf("error unmarshalling events from database, error: %v",
- err)
+ log.WithFields(f).WithError(err).Warn("error unmarshalling events from database")
return nil, err
}
+
for _, e := range items {
events = append(events, e.toEvent())
}
+
return events, nil
}
@@ -639,6 +692,11 @@ func buildProjection() expression.ProjectionBuilder {
}
func (repo repository) GetRecentEvents(pageSize int64) (*models.EventList, error) {
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.GetRecentEvents",
+ "pageSize": pageSize,
+ }
+
ctime := time.Now()
maxQueryDays := 30
events := make([]*models.Event, 0)
@@ -646,6 +704,7 @@ func (repo repository) GetRecentEvents(pageSize int64) (*models.EventList, error
day := toDateFormat(ctime)
eventList, err := repo.getEventByDay(day, false, pageSize)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("error fetching events by day")
return nil, err
}
events = append(events, eventList...)
@@ -659,11 +718,16 @@ func (repo repository) GetRecentEvents(pageSize int64) (*models.EventList, error
return &models.EventList{
Events: events,
}, nil
-
}
func (repo repository) getEventByDay(day string, containsPII bool, pageSize int64) ([]*models.Event, error) {
- tableName := fmt.Sprintf("cla-%s-events", repo.stage)
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.getEventByDay",
+ "day": day,
+ "containsPII": containsPII,
+ "pageSize": pageSize,
+ }
+
var condition expression.KeyConditionBuilder
builder := expression.NewBuilder().WithProjection(buildProjection())
@@ -676,8 +740,10 @@ func (repo repository) getEventByDay(day string, containsPII bool, pageSize int6
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
+ log.WithFields(f).WithError(err).Warn("error building events query")
return nil, err
}
+
// Assemble the query input parameters
queryInput := &dynamodb.QueryInput{
ExpressionAttributeNames: expr.Names(),
@@ -685,7 +751,7 @@ func (repo repository) getEventByDay(day string, containsPII bool, pageSize int6
KeyConditionExpression: expr.KeyCondition(),
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
- TableName: aws.String(tableName),
+ TableName: aws.String(repo.eventsTable),
IndexName: aws.String(indexName),
Limit: aws.Int64(pageSize), // The maximum number of items to evaluate (not necessarily the number of matching items)
ScanIndexForward: aws.Bool(false),
@@ -696,12 +762,13 @@ func (repo repository) getEventByDay(day string, containsPII bool, pageSize int6
for {
results, errQuery := repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
- log.Warnf("error retrieving events. error = %s", errQuery.Error())
+ log.WithFields(f).Warnf("error retrieving events. error = %s", errQuery.Error())
return nil, errQuery
}
eventsList, modelErr := buildEventListModels(results)
if modelErr != nil {
+ log.WithFields(f).Warn("error building event models")
return nil, modelErr
}
@@ -721,9 +788,18 @@ func (repo repository) getEventByDay(day string, containsPII bool, pageSize int6
}
func (repo repository) AddDataToEvent(eventID, parentProjectSFID, projectSFID, projectSFName, companySFID, projectID string) error {
- tableName := fmt.Sprintf("cla-%s-events", repo.stage)
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.AddDataToEvent",
+ "eventID": eventID,
+ "parentProjectSFID": parentProjectSFID,
+ "projectSFID": projectSFID,
+ "projectSFName": projectSFName,
+ "companySFID": companySFID,
+ "projectID": projectID,
+ }
+
input := &dynamodb.UpdateItemInput{
- TableName: aws.String(tableName),
+ TableName: aws.String(repo.eventsTable),
Key: map[string]*dynamodb.AttributeValue{
"event_id": {
S: aws.String(eventID),
@@ -758,6 +834,7 @@ func (repo repository) AddDataToEvent(eventID, parentProjectSFID, projectSFID, p
ue.AddUpdateExpression("#company_sfid_project_id = :company_sfid_project_id", companySFID != "" && projectID != "")
if ue.Expression == "" {
// nothing to update
+ log.WithFields(f).Warn("not expression - nothing to update")
return nil
}
input.UpdateExpression = aws.String(ue.Expression)
@@ -765,9 +842,9 @@ func (repo repository) AddDataToEvent(eventID, parentProjectSFID, projectSFID, p
input.ExpressionAttributeValues = ue.ExpressionAttributeValues
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
- log.Debugf("update input: %v", input)
- log.Warnf("unable to add extra details to event : %s . error = %s", eventID, updateErr.Error())
+ log.WithFields(f).WithError(updateErr).Warnf("unable to add extra details to event : %s . error = %s", eventID, updateErr.Error())
return updateErr
}
+
return nil
}
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 8d6aacfd9..fb5d6291e 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -959,8 +959,8 @@ paths:
/events/foundation/{foundationSFID}:
get:
- summary: Get the events for the foundation
- description: get all the events for the foundation
+ summary: Get foundation events
+ description: Returns events for the specified foundation
operationId: getFoundationEvents
parameters:
- $ref: "#/parameters/path-foundationSFID"
@@ -996,8 +996,8 @@ paths:
/events/project/{projectSFID}:
get:
- summary: Get the events for the project
- description: get all the events for the project
+ summary: Get project events
+ description: Returns events for the specified project
operationId: getProjectEvents
parameters:
- $ref: "#/parameters/path-projectSFID"
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 6c16e0b67..f5de6db4b 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -4169,6 +4169,7 @@ class Meta:
event_date = UnicodeAttribute(null=True)
event_data = UnicodeAttribute(null=True)
+ event_data_lower = UnicodeAttribute(null=True)
event_summary = UnicodeAttribute(null=True)
event_date_and_contains_pii = UnicodeAttribute(null=True)
@@ -4228,6 +4229,8 @@ def __init__(
self.model.event_company_name_lower = self.model.event_company_name.lower()
self.model.event_data = event_data
+ if self.model.event_data:
+ self.model.event_data_lower = self.model.event_data.lower()
self.model.event_summary = event_summary
self.model.contains_pii = contains_pii
@@ -4284,6 +4287,9 @@ def get_event_user_id(self):
def get_event_data(self):
return self.model.event_data
+ def get_event_data_lower(self):
+ return self.model.event_data_lower
+
def get_event_summary(self):
return self.model.event_summary
@@ -4373,6 +4379,11 @@ def all_limit(self, limit: Optional[int] = None, last_evaluated_key: Optional[st
def set_event_data(self, event_data: str):
self.model.event_data = event_data
+ self.model.event_data_lower = event_data.lower()
+
+ def set_event_data_lower(self, event_data: str):
+ if event_data:
+ self.model.event_data_lower = event_data.lower()
def set_event_summary(self, event_summary: str):
self.model.event_summary = event_summary
From c07292196f5c27ffb14b95bf88fa4e085094c058 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 25 Mar 2021 17:51:52 -0700
Subject: [PATCH 0191/1276] Updatd PyYAML Lib Vulnerability (#2823)
- Resolved CVE-2020-14343 moderate severity Vulnerable versions: < 5.4
Patched version: 5.4 A vulnerability was discovered in the PyYAML
library in versions before 5.4, where it is susceptible to arbitrary
code execution when it processes untrusted YAML files through the
full_load method or with the FullLoader loader. Applications that use
the library to process untrusted input may be vulnerable to this flaw.
This flaw allows an attacker to execute arbitrary code on the system by
abusing the python/object/new constructor. This flaw is due to an
incomplete fix for CVE-2020-1747.
Signed-off-by: David Deal
---
cla-backend-go/swagger/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/swagger/requirements.txt b/cla-backend-go/swagger/requirements.txt
index 3b2ca554f..2b8cda8f5 100644
--- a/cla-backend-go/swagger/requirements.txt
+++ b/cla-backend-go/swagger/requirements.txt
@@ -2,4 +2,4 @@
# SPDX-License-Identifier: MIT
click==7.1.2
-PyYAML==5.3.1
+PyYAML>=5.4
From bd7c9293f3adc8f8bb71c7b88d1c02a3ccccf9a5 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Mon, 29 Mar 2021 18:38:40 +0300
Subject: [PATCH 0192/1276] [LFX 3147] Bug/Approval List Update (Ecla) (#2824)
- Handled empty ecla list for approval list updates
Signed-off-by: wanyaland
---
cla-backend-go/signatures/repository.go | 30 +++++++++++++++----------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 8977e0c1a..63f8d0c45 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2088,21 +2088,27 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", appErr)
return
}
- log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
- note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to email approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveEmailApprovalList)
- signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
- if signErr != nil {
- log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
- return
+ if len(signs.Signatures) == 0 {
+ log.WithFields(f).Debugf("company employee signatures do not exist for company:%s and project: %s ", companyID, projectID)
}
- //Log Event
- eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
- SignatureID: signs.Signatures[0].SignatureID,
- Email: email,
- CLAManager: claManager,
- CLAGroupID: signs.Signatures[0].ProjectID,
+ if len(signs.Signatures) > 0 {
+ log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to email approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveEmailApprovalList)
+
+ signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
+ if signErr != nil {
+ log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
+ return
+ }
+ //Log Event
+ eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
+ SignatureID: signs.Signatures[0].SignatureID,
+ Email: email,
+ CLAManager: claManager,
+ CLAGroupID: signs.Signatures[0].ProjectID,
+ }
}
repo.eventsService.LogEventWithContext(ctx, eventArgs)
From cbd1bb30adfc85fa668ec28f892cc4c303d35222 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 29 Mar 2021 15:09:09 -0700
Subject: [PATCH 0193/1276] Added Python Event Function for Searching For
Missing Data Lower Column (#2825)
- Added search_missing_event_data_lower() function
- Added type hints
Signed-off-by: David Deal
---
cla-backend/cla/models/dynamo_models.py | 86 +++++++++++++++++--------
1 file changed, 60 insertions(+), 26 deletions(-)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index f5de6db4b..e89bf2e26 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -32,8 +32,8 @@
from cla.models import model_interfaces, key_value_store_interface, DoesNotExist
from cla.models.event_types import EventType
from cla.models.model_interfaces import User, Signature, ProjectCLAGroup, Repository, Gerrit
-from cla.project_service import ProjectService
from cla.models.model_utils import is_uuidv4
+from cla.project_service import ProjectService
stage = os.environ.get("STAGE", "")
cla_logo_url = os.environ.get("CLA_BUCKET_LOGO_URL", "")
@@ -4281,79 +4281,85 @@ def load(self, event_id):
raise cla.models.DoesNotExist("Event not found")
self.model = event
- def get_event_user_id(self):
+ def get_event_date_created(self) -> str:
+ return self.model.date_created
+
+ def get_event_date_modified(self) -> str:
+ return self.model.date_modified
+
+ def get_event_user_id(self) -> str:
return self.model.event_user_id
- def get_event_data(self):
+ def get_event_data(self) -> str:
return self.model.event_data
- def get_event_data_lower(self):
+ def get_event_data_lower(self) -> str:
return self.model.event_data_lower
- def get_event_summary(self):
+ def get_event_summary(self) -> str:
return self.model.event_summary
- def get_event_date(self):
+ def get_event_date(self) -> str:
return self.model.event_date
- def get_event_id(self):
+ def get_event_id(self) -> str:
return self.model.event_id
- def get_event_cla_group_id(self):
+ def get_event_cla_group_id(self) -> str:
return self.model.event_cla_group_id
- def get_event_cla_group_name(self):
+ def get_event_cla_group_name(self) -> str:
return self.model.event_cla_group_name
- def get_event_cla_group_name_lower(self):
+ def get_event_cla_group_name_lower(self) -> str:
return self.model.event_cla_group_name_lower
- def get_event_project_id(self):
+ def get_event_project_id(self) -> str:
return self.model.event_project_id
- def get_event_project_sfid(self):
+ def get_event_project_sfid(self) -> str:
return self.model.event_project_sfid
- def get_event_project_name(self):
+ def get_event_project_name(self) -> str:
return self.model.event_project_name
- def get_event_project_name_lower(self):
+ def get_event_project_name_lower(self) -> str:
return self.model.event_project_name_lower
- def get_event_parent_project_sfid(self):
+ def get_event_parent_project_sfid(self) -> str:
return self.model.event_parent_project_sfid
- def get_event_parent_project_name(self):
+ def get_event_parent_project_name(self) -> str:
return self.model.event_parent_project_name
- def get_event_type(self):
+ def get_event_type(self) -> str:
return self.model.event_type
- def get_event_time(self):
+ def get_event_time(self) -> str:
return self.model.date_created
- def get_event_time_epoch(self):
+ def get_event_time_epoch(self) -> int:
return self.model.event_time_epoch
- def get_event_company_id(self):
+ def get_event_company_id(self) -> str:
return self.model.event_company_id
- def get_event_company_sfid(self):
+ def get_event_company_sfid(self) -> str:
return self.model.event_company_sfid
- def get_event_company_name(self):
+ def get_event_company_name(self) -> str:
return self.model.event_company_name
- def get_event_company_name_lower(self):
+ def get_event_company_name_lower(self) -> str:
return self.model.event_company_name_lower
- def get_event_user_name(self):
+ def get_event_user_name(self) -> str:
return self.model.event_user_name
- def get_event_user_name_lower(self):
+ def get_event_user_name_lower(self) -> str:
return self.model.event_user_name_lower
- def get_company_id_external_project_id(self):
+ def get_company_id_external_project_id(self) -> str:
return self.model.company_id_external_project_id
def all(self, ids=None):
@@ -4377,6 +4383,34 @@ def all_limit(self, limit: Optional[int] = None, last_evaluated_key: Optional[st
ret.append(evt)
return ret, result_iterator.last_evaluated_key, result_iterator.total_count
+ def search_missing_event_data_lower(self, limit: Optional[int] = None, last_evaluated_key: Optional[str] = None):
+ filter_condition = (EventModel.event_data_lower.does_not_exist())
+ projection = ["event_id", "event_data", "event_data_lower"]
+ result_iterator = self.model.scan(limit=limit,
+ last_evaluated_key=last_evaluated_key,
+ filter_condition=filter_condition,
+ attributes_to_get=projection)
+ ret = []
+ for signature in result_iterator:
+ evt = Event()
+ evt.model = signature
+ ret.append(evt)
+ return ret, result_iterator.last_evaluated_key, result_iterator.total_count
+
+ # def search_by_year(self, year: str, limit: Optional[int] = None, last_evaluated_key: Optional[str] = None):
+ # filter_condition = (EventModel.event_date.contains(year))
+ # projection = ["event_id", "event_date"]
+ # result_iterator = self.model.scan(limit=limit,
+ # last_evaluated_key=last_evaluated_key,
+ # filter_condition=filter_condition,
+ # attributes_to_get=projection)
+ # ret = []
+ # for signature in result_iterator:
+ # evt = Event()
+ # evt.model = signature
+ # ret.append(evt)
+ # return ret, result_iterator.last_evaluated_key, result_iterator.total_count
+
def set_event_data(self, event_data: str):
self.model.event_data = event_data
self.model.event_data_lower = event_data.lower()
From 434b34f923c54aa817f7179e77d6f405e3418672 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Tue, 30 Mar 2021 13:01:43 +0300
Subject: [PATCH 0194/1276] closing docraptor body is handled elsewhere, doing
it here breaks the code
Signed-off-by: makkalot
---
cla-backend-go/docraptor/client.go | 7 -------
1 file changed, 7 deletions(-)
diff --git a/cla-backend-go/docraptor/client.go b/cla-backend-go/docraptor/client.go
index 89ffd83da..16a0ed202 100644
--- a/cla-backend-go/docraptor/client.go
+++ b/cla-backend-go/docraptor/client.go
@@ -72,12 +72,5 @@ func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
log.WithFields(f).WithError(err).Warn("problem with API call to docraptor")
return nil, err
}
- defer func() {
- closeErr := resp.Body.Close()
- if closeErr != nil {
- log.WithFields(f).WithError(closeErr).Warn("error closing response body")
- }
- }()
-
return resp.Body, nil
}
From 9e1035bbea5e3ce814a216990f18839929512190 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Tue, 30 Mar 2021 20:11:51 +0300
Subject: [PATCH 0195/1276] [#LFX 3147] Bug/Approval List (#2828)
- Ignore invalidate signatures errors raised when updating approval List
Signed-off-by: wanyaland
---
cla-backend-go/go.sum | 1 +
cla-backend-go/signatures/repository.go | 98 ++++++++++++-------------
2 files changed, 50 insertions(+), 49 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 19499d7aa..9f4b4ebe8 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -97,6 +97,7 @@ github.com/communitybridge/easycla v1.0.133 h1:aJulQGLLRISCMsZcCP4aIE8xGtHoBNm/E
github.com/communitybridge/easycla v1.0.135 h1:Dvn8jX+7BAnpmA+jvdK0n5ajWP8SoH5vvopt7whZDEU=
github.com/communitybridge/easycla v1.0.145 h1:ikhBSsOeEL2u3/EoyDsufh/j3HkjfFTiXAk1d61GoS8=
github.com/communitybridge/easycla v2.0.10+incompatible h1:6eRJ5fxrMxRZHBkg8piYo+zHTcSowMrP85nZXzp5mpA=
+github.com/communitybridge/easycla v2.0.16+incompatible h1:I0hEApDh4IvlwRPyHV1LOsSYlSPbqBsGszjSTHwkdak=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 63f8d0c45..c5d2b074a 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2168,7 +2168,6 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if invalidateErr != nil {
msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
}
}
}
@@ -2373,7 +2372,34 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil)
if err != nil {
log.WithFields(f).Warn("unable to get iclas")
- return err
+ }
+
+ if iclas != nil {
+ var iclaWg sync.WaitGroup
+ //Iterate iclas
+ iclaWg.Add(len(iclas.List))
+ log.WithFields(f).Debug("invalidating signature icla records... ")
+ for _, icla := range iclas.List {
+ go func(icla *models.IclaSignature) {
+ defer iclaWg.Done()
+ signature, sigErr := repo.GetSignature(ctx, icla.SignatureID)
+ if sigErr != nil {
+ log.WithFields(f).Warnf("unable to fetch signature for ID: %s ", icla.SignatureID)
+ return
+ }
+ // Grab user record
+ if signature.SignatureReferenceID == "" {
+ log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", signature)
+ return
+ }
+ verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
+ if verifyErr != nil {
+ log.WithFields(f).Warnf("unable to verify user: %s ", signature.SignatureReferenceID)
+ return
+ }
+ }(icla)
+ }
+ iclaWg.Wait()
}
// Get ECLAs
@@ -2387,54 +2413,28 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
return err
}
- var iclaWg, eclaWg sync.WaitGroup
-
- //Iterate iclas
- iclaWg.Add(len(iclas.List))
- log.WithFields(f).Debug("invalidating signature icla records... ")
-
- for _, icla := range iclas.List {
- go func(icla *models.IclaSignature) {
- defer iclaWg.Done()
- signature, err := repo.GetSignature(ctx, icla.SignatureID)
- if err != nil {
- log.WithFields(f).Warnf("unable to fetch signature for ID: %s ", icla.SignatureID)
- return
- }
- // Grab user record
- if signature.SignatureReferenceID == "" {
- log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", signature)
- return
- }
- verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
- if verifyErr != nil {
- log.WithFields(f).Warnf("unable to verify user: %s ", signature.SignatureReferenceID)
- return
- }
- }(icla)
- }
- iclaWg.Wait()
-
- log.WithFields(f).Debug("invalidating signature ecla records... ")
- // Iterate eclas
- eclaWg.Add(len(eclas.Signatures))
- for _, ecla := range eclas.Signatures {
- go func(ecla *models.Signature) {
- defer eclaWg.Done()
- // Grab user record
- if ecla.SignatureReferenceID == "" {
- log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", ecla)
- return
- }
- verifyErr := repo.verifyUserApprovals(ctx, ecla.SignatureReferenceID, ecla.SignatureID, claManager, approvalList)
- if verifyErr != nil {
- log.WithFields(f).Warnf("unable to verify user: %s ", ecla.SignatureReferenceID)
- return
- }
- }(ecla)
+ if eclas != nil {
+ var eclaWg sync.WaitGroup
+ log.WithFields(f).Debug("invalidating signature ecla records... ")
+ // Iterate eclas
+ eclaWg.Add(len(eclas.Signatures))
+ for _, ecla := range eclas.Signatures {
+ go func(ecla *models.Signature) {
+ defer eclaWg.Done()
+ // Grab user record
+ if ecla.SignatureReferenceID == "" {
+ log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", ecla)
+ return
+ }
+ verifyErr := repo.verifyUserApprovals(ctx, ecla.SignatureReferenceID, ecla.SignatureID, claManager, approvalList)
+ if verifyErr != nil {
+ log.WithFields(f).Warnf("unable to verify user: %s ", ecla.SignatureReferenceID)
+ return
+ }
+ }(ecla)
+ }
+ eclaWg.Wait()
}
- eclaWg.Wait()
-
return nil
}
From 1ea50d32c01d425f24bffb959b36baa104f5797a Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Tue, 30 Mar 2021 20:35:50 +0300
Subject: [PATCH 0196/1276] refactor ListClaGroupsForFoundationOrProject
(#2827)
handling for goroutines
Signed-off-by: makkalot
---
cla-backend-go/v2/cla_groups/service.go | 293 ++++++++++++++----------
1 file changed, 169 insertions(+), 124 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index b9d5ebaf8..d5aa44c65 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -10,6 +10,7 @@ import (
"sort"
"strings"
"sync"
+ "time"
"github.com/aws/aws-sdk-go/aws"
@@ -355,6 +356,11 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
"projectOrFoundationSFID": projectOrFoundationSFID,
}
+ // setup some timeout for the whole operation
+ var cancelFunc context.CancelFunc
+ ctx, cancelFunc = context.WithTimeout(ctx, time.Second*20)
+ defer cancelFunc()
+
// Our list of CLA Groups associated with this foundation (could be > 1) or project (only 1)
var v1ClaGroups = new(v1Models.ClaGroups)
// Our response model for this function
@@ -398,106 +404,90 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
// If it's a project...
if utils.IsProjectCategory(sfProjectModelDetails, parentDetails) {
- // Since this is a project and not a foundation, we'll want to set he parent foundation ID and name (which is
- // our parent in this case)
- log.WithFields(f).Debug("found 'project' in platform project service.")
- if sfProjectModelDetails.ProjectOutput.Foundation != nil {
- foundationID = sfProjectModelDetails.ProjectOutput.Foundation.ID
- foundationName = sfProjectModelDetails.ProjectOutput.Foundation.Name
- log.WithFields(f).Debugf("using parent foundation ID: %s and name: %s", foundationID, foundationName)
- } else {
- // Project with no parent - must be a standalone - use our ID and Name as the foundation
- foundationID = sfProjectModelDetails.ID
- foundationName = sfProjectModelDetails.Name
- log.WithFields(f).Debugf("no parent - using project as foundation ID: %s and name: %s", foundationID, foundationName)
+ var appendErr error
+ foundationID, foundationName, appendErr = s.appendCLAGroupsForProject(ctx, f, projectOrFoundationSFID, sfProjectModelDetails, v1ClaGroups)
+ if appendErr != nil {
+ return nil, appendErr
}
-
- log.WithFields(f).Debug("locating CLA Group mapping...")
- projectCLAGroup, lookupErr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectOrFoundationSFID)
- if lookupErr != nil {
- log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
- return nil, &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
+ } else if sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup {
+ if err := s.appendCLAGroupsForFoundation(ctx, f, projectOrFoundationSFID, v1ClaGroups); err != nil {
+ return nil, err
}
+ } else {
+ msg := fmt.Sprintf("unsupported foundation/project SFID type: %s", sfProjectModelDetails.ProjectType)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
- log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroup.ClaGroupID)
- v1ClaGroupsByProject, claGroupLoadErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroup.ClaGroupID)
- //v1ClaGroupsByProject, prjerr := s.v1ProjectService.GetClaGroupByProjectSFID(projectOrFoundationSFID, DontLoadDetails)
- if claGroupLoadErr != nil {
- log.WithFields(f).Warnf("problem loading CLA group by id, error: %+v", claGroupLoadErr)
- return nil, &utils.CLAGroupNotFound{CLAGroupID: projectCLAGroup.ClaGroupID, Err: claGroupLoadErr}
- }
+ log.WithFields(f).Debugf("Building response model for %d CLA Groups", len(v1ClaGroups.Projects))
- v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1ClaGroupsByProject)
+ claGroupIDList, err := s.buildClaGroupSummaryResponseModel(ctx, f, v1ClaGroups, foundationName, foundationID, responseModel)
+ if err != nil {
+ return nil, err
+ }
- v1CLAGroupData, v1ClaGroupErr := s.v1ProjectService.GetClaGroupByProjectSFID(ctx, projectOrFoundationSFID, false)
- if v1ClaGroupErr != nil {
- log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", v1ClaGroupErr)
- return nil, &utils.CLAGroupNotFound{CLAGroupID: projectOrFoundationSFID, Err: v1ClaGroupErr}
- }
+ // One more pass to update the metrics - bulk lookup the metrics and update the response model
+ log.WithFields(f).Debugf("Loading metrics for %d CLA Groups...", len(claGroupIDList.List()))
+ s.loadMetrics(ctx, f, responseModel, claGroupIDList)
- _, found := Find(v1ClaGroups.Projects, v1CLAGroupData.ProjectID)
- if !found {
- v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1CLAGroupData)
+ // Sort the response based on the Foundation and CLA group name
+ sort.Slice(responseModel.List, func(i, j int) bool {
+ switch strings.Compare(responseModel.List[i].FoundationName, responseModel.List[j].FoundationName) {
+ case -1:
+ return true
+ case 1:
+ return false
}
+ return responseModel.List[i].ClaGroupName < responseModel.List[j].ClaGroupName
+ })
- } else if sfProjectModelDetails.ProjectType == utils.ProjectTypeProjectGroup {
- log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
- projectCLAGroupMappings, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
- if lookupErr != nil {
- log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
- return nil, &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
- }
- log.WithFields(f).Debugf("discovered %d projects based on foundation SFID...", len(projectCLAGroupMappings))
+ return responseModel, nil
+}
- // Determine how many CLA Groups we have - we could have many and possibly return duplicates, we use this loop
- uniqueCLAGroupList := getUniqueCLAGroupIDs(projectCLAGroupMappings)
+func (s *service) loadMetrics(ctx context.Context, f logrus.Fields, responseModel *models.ClaGroupListSummary, claGroupIDList *utils.StringSet) {
+ type MetricsResult struct {
+ index int
+ iclaSignatureCount int64
+ cclaSignatureCount int64
+ Error error
+ }
+ metricsResultChannel := make(chan *MetricsResult, len(responseModel.List))
- type CLAGroupResult struct {
- claGroupModel *v1Models.ClaGroup
- Error error
- }
- claGroupResultChannel := make(chan *CLAGroupResult, len(uniqueCLAGroupList))
-
- // Load these CLA Group records in parallel
- for _, projectCLAGroupClaGroupID := range uniqueCLAGroupList {
-
- // Load each CLA Group - save results to our channel
- go func(ctx context.Context, projectCLAGroupClaGroupID string) {
- log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroupClaGroupID)
- claGroupModel, claGroupLookupErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroupClaGroupID)
- if claGroupLookupErr != nil {
- log.WithFields(f).Warnf("problem locating project by id: %s, error: %+v", projectCLAGroupClaGroupID, claGroupLookupErr)
- claGroupResultChannel <- &CLAGroupResult{
- claGroupModel: nil,
- Error: &utils.SFProjectNotFound{ProjectSFID: projectCLAGroupClaGroupID, Err: claGroupLookupErr},
- }
- }
+ for idx, responseEntry := range responseModel.List {
+ go func(index int, responseEntry *models.ClaGroupSummary) {
+ log.WithFields(f).Debugf("fetching project signature metrics for CLA Group (%d): %s - %s", index, responseEntry.ClaGroupID, responseEntry.ClaGroupName)
+ iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeICLA), SignatureType: aws.String(utils.SignatureTypeCLA)})
+ if err != nil {
+ log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ }
- claGroupResultChannel <- &CLAGroupResult{
- claGroupModel: claGroupModel,
- Error: nil,
- }
- }(ctx, projectCLAGroupClaGroupID)
- }
+ cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeCCLA), SignatureType: aws.String(utils.SignatureTypeCCLA)})
+ if err != nil {
+ log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ }
- // Wait for the go routines to finish and load up the results
- log.WithFields(f).Debug("waiting for CLA Groups to load...")
- for range uniqueCLAGroupList {
- response := <-claGroupResultChannel
- if response.Error != nil {
- log.WithFields(f).WithError(response.Error).Warnf("unable to load CLA Group")
- return nil, response.Error
+ metricsResultChannel <- &MetricsResult{
+ index: index,
+ iclaSignatureCount: iclaSignatureDetails.ResultCount,
+ cclaSignatureCount: cclaSignatureDetails.ResultCount,
+ Error: err,
}
- v1ClaGroups.Projects = append(v1ClaGroups.Projects, *response.claGroupModel)
- }
- } else {
- msg := fmt.Sprintf("unsupported foundation/project SFID type: %s", sfProjectModelDetails.ProjectType)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
+ }(idx, responseEntry)
}
- log.WithFields(f).Debugf("Building response model for %d CLA Groups", len(v1ClaGroups.Projects))
+ log.WithFields(f).Debugf("Waiting for metrics responses for %d CLA Groups...", len(claGroupIDList.List()))
+ for range responseModel.List {
+ select {
+ case response := <-metricsResultChannel:
+ responseModel.List[response.index].TotalSignatures = response.cclaSignatureCount + response.iclaSignatureCount
+ case <-ctx.Done():
+ log.WithError(ctx.Err()).Warnf("waiting for metrics failed with timeout")
+ return
+ }
+ }
+}
+func (s *service) buildClaGroupSummaryResponseModel(ctx context.Context, f logrus.Fields, v1ClaGroups *v1Models.ClaGroups, foundationName string, foundationID string, responseModel *models.ClaGroupListSummary) (*utils.StringSet, error) {
claGroupIDList := utils.NewStringSet()
// Build the response model for each CLA Group...
@@ -506,11 +496,11 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
// Keep a list of the CLA Group IDs - we'll use it later to do a batch look in the metrics
claGroupIDList.Add(v1ClaGroup.ProjectID)
- currentICLADoc, docErr := v1Project.GetCurrentDocument(context.Background(), v1ClaGroup.ProjectIndividualDocuments)
+ currentICLADoc, docErr := v1Project.GetCurrentDocument(ctx, v1ClaGroup.ProjectIndividualDocuments)
if docErr != nil {
log.WithFields(f).WithError(docErr).Warn("problem determining current ICLA for this CLA Group")
}
- currentCCLADoc, docErr := v1Project.GetCurrentDocument(context.Background(), v1ClaGroup.ProjectCorporateDocuments)
+ currentCCLADoc, docErr := v1Project.GetCurrentDocument(ctx, v1ClaGroup.ProjectCorporateDocuments)
if docErr != nil {
log.WithFields(f).WithError(docErr).Warn("problem determining current CCLA for this CLA Group")
}
@@ -571,57 +561,112 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
// Add this CLA Group to our response model
responseModel.List = append(responseModel.List, cg)
}
+ return claGroupIDList, nil
+}
- // One more pass to update the metrics - bulk lookup the metrics and update the response model
- log.WithFields(f).Debugf("Loading metrics for %d CLA Groups...", len(claGroupIDList.List()))
- type MetricsResult struct {
- index int
- iclaSignatureCount int64
- cclaSignatureCount int64
- Error error
+func (s *service) appendCLAGroupsForFoundation(ctx context.Context, f logrus.Fields, projectOrFoundationSFID string, v1ClaGroups *v1Models.ClaGroups) error {
+ log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
+ projectCLAGroupMappings, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
+ if lookupErr != nil {
+ log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
+ return &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
}
- metricsResultChannel := make(chan *MetricsResult, len(responseModel.List))
+ log.WithFields(f).Debugf("discovered %d projects based on foundation SFID...", len(projectCLAGroupMappings))
- for idx, responseEntry := range responseModel.List {
- go func(index int, responseEntry *models.ClaGroupSummary) {
- log.WithFields(f).Debugf("fetching project signature metrics for CLA Group (%d): %s - %s", index, responseEntry.ClaGroupID, responseEntry.ClaGroupName)
- iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeICLA), SignatureType: aws.String(utils.SignatureTypeCLA)})
- if err != nil {
- log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ // Determine how many CLA Groups we have - we could have many and possibly return duplicates, we use this loop
+ uniqueCLAGroupList := getUniqueCLAGroupIDs(projectCLAGroupMappings)
+
+ type CLAGroupResult struct {
+ claGroupModel *v1Models.ClaGroup
+ Error error
+ }
+
+ claGroupResultChannel := make(chan *CLAGroupResult, len(uniqueCLAGroupList))
+
+ // Load these CLA Group records in parallel
+ for _, projectCLAGroupClaGroupID := range uniqueCLAGroupList {
+
+ // Load each CLA Group - save results to our channel
+ go func(ctx context.Context, projectCLAGroupClaGroupID string) {
+ log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroupClaGroupID)
+ claGroupModel, claGroupLookupErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroupClaGroupID)
+ if claGroupLookupErr != nil {
+ log.WithFields(f).Warnf("problem locating project by id: %s, error: %+v", projectCLAGroupClaGroupID, claGroupLookupErr)
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: nil,
+ Error: &utils.SFProjectNotFound{ProjectSFID: projectCLAGroupClaGroupID, Err: claGroupLookupErr},
+ }
}
- cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeCCLA), SignatureType: aws.String(utils.SignatureTypeCCLA)})
- if err != nil {
- log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ claGroupResultChannel <- &CLAGroupResult{
+ claGroupModel: claGroupModel,
+ Error: nil,
}
+ }(ctx, projectCLAGroupClaGroupID)
+ }
- metricsResultChannel <- &MetricsResult{
- index: index,
- iclaSignatureCount: iclaSignatureDetails.ResultCount,
- cclaSignatureCount: cclaSignatureDetails.ResultCount,
- Error: err,
+ // Wait for the go routines to finish and load up the results
+ log.WithFields(f).Debug("waiting for CLA Groups to load...")
+ for range uniqueCLAGroupList {
+ select {
+ case response := <-claGroupResultChannel:
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warnf("unable to load CLA Group")
+ return response.Error
}
- }(idx, responseEntry)
+ v1ClaGroups.Projects = append(v1ClaGroups.Projects, *response.claGroupModel)
+ case <-ctx.Done():
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for CLA Groups to load timeouted")
+ return fmt.Errorf("cla group laoding failed : %v", ctx.Err())
+ }
}
+ return nil
+}
- log.WithFields(f).Debugf("Waiting for metrics responses for %d CLA Groups...", len(claGroupIDList.List()))
- for range responseModel.List {
- response := <-metricsResultChannel
- responseModel.List[response.index].TotalSignatures = response.cclaSignatureCount + response.iclaSignatureCount
+func (s *service) appendCLAGroupsForProject(ctx context.Context, f logrus.Fields, projectOrFoundationSFID string, sfProjectModelDetails *v2ProjectServiceModels.ProjectOutputDetailed, v1ClaGroups *v1Models.ClaGroups) (string, string, error) {
+ // Since this is a project and not a foundation, we'll want to set he parent foundation ID and name (which is
+ // our parent in this case)
+ var foundationID, foundationName string
+ log.WithFields(f).Debug("found 'project' in platform project service.")
+ if sfProjectModelDetails.ProjectOutput.Foundation != nil {
+ foundationID = sfProjectModelDetails.ProjectOutput.Foundation.ID
+ foundationName = sfProjectModelDetails.ProjectOutput.Foundation.Name
+ log.WithFields(f).Debugf("using parent foundation ID: %s and name: %s", foundationID, foundationName)
+ } else {
+ // Project with no parent - must be a standalone - use our ID and Name as the foundation
+ foundationID = sfProjectModelDetails.ID
+ foundationName = sfProjectModelDetails.Name
+ log.WithFields(f).Debugf("no parent - using project as foundation ID: %s and name: %s", foundationID, foundationName)
}
- // Sort the response based on the Foundation and CLA group name
- sort.Slice(responseModel.List, func(i, j int) bool {
- switch strings.Compare(responseModel.List[i].FoundationName, responseModel.List[j].FoundationName) {
- case -1:
- return true
- case 1:
- return false
- }
- return responseModel.List[i].ClaGroupName < responseModel.List[j].ClaGroupName
- })
+ log.WithFields(f).Debug("locating CLA Group mapping...")
+ projectCLAGroup, lookupErr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectOrFoundationSFID)
+ if lookupErr != nil {
+ log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
+ return "", "", &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
+ }
- return responseModel, nil
+ log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroup.ClaGroupID)
+ v1ClaGroupsByProject, claGroupLoadErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroup.ClaGroupID)
+ //v1ClaGroupsByProject, prjerr := s.v1ProjectService.GetClaGroupByProjectSFID(projectOrFoundationSFID, DontLoadDetails)
+ if claGroupLoadErr != nil {
+ log.WithFields(f).Warnf("problem loading CLA group by id, error: %+v", claGroupLoadErr)
+ return "", "", &utils.CLAGroupNotFound{CLAGroupID: projectCLAGroup.ClaGroupID, Err: claGroupLoadErr}
+ }
+
+ v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1ClaGroupsByProject)
+
+ v1CLAGroupData, v1ClaGroupErr := s.v1ProjectService.GetClaGroupByProjectSFID(ctx, projectOrFoundationSFID, false)
+ if v1ClaGroupErr != nil {
+ log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", v1ClaGroupErr)
+ return "", "", &utils.CLAGroupNotFound{CLAGroupID: projectOrFoundationSFID, Err: v1ClaGroupErr}
+ }
+
+ _, found := Find(v1ClaGroups.Projects, v1CLAGroupData.ProjectID)
+ if !found {
+ v1ClaGroups.Projects = append(v1ClaGroups.Projects, *v1CLAGroupData)
+ }
+ return foundationID, foundationName, nil
}
func (s *service) ListAllFoundationClaGroups(ctx context.Context, foundationID *string) (*models.FoundationMappingList, error) {
From b786184c386f81bcafe3e3a7f7ded0ea979544b6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 30 Mar 2021 19:34:23 -0700
Subject: [PATCH 0197/1276] Bump y18n from 3.2.1 to 3.2.2 in
/cla-frontend-contributor-console/edge (#2832)
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/edge/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index 3bc944542..8c6b32458 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -3421,9 +3421,9 @@ xtend@^4.0.0:
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
- integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
+ integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
yallist@^2.1.2:
version "2.1.2"
From 9e7663a35d1c59a332e6b018d26dcfe2424ec91a Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 31 Mar 2021 07:59:16 +0300
Subject: [PATCH 0198/1276] [#LFX 3147] Bug/Approval List Update (#2833)
- Resolved dereference issue caused during invalidating signature records
Signed-off-by: wanyaland
---
cla-backend-go/signatures/repository.go | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index c5d2b074a..95ae6caa3 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2164,6 +2164,8 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.ApprovalList = params.RemoveDomainApprovalList
approvalList.Action = utils.RemoveApprovals
approvalList.GerritICLAECLAs = gerritICLAECLAs
+ approvalList.ClaGroupID = projectID
+ approvalList.CompanyID = companyID
invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
if invalidateErr != nil {
msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
@@ -2369,6 +2371,7 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
// Get ICLAs
+ log.WithFields(f).Debug("getting icla records... ")
iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil)
if err != nil {
log.WithFields(f).Warn("unable to get iclas")
@@ -2403,11 +2406,14 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
// Get ECLAs
+ log.WithFields(f).Debug("getting ecla records... ")
companyProjectParams := signatures.GetProjectCompanyEmployeeSignaturesParams{
CompanyID: approvalList.CompanyID,
ProjectID: approvalList.ClaGroupID,
}
- eclas, err := repo.GetProjectCompanyEmployeeSignatures(ctx, companyProjectParams, nil, int64(10))
+
+ criteria := ApprovalCriteria{}
+ eclas, err := repo.GetProjectCompanyEmployeeSignatures(ctx, companyProjectParams, &criteria, int64(10))
if err != nil {
log.WithFields(f).Warnf("unable to get cclas for company: %s and project: %s ", approvalList.CompanyID, approvalList.ClaGroupID)
return err
From f69b2d2da51adcdc948112ef2e0c6b1de3e9f8aa Mon Sep 17 00:00:00 2001
From: makkalot
Date: Wed, 31 Mar 2021 14:16:30 +0300
Subject: [PATCH 0199/1276] missing foundation sfid in cla group update event
was preventing querying via the foundation sfid.
Signed-off-by: makkalot
---
cla-backend-go/v2/cla_groups/handlers.go | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index a6e97d184..88b5e4c5e 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -144,9 +144,10 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ProjectID: claGroup.ClaGroupID,
- LfUsername: authUser.UserName,
+ EventType: events.CLAGroupUpdated,
+ ProjectID: claGroup.ClaGroupID,
+ LfUsername: authUser.UserName,
+ ParentProjectSFID: claGroupModel.FoundationSFID,
EventData: &events.CLAGroupUpdatedEventData{
ClaGroupName: params.Body.ClaGroupName,
ClaGroupDescription: params.Body.ClaGroupDescription,
From 96d97f6d87dbdafbc5c69e851ab541cb5934c701 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 31 Mar 2021 08:03:15 -0700
Subject: [PATCH 0200/1276] Bump y18n from 3.2.1 to 3.2.2 in
/cla-frontend-project-console/src (#2829)
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 610bff4d9..658895412 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -4414,8 +4414,9 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
y18n@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
+ integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
yallist@^2.1.2:
version "2.1.2"
From 2affd839bc257d4396e4cf9a405df3729311fedf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 31 Mar 2021 10:22:47 -0700
Subject: [PATCH 0201/1276] Bump y18n from 3.2.1 to 3.2.2 in
/cla-frontend-corporate-console/src (#2830)
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index cbcf7ced9..00c9980c1 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -4251,8 +4251,9 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
y18n@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
+ integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
yallist@^2.1.2:
version "2.1.2"
From b9dd4eabe8f206e39fe861bdfc1354bf24884049 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 31 Mar 2021 10:36:05 -0700
Subject: [PATCH 0202/1276] Bump y18n from 3.2.1 to 3.2.2 in
/cla-frontend-contributor-console/src (#2831)
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 1cd7df705..377a3f2dc 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -4325,8 +4325,9 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
y18n@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
+ integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
yallist@^2.1.2:
version "2.1.2"
From 9977146c9a388a3cdb55acced40bf468bf8340e6 Mon Sep 17 00:00:00 2001
From: makkalot
Date: Thu, 1 Apr 2021 14:02:11 +0300
Subject: [PATCH 0203/1276] - set cla group id if not set - set parent project
sfid to foundation for projects that don't have parents
Signed-off-by: makkalot
---
cla-backend-go/events/service.go | 7 ++++++-
cla-backend-go/v2/cla_groups/handlers.go | 7 +++----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index a4cd66efe..ea948ac17 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -189,7 +189,7 @@ func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
args.CLAGroupName = args.ClaGroupModel.ProjectName
} else {
// Did they set the CLA Group ID?
- claGroupID := ""
+ var claGroupID string
if args.CLAGroupID != "" {
claGroupID = args.CLAGroupID
} else if args.ProjectID != "" && utils.IsUUIDv4(args.ProjectID) { // legacy parameter
@@ -205,6 +205,7 @@ func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
}
args.ClaGroupModel = claGroupModel
args.CLAGroupName = claGroupModel.ProjectName
+ args.CLAGroupID = claGroupID
} else if args.ProjectSFID != "" {
projectCLAGroupModel, projectCLAGroupErr := s.combinedRepo.GetClaGroupIDForProject(args.ProjectSFID)
if projectCLAGroupErr != nil || projectCLAGroupModel == nil {
@@ -269,6 +270,10 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
project.Parent, parentProjectID, parentProjectName)
args.ParentProjectSFID = parentProjectID
args.ParentProjectName = parentProjectName
+ } else if project.Foundation != nil {
+ // if there's no parent set the foundation as parent project
+ args.ParentProjectSFID = project.Foundation.ID
+ args.ParentProjectName = project.Foundation.Name
}
}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 88b5e4c5e..a6e97d184 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -144,10 +144,9 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ProjectID: claGroup.ClaGroupID,
- LfUsername: authUser.UserName,
- ParentProjectSFID: claGroupModel.FoundationSFID,
+ EventType: events.CLAGroupUpdated,
+ ProjectID: claGroup.ClaGroupID,
+ LfUsername: authUser.UserName,
EventData: &events.CLAGroupUpdatedEventData{
ClaGroupName: params.Body.ClaGroupName,
ClaGroupDescription: params.Body.ClaGroupDescription,
From 8b7fdfd13786f0f53e9c9265f04990eaedfa3a5e Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 1 Apr 2021 17:46:37 +0300
Subject: [PATCH 0204/1276] [#2792] Feature/Approval Update - invalidate ICLA
(#2841)
- added icla signature invalidation upon email removal from approval list
Signed-off-by: wanyaland
---
cla-backend-go/go.sum | 1 +
cla-backend-go/signatures/repository.go | 21 +++++++++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 9f4b4ebe8..a29cdd858 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -98,6 +98,7 @@ github.com/communitybridge/easycla v1.0.135 h1:Dvn8jX+7BAnpmA+jvdK0n5ajWP8SoH5vv
github.com/communitybridge/easycla v1.0.145 h1:ikhBSsOeEL2u3/EoyDsufh/j3HkjfFTiXAk1d61GoS8=
github.com/communitybridge/easycla v2.0.10+incompatible h1:6eRJ5fxrMxRZHBkg8piYo+zHTcSowMrP85nZXzp5mpA=
github.com/communitybridge/easycla v2.0.16+incompatible h1:I0hEApDh4IvlwRPyHV1LOsSYlSPbqBsGszjSTHwkdak=
+github.com/communitybridge/easycla v2.0.19+incompatible h1:HLaNt3jGDXPh3Au+rW/HKbJNkQf3daboVIrP9G6WYQ4=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 95ae6caa3..69e6c882a 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2113,6 +2113,27 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
repo.eventsService.LogEventWithContext(ctx, eventArgs)
+ // invalidate icla
+ log.WithFields(f).Debugf("invalidating icla for user: %s...", email)
+ claUser, userErr := repo.usersRepo.GetUserByEmail(email)
+ if userErr != nil {
+ log.WithFields(f).Debugf("error getting user by email: %s ", email)
+ }
+
+ if claUser != nil {
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
+ if iclaErr != nil {
+ log.WithFields(f).Debugf("unable to get icla signature for user: %s ", email)
+ }
+ if icla != nil {
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal ", utils.GetBestUsername(claManager), email)
+ err := repo.InvalidateProjectRecord(ctx, icla.SignatureID, note)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to invalidate record for user:%s ", email)
+ }
+ }
+ }
+
//update gerrit permissions
gerritUser, err := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
if err != nil || gerritUser == nil {
From da837871fedaf219d8aedbee8373955aec1f18b7 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 1 Apr 2021 09:18:25 -0700
Subject: [PATCH 0205/1276] Updated Logging + Added New Python Query (#2839)
- Updated logger functionName output
- Added python get_repository_models_by_repository_sfdc_id helper query
Signed-off-by: David Deal
---
.../v2/github_organizations/service.go | 6 +++---
cla-backend/cla/models/dynamo_models.py | 17 +++++++++++++----
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index c45350976..c4b523556 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -73,7 +73,7 @@ const (
func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganizations",
+ "functionName": "v2.github_organizations.service.GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -251,7 +251,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
func (s service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
- "functionName": "AddGitHubOrganization",
+ "functionName": "v2.github_organizations.service.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"autoEnabled": utils.BoolValue(input.AutoEnabled),
@@ -300,7 +300,7 @@ func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID strin
func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
- "functionName": "DeleteGitHubOrganization",
+ "functionName": "v2.github_organizations.service.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"githubOrgName": githubOrgName,
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index e89bf2e26..4ef900489 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -1990,14 +1990,14 @@ class Meta:
repository_url = UnicodeAttribute()
repository_organization_name = UnicodeAttribute()
repository_external_id = UnicodeAttribute(null=True)
- repository_project_index = ProjectRepositoryIndex()
- project_sfid_repository_index = ProjectSFIDRepositoryIndex()
repository_sfdc_id = UnicodeAttribute(null=True)
project_sfid = UnicodeAttribute(null=True)
- repository_external_index = ExternalRepositoryIndex()
- repository_sfdc_index = SFDCRepositoryIndex()
enabled = BooleanAttribute(default=False)
note = UnicodeAttribute(null=True)
+ repository_external_index = ExternalRepositoryIndex()
+ repository_project_index = ProjectRepositoryIndex()
+ project_sfid_repository_index = ProjectSFIDRepositoryIndex()
+ repository_sfdc_index = SFDCRepositoryIndex()
class Repository(model_interfaces.Repository):
@@ -2062,6 +2062,15 @@ def get_repository_by_project_sfid(self, project_sfid) -> List[dict]:
repositories.append(repository.to_dict())
return repositories
+ def get_repository_models_by_repository_sfdc_id(self, project_sfid) -> List[Repository]:
+ repository_generator = self.model.repository_sfdc_index.query(project_sfid)
+ repositories = []
+ for repository_model in repository_generator:
+ repository = Repository()
+ repository.model = repository_model
+ repositories.append(repository)
+ return repositories
+
def delete(self):
self.model.delete()
From 11a432c436b26e30703abb914d3a7a6ddc8285b5 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Thu, 1 Apr 2021 19:38:23 +0300
Subject: [PATCH 0206/1276] adding pagination to icla signatures list (#2844)
Signed-off-by: makkalot
---
cla-backend-go/signatures/repository.go | 233 ++++++++++++------
cla-backend-go/signatures/service.go | 6 +-
cla-backend-go/swagger/cla.v2.yaml | 2 +
.../swagger/common/icla-signatures.yaml | 12 +
cla-backend-go/v2/signatures/handlers.go | 2 +-
cla-backend-go/v2/signatures/service.go | 8 +-
6 files changed, 180 insertions(+), 83 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 69e6c882a..040cd8564 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -10,6 +10,7 @@ import (
"sort"
"strings"
"sync"
+ "time"
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/sirupsen/logrus"
@@ -50,7 +51,9 @@ const (
SignatureReferenceIndex = "reference-signature-index"
SignatureReferenceSearchIndex = "reference-signature-search-index"
- HugePageSize = 10000
+ HugePageSize = 10000
+ DefaultPageSize = 10
+ BigPageSize = 100
)
// SignatureRepository interface defines the functions for the github whitelist service
@@ -84,10 +87,15 @@ type SignatureRepository interface {
AddUsersDetails(ctx context.Context, signatureID string, userID string) error
AddSignedOn(ctx context.Context, signatureID string) error
- GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error)
+ GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error)
}
+type iclaSignatureWithDetails struct {
+ IclaSignature *models.IclaSignature
+ SignatureReferenceID string
+}
+
// repository data model
type repository struct {
stage string
@@ -2393,7 +2401,7 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
// Get ICLAs
log.WithFields(f).Debug("getting icla records... ")
- iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil)
+ iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil, 0, "")
if err != nil {
log.WithFields(f).Warn("unable to get iclas")
}
@@ -2730,7 +2738,7 @@ func (repo repository) AddSignedOn(ctx context.Context, signatureID string) erro
return nil
}
-func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error) {
+func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
"functionName": "GetClaGroupICLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -2738,12 +2746,7 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
"searchTerm": utils.StringValue(searchTerm),
}
- //sortKeyPrefix := fmt.Sprintf("%s#%v#%v", utils.ClaTypeICLA, true, true)
- // This is the key we want to match
- //condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
- // And(expression.Key("sigtype_signed_approved_id").BeginsWith(sortKeyPrefix))
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
-
filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
@@ -2770,93 +2773,168 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
TableName: aws.String(repo.signatureTableName),
- //IndexName: aws.String(SignatureProjectIDSigTypeSignedApprovedIDIndex),
- IndexName: aws.String(SignatureProjectIDIndex),
- Limit: aws.Int64(HugePageSize),
+ IndexName: aws.String(SignatureProjectIDIndex),
+ }
+
+ if pageSize == 0 {
+ pageSize = DefaultPageSize
+ }
+
+ if pageSize > BigPageSize {
+ pageSize = BigPageSize
}
+
+ queryInput.Limit = &pageSize
+
if searchTerm != nil {
searchTerm = aws.String(strings.ToLower(*searchTerm))
}
- type IclaSignatureWithDetails struct {
- IclaSignature *models.IclaSignature
- SignatureReferenceID string
+ // If we have the next key, set the exclusive start key value
+ if nextKey != "" {
+ log.WithFields(f).Debugf("Received a nextKey, value: %s", nextKey)
+ // The primary key of the first item that this operation will evaluate.
+ // and the query key (if not the same)
+ queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
+ "signature_id": {
+ S: aws.String(nextKey),
+ },
+ "signature_project_id": {
+ S: aws.String(claGroupID),
+ },
+ }
}
- var intermediateResponse []*IclaSignatureWithDetails
- for {
+ var intermediateResponse []*iclaSignatureWithDetails
+ var lastEvaluatedKey string
+ // Loop until we have all the records
+ for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
- results, queryErr := repo.dynamoDBClient.Query(queryInput)
- if queryErr != nil {
- log.WithFields(f).Warnf("error retrieving icla signatures for project: %s, error: %v", claGroupID, queryErr)
- return nil, queryErr
+ log.WithFields(f).Debugf("Running list icla signatures query using queryInput: %+v", queryInput)
+ results, errQuery := repo.dynamoDBClient.Query(queryInput)
+ if errQuery != nil {
+ log.WithFields(f).Warnf("error retrieving icla signatures for project: %s , error: %v",
+ claGroupID, errQuery)
+ return nil, errQuery
}
var dbSignatures []ItemSignature
- err := dynamodbattribute.UnmarshalListOfMaps(results.Items, &dbSignatures)
- if err != nil {
+ unmarshallError := dynamodbattribute.UnmarshalListOfMaps(results.Items, &dbSignatures)
+ if unmarshallError != nil {
log.WithFields(f).Warnf("error unmarshalling icla signatures from database for cla group: %s, error: %v",
- claGroupID, err)
- return nil, err
+ claGroupID, unmarshallError)
+ return nil, unmarshallError
}
- for _, sig := range dbSignatures {
- if searchTerm != nil {
- if !strings.Contains(sig.SignatureReferenceNameLower, *searchTerm) {
- continue
- }
+ intermediateResponse = append(intermediateResponse, repo.getIntermediateICLAResponse(f, dbSignatures, searchTerm)...)
+
+ log.WithFields(f).Debugf("LastEvaluatedKey: %+v", results.LastEvaluatedKey["signature_id"])
+ if results.LastEvaluatedKey["signature_id"] != nil {
+ lastEvaluatedKey = *results.LastEvaluatedKey["signature_id"].S
+ queryInput.ExclusiveStartKey = results.LastEvaluatedKey
+ } else {
+ lastEvaluatedKey = ""
+ }
+
+ if int64(len(intermediateResponse)) >= pageSize {
+ break
+ }
+ }
+
+ // How many total records do we have - may not be up-to-date as this value is updated only periodically
+ describeTableInput := &dynamodb.DescribeTableInput{
+ TableName: &repo.signatureTableName,
+ }
+ describeTableResult, err := repo.dynamoDBClient.DescribeTable(describeTableInput)
+ if err != nil {
+ log.WithFields(f).Warnf("error retrieving total record count for project: %s, error: %v", claGroupID, err)
+ return nil, err
+ }
+ // Meta-data for the response
+ totalCount := *describeTableResult.Table.ItemCount
+
+ if int64(len(intermediateResponse)) > pageSize {
+ intermediateResponse = intermediateResponse[0:pageSize]
+ lastEvaluatedKey = intermediateResponse[pageSize-1].IclaSignature.SignatureID
+ }
+
+ // Append all the responses to our list
+ out := &models.IclaSignatures{
+ LastKeyScanned: lastEvaluatedKey,
+ PageSize: pageSize,
+ ResultCount: int64(len(intermediateResponse)),
+ TotalCount: totalCount,
+ }
+
+ iclaSignatures, err := repo.addAdditionalICLAMetaData(f, intermediateResponse)
+ if err != nil {
+ return nil, err
+ }
+
+ out.List = iclaSignatures
+ return out, nil
+}
+
+func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures []ItemSignature, searchTerm *string) []*iclaSignatureWithDetails {
+ var intermediateResponse []*iclaSignatureWithDetails
+
+ for _, sig := range dbSignatures {
+ if searchTerm != nil {
+ if !strings.Contains(sig.SignatureReferenceNameLower, *searchTerm) {
+ continue
}
+ }
- // Set the signed date/time
- var sigSignedTime string
- // Use the user docusign date signed value if it is present - older signatures do not have this
- if sig.UserDocusignDateSigned != "" {
- // Put the date into a standard format
- t, err := utils.ParseDateTime(sig.UserDocusignDateSigned)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
- } else {
- sigSignedTime = utils.TimeToString(t)
- }
+ // Set the signed date/time
+ var sigSignedTime string
+ // Use the user docusign date signed value if it is present - older signatures do not have this
+ if sig.UserDocusignDateSigned != "" {
+ // Put the date into a standard format
+ t, err := utils.ParseDateTime(sig.UserDocusignDateSigned)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
} else {
- // Put the date into a standard format
- t, err := utils.ParseDateTime(sig.DateCreated)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
- } else {
- sigSignedTime = utils.TimeToString(t)
- }
+ sigSignedTime = utils.TimeToString(t)
+ }
+ } else {
+ // Put the date into a standard format
+ t, err := utils.ParseDateTime(sig.DateCreated)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
+ } else {
+ sigSignedTime = utils.TimeToString(t)
}
-
- intermediateResponse = append(intermediateResponse, &IclaSignatureWithDetails{
- IclaSignature: &models.IclaSignature{
- GithubUsername: sig.UserGithubUsername,
- LfUsername: sig.UserLFUsername,
- SignatureID: sig.SignatureID,
- UserEmail: sig.UserEmail,
- UserName: sig.UserName,
- SignedOn: sigSignedTime,
- UserDocusignName: sig.UserDocusignName,
- UserDocusignDateSigned: sigSignedTime,
- SignatureModified: sig.DateModified,
- },
- SignatureReferenceID: sig.SignatureReferenceID,
- })
}
- if len(results.LastEvaluatedKey) == 0 {
- break
- }
- queryInput.ExclusiveStartKey = results.LastEvaluatedKey
- log.WithFields(f).Debug("querying next page")
+ intermediateResponse = append(intermediateResponse, &iclaSignatureWithDetails{
+ IclaSignature: &models.IclaSignature{
+ GithubUsername: sig.UserGithubUsername,
+ LfUsername: sig.UserLFUsername,
+ SignatureID: sig.SignatureID,
+ UserEmail: sig.UserEmail,
+ UserName: sig.UserName,
+ SignedOn: sigSignedTime,
+ UserDocusignName: sig.UserDocusignName,
+ UserDocusignDateSigned: sigSignedTime,
+ SignatureModified: sig.DateModified,
+ },
+ SignatureReferenceID: sig.SignatureReferenceID,
+ })
}
+ return intermediateResponse
+}
+
+func (repo repository) addAdditionalICLAMetaData(f logrus.Fields, intermediateResponse []*iclaSignatureWithDetails) ([]*models.IclaSignature, error) {
log.WithFields(f).Debugf("Adding additional meta-data for %d records...", len(intermediateResponse))
// For some older ICLA signatures, we are missing the user's info, but we have their internal ID - let's look up those values before returning
responseChannel := make(chan *models.IclaSignature)
- for _, iclaSignatureWithDetails := range intermediateResponse {
- go func(iclaSignatureWithDetails *IclaSignatureWithDetails) {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
+ defer cancel()
+
+ for _, iclaDetails := range intermediateResponse {
+ go func(iclaSignatureWithDetails *iclaSignatureWithDetails) {
userModel, userLookupErr := repo.usersRepo.GetUser(iclaSignatureWithDetails.SignatureReferenceID)
if userLookupErr != nil || userModel == nil {
log.WithFields(f).WithError(userLookupErr).Warnf("unable to lookup user with id: %s", iclaSignatureWithDetails.SignatureReferenceID)
@@ -2883,16 +2961,21 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
}
responseChannel <- iclaSignatureWithDetails.IclaSignature
- }(iclaSignatureWithDetails)
+ }(iclaDetails)
}
- // Append all the responses to our list
- out := &models.IclaSignatures{List: make([]*models.IclaSignature, 0)}
+ var finalResults []*models.IclaSignature
for i := 0; i < len(intermediateResponse); i++ {
- out.List = append(out.List, <-responseChannel)
+ select {
+ case result := <-responseChannel:
+ finalResults = append(finalResults, result)
+ case <-ctx.Done():
+ log.WithError(ctx.Err()).Warnf("timeout during adding additional meta to icla signatures")
+ return nil, ctx.Err()
+ }
}
- return out, nil
+ return finalResults, nil
}
func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error) {
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 0a384ef4f..86455cf77 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -53,7 +53,7 @@ type SignatureService interface {
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
RemoveCLAManager(ctx context.Context, ignatureID, claManagerID string) (*models.Signature, error)
- GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error)
+ GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
GetClaGroupCCLASignatures(ctx context.Context, claGroupID string) (*models.Signatures, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error)
}
@@ -814,8 +814,8 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
}
}
-func (s service) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error) {
- return s.repo.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm)
+func (s service) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
+ return s.repo.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, pageSize, nextKey)
}
func (s service) GetClaGroupCCLASignatures(ctx context.Context, claGroupID string) (*models.Signatures, error) {
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index fb5d6291e..8b8e30fe4 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1617,6 +1617,8 @@ paths:
- $ref: "#/parameters/path-claGroupID"
- $ref: '#/parameters/searchTerm'
- $ref: '#/parameters/sortOrder'
+ - $ref: '#/parameters/pageSize'
+ - $ref: '#/parameters/nextKey'
responses:
'200':
description: 'Success'
diff --git a/cla-backend-go/swagger/common/icla-signatures.yaml b/cla-backend-go/swagger/common/icla-signatures.yaml
index e337d4a4e..1d614b916 100644
--- a/cla-backend-go/swagger/common/icla-signatures.yaml
+++ b/cla-backend-go/swagger/common/icla-signatures.yaml
@@ -3,6 +3,18 @@
type: object
properties:
+ lastKeyScanned:
+ type: string
+ pageSize:
+ type: integer
+ resultCount:
+ type: integer
+ format: int64
+ x-omitempty: false
+ totalCount:
+ type: integer
+ format: int64
+ x-omitempty: false
list:
type: array
items:
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 1181cda1c..b44d18596 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -840,7 +840,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for ICLA signatures...")
- results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm)
+ results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm, *params.PageSize, *params.NextKey)
if err != nil {
msg := fmt.Sprintf("problem loading ICLA signatures by CLA Group ID search term: %s", aws.StringValue(params.SearchTerm))
log.WithFields(f).WithError(err).Warn(msg)
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 3d04f4075..a60dd4039 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -56,7 +56,7 @@ type Service interface {
GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error)
GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
- GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error)
+ GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companySFID string, searchTerm *string) (*models.CorporateContributorList, error)
GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error)
@@ -132,7 +132,7 @@ func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGro
func (s service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
var b bytes.Buffer
- result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, nil)
+ result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, nil, 0, "")
if err != nil {
return nil, err
}
@@ -167,7 +167,7 @@ func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID str
return b.Bytes(), nil
}
-func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string) (*models.IclaSignatures, error) {
+func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
"functionName": "v2.signatures.service.GetProjectIclaSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -176,7 +176,7 @@ func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string
}
var out models.IclaSignatures
- result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm)
+ result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, pageSize, nextKey)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load ICLA signatures using the specified search parameters")
return nil, err
From 2b97182ac4bd95126269c62b93ef17344e748cbf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 1 Apr 2021 10:02:59 -0700
Subject: [PATCH 0207/1276] Bump y18n from 4.0.0 to 4.0.1 in /cla-backend
(#2835)
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 1ba44bbbb..eb2ab3c37 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -6382,9 +6382,9 @@ xtend@^4.0.0:
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
- integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
+ integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
yallist@^4.0.0:
version "4.0.0"
From 68f88622715ac534ad6bcef8d107c0c9ccffa0c6 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 1 Apr 2021 20:45:29 +0300
Subject: [PATCH 0208/1276] [#2838] Bug/Get Project GH Orgs (#2845)
- Get GH orgs for Parent/Project or both use cases
Signed-off-by: wanyaland
---
.../github_organizations/service.go | 47 ++++++++++++++++---
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index 3aa8fb11c..9f1b38d47 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -77,17 +77,21 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
"projectSFID": projectSFID,
}
- gitHubOrgModels, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
+ // track githubOrgs based on parent/child anchor
+ var gitHubOrgModels = models.GithubOrganizations{}
+ var githubOrgs = make([]*models.GithubOrganization, 0)
+
+ projectGithubModels, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
if err != nil {
log.WithFields(f).Warnf("problem fetching github organizations by projectSFID, error: %+v", err)
return nil, err
}
- if len(gitHubOrgModels.List) >= 0 {
- return gitHubOrgModels, err
+ if len(projectGithubModels.List) >= 0 {
+ githubOrgs = append(githubOrgs, projectGithubModels.List...)
}
- log.WithFields(f).Debug("unable to find github organizations by projectSFID - searching by parent...")
+ log.WithFields(f).Debug("factoring github orgs for parent...")
// Lookup the parent
parentProjectSFID, projErr := v2ProjectService.GetClient().GetParentProject(projectSFID)
if projErr != nil {
@@ -97,12 +101,25 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
if parentProjectSFID != projectSFID {
log.WithFields(f).Debugf("searching github organization by parent SFID: %s", parentProjectSFID)
- return s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
+ parentGithubModels, parentErr := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
+ if parentErr != nil {
+ log.WithFields(f).Warnf("problem fetching github organizations by paarent projectSFID: %s , error: %+v", parentProjectSFID, err)
+ return nil, parentErr
+ }
+
+ if len(parentGithubModels.List) >= 0 {
+ githubOrgs = append(githubOrgs, parentGithubModels.List...)
+ }
}
log.WithFields(f).Debugf("no parent or parent is %s or %s - search criteria exhausted",
utils.TheLinuxFoundation, utils.TheLinuxFoundation)
- return gitHubOrgModels, err
+
+ gitHubOrgModels.List = githubOrgs
+ // Remove potential duplicates
+ s.removeDuplicateGHOrgs(gitHubOrgModels.List)
+
+ return &gitHubOrgModels, err
}
func (s service) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
@@ -166,3 +183,21 @@ func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
return s.repo.DeleteGithubOrganization(ctx, projectSFID, githubOrgName)
}
+
+// filter ghOrgs duplicates
+func (s service) removeDuplicateGHOrgs(input []*models.GithubOrganization) []*models.GithubOrganization {
+ if input == nil {
+ return nil
+ }
+ keys := make(map[string]bool)
+
+ output := []*models.GithubOrganization{}
+ for _, ghOrg := range input {
+ if _, value := keys[ghOrg.OrganizationName]; !value {
+ keys[ghOrg.OrganizationName] = true
+ output = append(output, ghOrg)
+ }
+ }
+
+ return output
+}
From 726960534fa28bc153fe3dbf2bc2d243b2f56d22 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Thu, 1 Apr 2021 21:27:07 +0300
Subject: [PATCH 0209/1276] some defensive code on pagination parameters
(#2846)
Signed-off-by: makkalot
---
cla-backend-go/signatures/repository.go | 14 --------------
cla-backend-go/swagger/common/icla-signatures.yaml | 4 ----
cla-backend-go/v2/signatures/handlers.go | 14 +++++++++++++-
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 040cd8564..0f8db8bdd 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2810,7 +2810,6 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
- log.WithFields(f).Debugf("Running list icla signatures query using queryInput: %+v", queryInput)
results, errQuery := repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
log.WithFields(f).Warnf("error retrieving icla signatures for project: %s , error: %v",
@@ -2842,18 +2841,6 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
}
}
- // How many total records do we have - may not be up-to-date as this value is updated only periodically
- describeTableInput := &dynamodb.DescribeTableInput{
- TableName: &repo.signatureTableName,
- }
- describeTableResult, err := repo.dynamoDBClient.DescribeTable(describeTableInput)
- if err != nil {
- log.WithFields(f).Warnf("error retrieving total record count for project: %s, error: %v", claGroupID, err)
- return nil, err
- }
- // Meta-data for the response
- totalCount := *describeTableResult.Table.ItemCount
-
if int64(len(intermediateResponse)) > pageSize {
intermediateResponse = intermediateResponse[0:pageSize]
lastEvaluatedKey = intermediateResponse[pageSize-1].IclaSignature.SignatureID
@@ -2864,7 +2851,6 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
LastKeyScanned: lastEvaluatedKey,
PageSize: pageSize,
ResultCount: int64(len(intermediateResponse)),
- TotalCount: totalCount,
}
iclaSignatures, err := repo.addAdditionalICLAMetaData(f, intermediateResponse)
diff --git a/cla-backend-go/swagger/common/icla-signatures.yaml b/cla-backend-go/swagger/common/icla-signatures.yaml
index 1d614b916..b967732ac 100644
--- a/cla-backend-go/swagger/common/icla-signatures.yaml
+++ b/cla-backend-go/swagger/common/icla-signatures.yaml
@@ -11,10 +11,6 @@ properties:
type: integer
format: int64
x-omitempty: false
- totalCount:
- type: integer
- format: int64
- x-omitempty: false
list:
type: array
items:
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index b44d18596..2ee9830b7 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -840,7 +840,19 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for ICLA signatures...")
- results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm, *params.PageSize, *params.NextKey)
+
+ var pageSize int64
+ var nextKey string
+
+ if params.PageSize != nil {
+ pageSize = *params.PageSize
+ }
+
+ if params.NextKey != nil {
+ nextKey = *params.NextKey
+ }
+
+ results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm, pageSize, nextKey)
if err != nil {
msg := fmt.Sprintf("problem loading ICLA signatures by CLA Group ID search term: %s", aws.StringValue(params.SearchTerm))
log.WithFields(f).WithError(err).Warn(msg)
From 54ab0cc802ce490b4940d63f1c13dca2cc6874b8 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Sat, 3 Apr 2021 01:40:53 +0300
Subject: [PATCH 0210/1276] [#2792] Bug/Domain Approval Removal (#2848)
- Invalidate icla/ecla resolved by searching email against domain
Signed-off-by: nickmango
---
cla-backend-go/signatures/repository.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 0f8db8bdd..149cf677b 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2518,7 +2518,9 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
if approvalList.Criteria == utils.EmailDomainCriteria {
// Handle Domains
- if utils.StringInSlice(getBestEmail(user), approvalList.DomainApprovals) {
+ email := getBestEmail(user)
+ domain := strings.Split(email, "@")[1]
+ if utils.StringInSlice(domain, approvalList.DomainApprovals) {
if !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) && !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) {
//Invalidate record
note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.EmailDomainCriteria)
From 865f7e89389f6fa986f305642a776b064fdf679c Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Mon, 5 Apr 2021 18:06:49 +0300
Subject: [PATCH 0211/1276] [#2838] Bug/Project Github Orgs (#2849)
- Leveraged v1 getorgs service that caters for duplicates and returns right gh-orgs
Signed-off-by: Harold Wanyama
Co-authored-by: Harold Wanyama
---
cla-backend-go/cmd/server.go | 2 +-
.../v2/github_organizations/service.go | 25 +++++++++----------
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index a7d5a4bf4..08761f72c 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -289,7 +289,7 @@ func server(localMode bool) http.Handler {
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
- v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
+ v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo, githubOrganizationsService)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, eventsService, autoEnableService)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index c4b523556..730a919bb 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -48,15 +48,17 @@ type Service interface {
type service struct {
repo v1GithubOrg.Repository
ghRepository v1Repositories.Repository
+ ghService v1GithubOrg.Service
projectsCLAGroupService projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo v1GithubOrg.Repository, ghRepository v1Repositories.Repository, projectsCLAGroupService projects_cla_groups.Repository) Service {
+func NewService(repo v1GithubOrg.Repository, ghRepository v1Repositories.Repository, projectsCLAGroupService projects_cla_groups.Repository, ghService v1GithubOrg.Service) Service {
return service{
repo: repo,
ghRepository: ghRepository,
projectsCLAGroupService: projectsCLAGroupService,
+ ghService: ghService,
}
}
@@ -78,6 +80,14 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
"projectSFID": projectSFID,
}
+ orgs, err := s.ghService.GetGithubOrganizations(ctx, projectSFID)
+ // log.WithFields(f).Debug("loading github organization details by projectSFID...")
+ //orgs, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading github organizations from the project service")
+ return nil, err
+ }
+
psc := v2ProjectService.GetClient()
log.WithFields(f).Debug("loading project details from the project service...")
projectServiceRecord, err := psc.GetProject(projectSFID)
@@ -89,9 +99,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
log.Debugf("project record: %+v ", projectServiceRecord)
var parentProjectSFID string
- if (projectServiceRecord.Foundation != nil &&
- (projectServiceRecord.Foundation.Name == utils.TheLinuxFoundation || projectServiceRecord.Foundation.Name == utils.LFProjectsLLC)) ||
- projectServiceRecord.Parent == "" {
+ if utils.IsProjectHasRootParent(projectServiceRecord) {
parentProjectSFID = projectSFID
} else {
parentProjectSFID = projectServiceRecord.Parent
@@ -99,15 +107,6 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
- log.WithFields(f).Debug("loading github organization details by parentProjectSFID...")
- orgs, err := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
- // log.WithFields(f).Debug("loading github organization details by projectSFID...")
- //orgs, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("problem loading github organizations from the project service")
- return nil, err
- }
-
out := &models.ProjectGithubOrganizations{
List: make([]*models.ProjectGithubOrganization, 0),
}
From 4119b324f9af79db46b55d6ed2683737e9e50fd1 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 5 Apr 2021 19:53:13 +0300
Subject: [PATCH 0212/1276] adding more information to cla_group.updated event
log summary and (#2851)
details method
Signed-off-by: makkalot
---
cla-backend-go/events/event_data.go | 49 ++++++++--
cla-backend-go/events/event_data_test.go | 110 +++++++++++++++++++++++
cla-backend-go/project/handlers.go | 15 +++-
cla-backend-go/v2/cla_groups/handlers.go | 15 +++-
cla-backend-go/v2/project/handlers.go | 18 +++-
5 files changed, 190 insertions(+), 17 deletions(-)
create mode 100644 cla-backend-go/events/event_data_test.go
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 75e7ab1f7..7c9858d40 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -330,8 +330,10 @@ type CLAGroupCreatedEventData struct{}
// CLAGroupUpdatedEventData . . .
type CLAGroupUpdatedEventData struct {
- ClaGroupName string
- ClaGroupDescription string
+ NewClaGroupName string
+ NewClaGroupDescription string
+ OldClaGroupName string
+ OldClaGroupDescription string
}
// CLAGroupDeletedEventData . . .
@@ -676,8 +678,23 @@ func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (s
// GetEventDetailsString . . .
func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Group ID: %s was updated by: %s with Name: %s, Description: %s.",
- args.ProjectID, args.UserName, ed.ClaGroupName, ed.ClaGroupDescription)
+ var nameUpdated bool
+
+ data := fmt.Sprintf("CLA Group ID: %s was updated by: %s", args.ProjectID, args.UserName)
+ if ed.NewClaGroupName != ""{
+ data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
+ nameUpdated = true
+ }
+
+ if ed.NewClaGroupDescription != ""{
+ if nameUpdated{
+ data = data + ","
+ }else{
+ data = data + " with"
+ }
+ data = fmt.Sprintf("%s Description from : %s to : %s", data, ed.OldClaGroupDescription, ed.NewClaGroupDescription)
+ }
+ data = data + "."
return data, true
}
@@ -1457,7 +1474,29 @@ func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString . . .
func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was updated by the user %s.", args.ProjectName, args.UserName)
+ var nameUpdated, descriptionUpdated bool
+
+ message := "The CLA Group"
+ if ed.NewClaGroupName != ""{
+ message = message + " name was updated to : " + ed.NewClaGroupName
+ nameUpdated = true
+ }
+
+ if ed.NewClaGroupDescription != ""{
+ descriptionUpdated = true
+ if nameUpdated{
+ message = message + " and the description was updated to : " + ed.NewClaGroupDescription
+ }else{
+ message = message + " description was updated to : " + ed.NewClaGroupDescription
+ }
+ }
+
+ //shouldn't happen
+ if !nameUpdated && !descriptionUpdated{
+ message = message + " was updated"
+ }
+
+ data := fmt.Sprintf("%s by the user %s.", message, args.UserName)
return data, true
}
diff --git a/cla-backend-go/events/event_data_test.go b/cla-backend-go/events/event_data_test.go
new file mode 100644
index 000000000..76c525572
--- /dev/null
+++ b/cla-backend-go/events/event_data_test.go
@@ -0,0 +1,110 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package events
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ testUser = "john"
+)
+
+func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
+
+ testCases := []struct {
+ name string
+ eventData *CLAGroupUpdatedEventData
+ summaryStr string
+ }{
+ {
+ name: "empty",
+ eventData: &CLAGroupUpdatedEventData{},
+ summaryStr: "The CLA Group was updated by the user john.",
+ },
+ {
+ name: "only name updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ },
+ summaryStr: "The CLA Group name was updated to : updatedNameValue by the user john.",
+ },
+ {
+ name: "only description updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupDescription: "updatedDescriptionValue",
+ },
+ summaryStr: "The CLA Group description was updated to : updatedDescriptionValue by the user john.",
+ },
+ {
+ name: "name and description updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ NewClaGroupDescription: "updatedDescriptionValue",
+ },
+ summaryStr: "The CLA Group name was updated to : updatedNameValue and the description was updated to : updatedDescriptionValue by the user john.",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ summary, _ := tc.eventData.GetEventSummaryString(&LogEventArgs{UserName: testUser})
+ assert.Equal(tt, tc.summaryStr, summary)
+ })
+ }
+}
+
+func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
+ projectID := "projectIDValue"
+
+ testCases := []struct {
+ name string
+ eventData *CLAGroupUpdatedEventData
+ detailStr string
+ }{
+ {
+ name: "empty",
+ eventData: &CLAGroupUpdatedEventData{},
+ detailStr: "CLA Group ID: projectIDValue was updated by: john.",
+ },
+ {
+ name: "only name updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ OldClaGroupName: "oldNameValue",
+ },
+ detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue.",
+ },
+ {
+ name: "only description updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupDescription: "updatedDescriptionValue",
+ OldClaGroupDescription: "oldDescriptionValue",
+ },
+ detailStr: "CLA Group ID: projectIDValue was updated by: john with Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ },
+ {
+ name: "name and description updated",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ OldClaGroupName: "oldNameValue",
+ NewClaGroupDescription: "updatedDescriptionValue",
+ OldClaGroupDescription: "oldDescriptionValue",
+ },
+ detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue, Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ summary, _ := tc.eventData.GetEventDetailsString(&LogEventArgs{
+ UserName: testUser,
+ ProjectID: projectID,
+ })
+ assert.Equal(tt, tc.detailStr, summary)
+ })
+ }
+}
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index 35f9acf67..32cb774ab 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -282,7 +282,7 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
reqID := utils.GetRequestID(projectParams.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- exitingModel, getErr := service.GetCLAGroupByID(ctx, projectParams.Body.ProjectID)
+ existingModel, getErr := service.GetCLAGroupByID(ctx, projectParams.Body.ProjectID)
if getErr != nil {
msg := fmt.Sprintf("Error querying the project by ID, error: %+v", getErr)
log.Warnf("Update Project Failed - %s", msg)
@@ -293,7 +293,7 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
}
// If the project with the same name exists...
- if exitingModel == nil {
+ if existingModel == nil {
msg := fmt.Sprintf("unable to locate project with ID: %s", projectParams.Body.ProjectID)
log.Warn(msg)
return project.NewUpdateProjectNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
@@ -302,6 +302,10 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
})
}
+ var oldCLAGroupName, oldCLAGroupDescription string
+ oldCLAGroupName = existingModel.ProjectName
+ oldCLAGroupDescription = existingModel.ProjectDescription
+
claGroupModel, err := service.UpdateCLAGroup(ctx, &projectParams.Body)
if err != nil {
if err == ErrProjectDoesNotExist {
@@ -318,8 +322,11 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
EventData: &events.CLAGroupUpdatedEventData{
- ClaGroupName: projectParams.Body.ProjectName,
- ClaGroupDescription: projectParams.Body.ProjectDescription,
+ NewClaGroupName: projectParams.Body.ProjectName,
+ NewClaGroupDescription: projectParams.Body.ProjectDescription,
+
+ OldClaGroupName: oldCLAGroupName,
+ OldClaGroupDescription: oldCLAGroupDescription,
},
})
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index a6e97d184..3e2475194 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -135,6 +135,10 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("unable to update the CLA Group Name or Description - values are the same for CLA Group ID: %s", params.ClaGroupID)))
}
+ var oldCLAGroupName, oldCLAGroupDescription string
+ oldCLAGroupName = claGroupModel.ProjectName
+ oldCLAGroupDescription = claGroupModel.ProjectDescription
+
claGroup, err := service.UpdateCLAGroup(ctx, authUser, claGroupModel, params.Body, utils.StringValue(params.XUSERNAME))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to update the CLA Group Name and/or Description - update failed")
@@ -148,8 +152,11 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ProjectID: claGroup.ClaGroupID,
LfUsername: authUser.UserName,
EventData: &events.CLAGroupUpdatedEventData{
- ClaGroupName: params.Body.ClaGroupName,
- ClaGroupDescription: params.Body.ClaGroupDescription,
+ NewClaGroupName: params.Body.ClaGroupName,
+ NewClaGroupDescription: params.Body.ClaGroupDescription,
+
+ OldClaGroupName: oldCLAGroupName,
+ OldClaGroupDescription: oldCLAGroupDescription,
},
})
@@ -358,8 +365,8 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ClaGroupModel: cg,
LfUsername: authUser.UserName,
EventData: &events.CLAGroupUpdatedEventData{
- ClaGroupName: cg.ProjectName,
- ClaGroupDescription: cg.ProjectDescription,
+ OldClaGroupName: cg.ProjectName,
+ OldClaGroupDescription: cg.ProjectDescription,
},
})
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 6fa24ff72..4bc2ba68d 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -239,14 +239,24 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewUpdateProjectBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
+ eventData := &events.CLAGroupUpdatedEventData{
+ OldClaGroupName: claGroupModel.ProjectName,
+ OldClaGroupDescription: claGroupModel.ProjectDescription,
+ }
+
+ if in.ProjectName != "" {
+ eventData.NewClaGroupName = in.ProjectName
+ }
+
+ if in.ProjectDescription != "" {
+ eventData.NewClaGroupDescription = in.ProjectDescription
+ }
+
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLAGroupUpdated,
ClaGroupModel: claGroupModel,
LfUsername: user.UserName,
- EventData: &events.CLAGroupUpdatedEventData{
- ClaGroupName: claGroupModel.ProjectName,
- ClaGroupDescription: claGroupModel.ProjectDescription,
- },
+ EventData: eventData,
})
result, err := v2ProjectModel(claGroupModel)
From 1651eb90d3adbf0e9a5dcc771187431197f4a140 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Mon, 5 Apr 2021 23:14:54 +0300
Subject: [PATCH 0213/1276] [#2842] Feature/Invalidation Email alert (#2852)
Co-authored-by: David Deal
---
cla-backend-go/signatures/email.go | 23 ++++++++
cla-backend-go/signatures/models.go | 1 +
cla-backend-go/signatures/repository.go | 75 ++++++++++++++++++++-----
cla-backend-go/tests/signatures_test.go | 29 ++++++++++
cla-backend-go/utils/email.go | 21 +++++++
5 files changed, 135 insertions(+), 14 deletions(-)
create mode 100644 cla-backend-go/signatures/email.go
create mode 100644 cla-backend-go/tests/signatures_test.go
diff --git a/cla-backend-go/signatures/email.go b/cla-backend-go/signatures/email.go
new file mode 100644
index 000000000..7470f236c
--- /dev/null
+++ b/cla-backend-go/signatures/email.go
@@ -0,0 +1,23 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package signatures
+
+//InvalidateSignatureTemplateParams representing params when invalidating icla/ecla
+type InvalidateSignatureTemplateParams struct {
+ RecipientName string
+ ClaType string
+ ClaManager string
+ RemovalCriteria string
+}
+
+const (
+ //InvalidateSignatureTemplateName is email template for InvalidateSignatureTemplate
+ InvalidateSignatureTemplateName = "InvalidateSignatureTemplate"
+ //InvalidateSignatureTemplate ...
+ InvalidateSignatureTemplate = `
+
Hello {{.RecipientName}}
+
This is a notification email from EasyCLA regarding approval list removal for {{.RemovalCriteria}}
+
Due to this change your individual contribution authorization has been removed and you will be blocked from making subsequent code contributions on behalf of this project.
+ `
+)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index aad7ee21e..2775ef8e9 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -24,6 +24,7 @@ type ApprovalList struct {
Action string
ClaGroupID string
CompanyID string
+ Version string
DomainApprovals []string
GHOrgApprovals []string
GitHubUsernameApprovals []string
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 149cf677b..de9bc08d7 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2087,6 +2087,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
for _, email := range params.RemoveEmailApprovalList {
go func(email string) {
defer wg.Done()
+ claUser, userErr := repo.usersRepo.GetUserByEmail(email)
+ if userErr != nil {
+ log.WithFields(f).Debugf("error getting user by email: %s ", email)
+ }
criteria := &ApprovalCriteria{
UserEmail: email,
}
@@ -2110,6 +2114,24 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
return
}
+
+ // update user by emai
+ // send email
+ if claUser != nil {
+ log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ _, err := utils.RenderTemplate(utils.V2, InvalidateSignatureTemplateName,
+ InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(claUser),
+ ClaType: utils.ClaTypeCCLA,
+ ClaManager: utils.GetBestUsername(claManager),
+ RemovalCriteria: approvalList.Criteria,
+ })
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
+ return
+ }
+ }
+
//Log Event
eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
SignatureID: signs.Signatures[0].SignatureID,
@@ -2122,11 +2144,6 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
repo.eventsService.LogEventWithContext(ctx, eventArgs)
// invalidate icla
- log.WithFields(f).Debugf("invalidating icla for user: %s...", email)
- claUser, userErr := repo.usersRepo.GetUserByEmail(email)
- if userErr != nil {
- log.WithFields(f).Debugf("error getting user by email: %s ", email)
- }
if claUser != nil {
icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
@@ -2195,6 +2212,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.GerritICLAECLAs = gerritICLAECLAs
approvalList.ClaGroupID = projectID
approvalList.CompanyID = companyID
+ approvalList.Version = utils.V2
invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
if invalidateErr != nil {
msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
@@ -2287,6 +2305,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Criteria = utils.GitHubOrgCriteria
approvalList.ApprovalList = params.RemoveGithubOrgApprovalList
approvalList.Action = utils.RemoveApprovals
+ approvalList.Version = utils.V2
// Get repositories by CLAGroup
repositories, err := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
if err != nil {
@@ -2424,11 +2443,25 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", signature)
return
}
- verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
+ user, verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
if verifyErr != nil {
log.WithFields(f).Warnf("unable to verify user: %s ", signature.SignatureReferenceID)
return
}
+ email := getBestEmail(user)
+ // send email
+ log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ _, emailErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
+ InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(user),
+ ClaType: utils.ClaTypeICLA,
+ ClaManager: utils.GetBestUsername(claManager),
+ RemovalCriteria: approvalList.Criteria,
+ })
+ if emailErr != nil {
+ log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
+ return
+ }
}(icla)
}
iclaWg.Wait()
@@ -2461,11 +2494,25 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", ecla)
return
}
- verifyErr := repo.verifyUserApprovals(ctx, ecla.SignatureReferenceID, ecla.SignatureID, claManager, approvalList)
+ user, verifyErr := repo.verifyUserApprovals(ctx, ecla.SignatureReferenceID, ecla.SignatureID, claManager, approvalList)
if verifyErr != nil {
log.WithFields(f).Warnf("unable to verify user: %s ", ecla.SignatureReferenceID)
return
}
+ email := getBestEmail(user)
+ // send email
+ log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ _, err := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
+ InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(user),
+ ClaType: utils.ClaTypeECLA,
+ ClaManager: utils.GetBestUsername(claManager),
+ RemovalCriteria: approvalList.Criteria,
+ })
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
+ return
+ }
}(ecla)
}
eclaWg.Wait()
@@ -2498,7 +2545,7 @@ func (repo repository) getGerritUserByEmail(ctx context.Context, email string, g
}
// verify UserApprovals checks user
-func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) error {
+func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) (*models.User, error) {
f := logrus.Fields{
"functionName": "verifyUserApprovals",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -2508,7 +2555,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
user, err := repo.usersRepo.GetUser(userID)
if err != nil {
log.WithFields(f).Warnf("unable to get user record for ID: %s ", userID)
- return err
+ return nil, err
}
authUser := auth.User{
@@ -2527,7 +2574,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
err := repo.InvalidateProjectRecord(ctx, signatureID, note)
if err != nil {
log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
- return err
+ return user, err
}
log.WithFields(f).Debugf("removing gerrit user:%s from claGroup: %s ...", user.LfUsername, approvalList.ClaGroupID)
@@ -2535,13 +2582,13 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
if iclaErr != nil {
msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
log.WithFields(f).Warn(msg)
- return iclaErr
+ return user, iclaErr
}
eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
if eclaErr != nil {
msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
log.WithFields(f).Warn(msg)
- return eclaErr
+ return user, eclaErr
}
}
}
@@ -2554,13 +2601,13 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
err := repo.InvalidateProjectRecord(ctx, signatureID, note)
if err != nil {
log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
- return err
+ return user, err
}
}
}
}
- return nil
+ return user, nil
}
// removeColumn is a helper function to remove a given column when we need to zero out the column value - typically the approval list
diff --git a/cla-backend-go/tests/signatures_test.go b/cla-backend-go/tests/signatures_test.go
new file mode 100644
index 000000000..46698ba77
--- /dev/null
+++ b/cla-backend-go/tests/signatures_test.go
@@ -0,0 +1,29 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+//TestInvalidateSignatureTemplate validates email sent when signature is invalidated
+func TestInvalidateSignatureTemplate(t *testing.T) {
+ params := signatures.InvalidateSignatureTemplateParams{
+ RecipientName: "TestUser",
+ ClaType: utils.ClaTypeICLA,
+ ClaManager: "claManager",
+ RemovalCriteria: "email removal",
+ }
+
+ result, err := utils.RenderTemplate(utils.V1, signatures.InvalidateSignatureTemplateName, signatures.InvalidateSignatureTemplate, params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello TestUser")
+ assert.Contains(t, result, "regarding approval list removal for email removal")
+ assert.Contains(t, result, "your signature record has been invalidated")
+
+}
diff --git a/cla-backend-go/utils/email.go b/cla-backend-go/utils/email.go
index bf3758f34..36047b891 100644
--- a/cla-backend-go/utils/email.go
+++ b/cla-backend-go/utils/email.go
@@ -4,7 +4,9 @@
package utils
import (
+ "bytes"
"errors"
+ "html/template"
"strings"
"github.com/sirupsen/logrus"
@@ -124,3 +126,22 @@ support.`
func GetEmailSignOffContent() string {
return `
EasyCLA Support Team
`
}
+
+// RenderTemplate renders the template for given template with given params
+func RenderTemplate(claGroupVersion, templateName, templateStr string, params interface{}) (string, error) {
+ tmpl := template.New(templateName)
+ t, err := tmpl.Parse(templateStr)
+ if err != nil {
+ return "", err
+ }
+
+ var tpl bytes.Buffer
+ if err := t.Execute(&tpl, params); err != nil {
+ return "", err
+ }
+
+ result := tpl.String()
+ result = result + GetEmailHelpContent(claGroupVersion == V2)
+ result = result + GetEmailSignOffContent()
+ return result, nil
+}
From 05ee9e4d16018a82bb1d0fbfadd33332eb241b2e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 5 Apr 2021 15:46:16 -0700
Subject: [PATCH 0214/1276] Resolved [#2838] GitHub Organization Not Displayed
on PCC (#2853)
- Updated GitHub Org and Repository queries to resolve display issue.
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 18 ++++++-------
cla-backend-go/repositories/repository.go | 27 +++++++------------
cla-backend-go/repositories/service.go | 4 +--
cla-backend-go/tests/signatures_test.go | 4 +--
.../v2/github_organizations/service.go | 7 ++---
cla-backend-go/v2/repositories/service.go | 2 +-
6 files changed, 26 insertions(+), 36 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 7c9858d40..2a770422e 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -681,15 +681,15 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
var nameUpdated bool
data := fmt.Sprintf("CLA Group ID: %s was updated by: %s", args.ProjectID, args.UserName)
- if ed.NewClaGroupName != ""{
+ if ed.NewClaGroupName != "" {
data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
nameUpdated = true
}
- if ed.NewClaGroupDescription != ""{
- if nameUpdated{
+ if ed.NewClaGroupDescription != "" {
+ if nameUpdated {
data = data + ","
- }else{
+ } else {
data = data + " with"
}
data = fmt.Sprintf("%s Description from : %s to : %s", data, ed.OldClaGroupDescription, ed.NewClaGroupDescription)
@@ -1477,22 +1477,22 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
var nameUpdated, descriptionUpdated bool
message := "The CLA Group"
- if ed.NewClaGroupName != ""{
+ if ed.NewClaGroupName != "" {
message = message + " name was updated to : " + ed.NewClaGroupName
nameUpdated = true
}
- if ed.NewClaGroupDescription != ""{
+ if ed.NewClaGroupDescription != "" {
descriptionUpdated = true
- if nameUpdated{
+ if nameUpdated {
message = message + " and the description was updated to : " + ed.NewClaGroupDescription
- }else{
+ } else {
message = message + " description was updated to : " + ed.NewClaGroupDescription
}
}
//shouldn't happen
- if !nameUpdated && !descriptionUpdated{
+ if !nameUpdated && !descriptionUpdated {
message = message + " was updated"
}
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index 5d5638325..d5275448c 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -64,7 +64,7 @@ type Repository interface {
GetRepositoriesByCLAGroup(ctx context.Context, claGroup string, enabled bool) ([]*models.GithubRepository, error)
GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error)
GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error)
- ListProjectRepositories(ctx context.Context, externalProjectID string, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error)
+ ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error)
}
// NewRepository create new Repository
@@ -545,31 +545,22 @@ func (r repo) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID
}
// List github repositories of project by external/salesforce project id
-func (r repo) ListProjectRepositories(ctx context.Context, externalProjectID string, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
+func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.ListProjectRepositories",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "externalProjectID": externalProjectID,
- "projectSFID": projectSFID,
- "enabled": utils.BoolValue(enabled),
+ "functionName": "repositories.repository.ListProjectRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "enabled": utils.BoolValue(enabled),
}
- var indexName string
out := &models.ListGithubRepositories{
List: make([]*models.GithubRepository, 0),
}
- var condition expression.KeyConditionBuilder
- var filter expression.ConditionBuilder
- if externalProjectID != "" {
- condition = expression.Key("repository_sfdc_id").Equal(expression.Value(externalProjectID))
- indexName = SFDCRepositoryIndex
- } else {
- condition = expression.Key("project_sfid").Equal(expression.Value(projectSFID))
- indexName = ProjectSFIDRepositoryOrganizationNameIndex
- }
+ condition := expression.Key("project_sfid").Equal(expression.Value(projectSFID))
// Add the enabled filter, if set
+ var filter expression.ConditionBuilder
if enabled != nil {
filter = expression.Name(repositoryEnabledColumn).Equal(expression.Value(enabled))
}
@@ -585,7 +576,7 @@ func (r repo) ListProjectRepositories(ctx context.Context, externalProjectID str
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(r.repositoryTableName),
- IndexName: aws.String(indexName),
+ IndexName: aws.String(ProjectSFIDRepositoryOrganizationNameIndex),
}
results, err := r.dynamoDBClient.Query(queryInput)
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index 67d7210ef..9c2cd36ed 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -152,7 +152,7 @@ func (s *service) DisableRepository(ctx context.Context, repositoryID string) er
}
func (s *service) ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.ListGithubRepositories, error) {
- return s.repo.ListProjectRepositories(ctx, externalProjectID, "", enabled)
+ return s.repo.ListProjectRepositories(ctx, externalProjectID, enabled)
}
func (s *service) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
@@ -160,7 +160,7 @@ func (s *service) GetRepository(ctx context.Context, repositoryID string) (*mode
}
func (s *service) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
- return s.repo.ListProjectRepositories(ctx, "", projectSFID, enabled)
+ return s.repo.ListProjectRepositories(ctx, projectSFID, enabled)
}
// GetRepositoryByName returns the repository by name: project-level/cla-project
diff --git a/cla-backend-go/tests/signatures_test.go b/cla-backend-go/tests/signatures_test.go
index 46698ba77..d5b1ab65e 100644
--- a/cla-backend-go/tests/signatures_test.go
+++ b/cla-backend-go/tests/signatures_test.go
@@ -23,7 +23,5 @@ func TestInvalidateSignatureTemplate(t *testing.T) {
result, err := utils.RenderTemplate(utils.V1, signatures.InvalidateSignatureTemplateName, signatures.InvalidateSignatureTemplate, params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello TestUser")
- assert.Contains(t, result, "regarding approval list removal for email removal")
- assert.Contains(t, result, "your signature record has been invalidated")
-
+ assert.Contains(t, result, "Due to this change your individual contribution authorization has been removed and you will be blocked from making subsequent code contributions on behalf of this project.")
}
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 730a919bb..e6ef1583a 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -80,6 +80,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
"projectSFID": projectSFID,
}
+ // Load the GitHub Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
orgs, err := s.ghService.GetGithubOrganizations(ctx, projectSFID)
// log.WithFields(f).Debug("loading github organization details by projectSFID...")
//orgs, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
@@ -96,7 +97,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return nil, err
}
- log.Debugf("project record: %+v ", projectServiceRecord)
+ // log.Debugf("project record: %+v ", projectServiceRecord)
var parentProjectSFID string
if utils.IsProjectHasRootParent(projectServiceRecord) {
@@ -169,9 +170,9 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
}
- log.WithFields(f).Debug("listing github repositories...")
+ log.WithFields(f).Debugf("loading github repositories by projectSFID: %s...", projectSFID)
enabled := true
- repos, err := s.ghRepository.ListProjectRepositories(ctx, parentProjectSFID, projectSFID, &enabled)
+ repos, err := s.ghRepository.ListProjectRepositories(ctx, projectSFID, &enabled)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading github repositories")
return nil, err
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index f37f8a1f5..92bc2b48c 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -251,7 +251,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
}
log.WithFields(f).Debug("loaded project from the project service")
enabled := true
- return s.repo.ListProjectRepositories(ctx, "", projectSFID, &enabled)
+ return s.repo.ListProjectRepositories(ctx, projectSFID, &enabled)
//// Lookup orgs via projectSFID
//log.WithFields(f).Debug("querying EasyCLA for organizations by project id...")
From 58a03669b71febc44a5a6fa460d80a94bbd73377 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Tue, 6 Apr 2021 20:55:59 +0300
Subject: [PATCH 0215/1276] fix legacy/v2 project property loading for event
logs (#2854)
Signed-off-by: makkalot
---
cla-backend-go/events/service.go | 24 +++++++++++++++---------
cla-backend-go/project/handlers.go | 3 ++-
cla-backend-go/v2/cla_groups/handlers.go | 7 ++++---
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index ea948ac17..e2670565a 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -222,24 +222,28 @@ func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
}
func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
+ if args == nil {
+ return errors.New("unable to load SF project data - args is nil")
+ }
+
f := logrus.Fields{
"functionName": "v1.events.service.loadSFProject",
+ "projectID": args.ProjectID,
+ "projectSFID": args.ProjectSFID,
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
- if args == nil {
- return errors.New("unable to load SF project data - args is nil")
+ // if it's a legacy model (v1) we need ot set the project sfid from project id
+ if args.ProjectSFID == "" && args.ProjectID != "" && utils.IsSalesForceID(args.ProjectID) {
+ args.ProjectSFID = args.ProjectID
}
- // Should be the same value for now...cleanup: need to remove one or the other
- if args.ProjectID == "" && args.ProjectSFID != "" {
- args.ProjectID = args.ProjectSFID
- }
- if args.ProjectSFID == "" && args.ProjectID != "" {
- args.ProjectSFID = args.ProjectID
+ // if project sfid not there try to set it from claGroupModel (v2)
+ if args.ProjectSFID == "" && args.ClaGroupModel != nil && args.ClaGroupModel.ProjectExternalID != "" {
+ args.ProjectSFID = args.ClaGroupModel.ProjectExternalID
}
- if utils.IsSalesForceID(args.ProjectID) {
+ if args.ProjectSFID != "" && utils.IsSalesForceID(args.ProjectSFID) {
// Check if project exists in platform project service
log.WithFields(f).Debugf("loading salesforce project by ID: %s...", args.ProjectSFID)
project, projectErr := project_service.GetClient().GetProject(args.ProjectSFID)
@@ -275,6 +279,8 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
args.ParentProjectSFID = project.Foundation.ID
args.ParentProjectName = project.Foundation.Name
}
+ } else {
+ log.WithFields(f).Warnf("project sfid %s was not set properly can't set parent project fields in event", args.ProjectSFID)
}
return nil
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index 32cb774ab..7799daa9e 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -316,8 +316,9 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
// Log an event
eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ ProjectID: projectParams.Body.ProjectID,
+ ProjectSFID: projectParams.Body.ProjectExternalID,
EventType: events.CLAGroupUpdated,
- ProjectSFID: claGroupModel.ProjectExternalID,
ClaGroupModel: claGroupModel,
UserID: claUser.UserID,
LfUsername: claUser.LFUsername,
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 3e2475194..97370a19e 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -148,9 +148,10 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ProjectID: claGroup.ClaGroupID,
- LfUsername: authUser.UserName,
+ EventType: events.CLAGroupUpdated,
+ ClaGroupModel: claGroupModel,
+ ProjectID: claGroup.ClaGroupID,
+ LfUsername: authUser.UserName,
EventData: &events.CLAGroupUpdatedEventData{
NewClaGroupName: params.Body.ClaGroupName,
NewClaGroupDescription: params.Body.ClaGroupDescription,
From 6941e11984f51e07710611977d5a1ebf784d8d46 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 6 Apr 2021 14:52:18 -0700
Subject: [PATCH 0216/1276] Resolves [#2838] GitHub Organization/Repo Display
Issue in PCC (#2855)
Signed-off-by: David Deal
---
.../github_organizations/helpers.go | 6 ++--
.../github_organizations/service.go | 10 +++----
.../v2/github_organizations/service.go | 29 ++++++++++++++-----
3 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/cla-backend-go/github_organizations/helpers.go b/cla-backend-go/github_organizations/helpers.go
index a42e43a3e..94ae540ce 100644
--- a/cla-backend-go/github_organizations/helpers.go
+++ b/cla-backend-go/github_organizations/helpers.go
@@ -59,15 +59,15 @@ func buildGithubOrganizationListModels(ctx context.Context, githubOrganizations
}
if ghorg.OrganizationInstallationID != 0 {
- log.WithFields(f).Debugf("Loading GitHub repository list based on installation id: %d...", ghorg.OrganizationInstallationID)
+ log.WithFields(f).Debugf("Loading GitHub repository list directly from GitHub based on the installation id: %d...", ghorg.OrganizationInstallationID)
list, err := github.GetInstallationRepositories(ctx, ghorg.OrganizationInstallationID)
if err != nil {
- log.WithFields(f).Warnf("unable to get repositories for installation id : %d", ghorg.OrganizationInstallationID)
+ log.WithFields(f).Warnf("unable to get repositories from GitHub for the installation id: %d", ghorg.OrganizationInstallationID)
ghorg.Repositories.Error = err.Error()
return
}
- log.WithFields(f).Debugf("Found %d GitHub repositories using installation id: %d...",
+ log.WithFields(f).Debugf("Found %d repositories from GitHUb using the installation id: %d...",
len(list), ghorg.OrganizationInstallationID)
for _, repoInfo := range list {
ghorg.Repositories.List = append(ghorg.Repositories.List, &models.GithubRepositoryInfo{
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index 9f1b38d47..f95241362 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -90,9 +90,10 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
if len(projectGithubModels.List) >= 0 {
githubOrgs = append(githubOrgs, projectGithubModels.List...)
}
+ log.WithFields(f).Debugf("loaded %d GitHub organizations using projectSFID: %s", len(projectGithubModels.List), projectSFID)
- log.WithFields(f).Debug("factoring github orgs for parent...")
// Lookup the parent
+ log.WithFields(f).Debugf("looking up parent for projectSFID: %s...", projectSFID)
parentProjectSFID, projErr := v2ProjectService.GetClient().GetParentProject(projectSFID)
if projErr != nil {
log.WithFields(f).Warnf("problem fetching project parent SFID, error: %+v", projErr)
@@ -100,7 +101,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
if parentProjectSFID != projectSFID {
- log.WithFields(f).Debugf("searching github organization by parent SFID: %s", parentProjectSFID)
+ log.WithFields(f).Debugf("found parent of projectSFID: %s to be %s. Searching github organization by parent SFID: %s...", projectSFID, parentProjectSFID, parentProjectSFID)
parentGithubModels, parentErr := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
if parentErr != nil {
log.WithFields(f).Warnf("problem fetching github organizations by paarent projectSFID: %s , error: %+v", parentProjectSFID, err)
@@ -110,12 +111,11 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
if len(parentGithubModels.List) >= 0 {
githubOrgs = append(githubOrgs, parentGithubModels.List...)
}
+ log.WithFields(f).Debugf("loaded %d GitHub organizations using projectSFID: %s", len(parentGithubModels.List), parentProjectSFID)
}
- log.WithFields(f).Debugf("no parent or parent is %s or %s - search criteria exhausted",
- utils.TheLinuxFoundation, utils.TheLinuxFoundation)
-
gitHubOrgModels.List = githubOrgs
+
// Remove potential duplicates
s.removeDuplicateGHOrgs(gitHubOrgModels.List)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index e6ef1583a..bf30b8650 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -170,16 +170,31 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
}
+ // We need to search the repository list based on two criteria
+ // Need to search by projectSFID and/or Organization ID????
log.WithFields(f).Debugf("loading github repositories by projectSFID: %s...", projectSFID)
- enabled := true
- repos, err := s.ghRepository.ListProjectRepositories(ctx, projectSFID, &enabled)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("problem loading github repositories")
- return nil, err
+ //enabled := true
+ //repos, err := s.ghRepository.ListProjectRepositories(ctx, projectSFID, &enabled)
+ //if err != nil {
+ // log.WithFields(f).WithError(err).Warn("problem loading github repositories")
+ // return nil, err
+ //}
+ var repoList []*v1Models.GithubRepository
+ for _, org := range orgs.List {
+ orgRepos, orgReposErr := s.ghRepository.GetRepositoriesByOrganizationName(ctx, org.OrganizationName)
+ if orgReposErr != nil {
+ log.WithFields(f).WithError(orgReposErr).Warn("problem loading github repositories by org name")
+ return nil, orgReposErr
+ }
+ repoList = append(repoList, orgRepos...)
}
- log.WithFields(f).Debugf("processing %d github repositories...", len(repos.List))
- for _, repo := range repos.List {
+ // Remove any duplicates
+
+ //jlog.WithFields(f).Debugf("processing %d github repositories...", len(repos.List))
+ log.WithFields(f).Debugf("processing %d github repositories...", len(repoList))
+ //for _, repo := range repos.List {
+ for _, repo := range repoList {
rorg, ok := orgmap[repo.RepositoryOrganizationName]
if !ok {
log.WithFields(f).Warnf("repositories table contain stale data for organization %s", repo.RepositoryOrganizationName)
From 64fef98a3b4311121751eb94a79a2b396e0cf026 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 6 Apr 2021 16:19:34 -0700
Subject: [PATCH 0217/1276] Resolved GitHub Query Issue with Missing Parent
SFID (#2856)
Signed-off-by: David Deal
---
cla-backend-go/github_organizations/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index f95241362..2d592a6ea 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -100,7 +100,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return nil, projErr
}
- if parentProjectSFID != projectSFID {
+ if parentProjectSFID != projectSFID && parentProjectSFID != "" {
log.WithFields(f).Debugf("found parent of projectSFID: %s to be %s. Searching github organization by parent SFID: %s...", projectSFID, parentProjectSFID, parentProjectSFID)
parentGithubModels, parentErr := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
if parentErr != nil {
From 926749c6a0ebc6816b394fa20291e1330a3b43d4 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Wed, 7 Apr 2021 20:47:47 +0300
Subject: [PATCH 0218/1276] [#2792,#2842] Bug/Update Approval List (#2857)
- Resolved icla invalidation for email removals
- Resolved email sent for invalidation notification
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/email.go | 6 +-
cla-backend-go/signatures/models.go | 1 +
cla-backend-go/signatures/repository.go | 91 ++++++++++++++++++-------
cla-backend-go/signatures/service.go | 2 +-
cla-backend-go/tests/signatures_test.go | 4 +-
5 files changed, 74 insertions(+), 30 deletions(-)
diff --git a/cla-backend-go/signatures/email.go b/cla-backend-go/signatures/email.go
index 7470f236c..84a69759e 100644
--- a/cla-backend-go/signatures/email.go
+++ b/cla-backend-go/signatures/email.go
@@ -9,6 +9,7 @@ type InvalidateSignatureTemplateParams struct {
ClaType string
ClaManager string
RemovalCriteria string
+ ProjectName string
}
const (
@@ -17,7 +18,8 @@ const (
//InvalidateSignatureTemplate ...
InvalidateSignatureTemplate = `
Hello {{.RecipientName}}
-
This is a notification email from EasyCLA regarding approval list removal for {{.RemovalCriteria}}
-
Due to this change your individual contribution authorization has been removed and you will be blocked from making subsequent code contributions on behalf of this project.
+
This is a notification email from EasyCLA regarding the claGroup {{.ProjectName}}
+
The ICLA signature for {{.RecipientName}} has been invalidated.
+
Please contact Project Manager for the claGroup {{.ProjectName}} and/or CLA Manager from your company if you have more questions.
`
)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 2775ef8e9..6199fe80a 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -23,6 +23,7 @@ type ApprovalList struct {
ApprovalList []string
Action string
ClaGroupID string
+ ClaGroupName string
CompanyID string
Version string
DomainApprovals []string
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index de9bc08d7..a68bdd63e 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -76,7 +76,7 @@ type SignatureRepository interface {
GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error)
GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams, pageSize int64) (*models.Signatures, error)
ProjectSignatures(ctx context.Context, projectID string) (*models.Signatures, error)
- UpdateApprovalList(ctx context.Context, claManager *models.User, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error)
+ UpdateApprovalList(ctx context.Context, claManager *models.User, claGroupModel *models.ClaGroup, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error)
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
RemoveCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
@@ -450,6 +450,8 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
"signatureSigned": "true",
}
+ log.WithFields(f).Debug("querying signature for icla records ...")
+
// These are the keys we want to match for an ICLA Signature with a given CLA Group and User ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(userID)))
@@ -1963,8 +1965,10 @@ func (repo repository) RemoveCLAManager(ctx context.Context, signatureID, claMan
}
// UpdateApprovalList updates the specified project/company signature with the updated approval list information
-func (repo repository) UpdateApprovalList(ctx context.Context, claManager *models.User, projectID, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
+func (repo repository) UpdateApprovalList(ctx context.Context, claManager *models.User, claGroupModel *models.ClaGroup, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
+ projectID := claGroupModel.ProjectID
+ projectName := claGroupModel.ProjectName
f := logrus.Fields{
"functionName": "UpdateApprovalList",
"projectID": projectID,
@@ -2082,14 +2086,17 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// if email removal update signature approvals
if params.RemoveEmailApprovalList != nil {
+ log.WithFields(f).Debugf("removing email: %+v the approval list", params.RemoveDomainApprovalList)
var wg sync.WaitGroup
wg.Add(len(params.RemoveEmailApprovalList))
for _, email := range params.RemoveEmailApprovalList {
go func(email string) {
defer wg.Done()
+ log.WithFields(f).Debugf("getting cla user record for email: %s ", email)
claUser, userErr := repo.usersRepo.GetUserByEmail(email)
- if userErr != nil {
+ if userErr != nil || claUser == nil {
log.WithFields(f).Debugf("error getting user by email: %s ", email)
+ return
}
criteria := &ApprovalCriteria{
UserEmail: email,
@@ -2115,21 +2122,26 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return
}
- // update user by emai
+ // update user by email
// send email
- if claUser != nil {
- log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- _, err := utils.RenderTemplate(utils.V2, InvalidateSignatureTemplateName,
- InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
- RecipientName: utils.GetBestUsername(claUser),
- ClaType: utils.ClaTypeCCLA,
- ClaManager: utils.GetBestUsername(claManager),
- RemovalCriteria: approvalList.Criteria,
- })
- if err != nil {
- log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
- return
- }
+ eclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, projectName)
+ log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ body, err := utils.RenderTemplate(claGroupModel.Version, InvalidateSignatureTemplateName,
+ InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(claUser),
+ ClaType: utils.ClaTypeCCLA,
+ ClaManager: utils.GetBestUsername(claManager),
+ RemovalCriteria: approvalList.Criteria,
+ ProjectName: projectName,
+ })
+ if err != nil {
+ log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
+ return
+ }
+ err = utils.SendEmail(eclaEmailSubject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
+ return
}
//Log Event
@@ -2139,15 +2151,15 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
CLAManager: claManager,
CLAGroupID: signs.Signatures[0].ProjectID,
}
+ repo.eventsService.LogEventWithContext(ctx, eventArgs)
}
- repo.eventsService.LogEventWithContext(ctx, eventArgs)
-
// invalidate icla
+ log.WithFields(f).Debugf("invalidating icla...:user: %+v", claUser)
if claUser != nil {
icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
- if iclaErr != nil {
+ if iclaErr != nil || icla == nil {
log.WithFields(f).Debugf("unable to get icla signature for user: %s ", email)
}
if icla != nil {
@@ -2156,6 +2168,26 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if err != nil {
log.WithFields(f).Warnf("unable to invalidate record for user:%s ", email)
}
+ // send email
+ log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
+ InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(claUser),
+ ClaType: utils.ClaTypeICLA,
+ ClaManager: utils.GetBestUsername(claManager),
+ RemovalCriteria: approvalList.Criteria,
+ ProjectName: projectName,
+ })
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
+ return
+ }
+ iclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, projectName)
+ err = utils.SendEmail(iclaEmailSubject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
+ return
+ }
}
}
@@ -2211,8 +2243,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Action = utils.RemoveApprovals
approvalList.GerritICLAECLAs = gerritICLAECLAs
approvalList.ClaGroupID = projectID
+ approvalList.ClaGroupName = claGroupModel.ProjectName
approvalList.CompanyID = companyID
- approvalList.Version = utils.V2
+ approvalList.Version = claGroupModel.Version
invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
if invalidateErr != nil {
msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
@@ -2305,7 +2338,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Criteria = utils.GitHubOrgCriteria
approvalList.ApprovalList = params.RemoveGithubOrgApprovalList
approvalList.Action = utils.RemoveApprovals
- approvalList.Version = utils.V2
+ approvalList.Version = claGroupModel.Version
// Get repositories by CLAGroup
repositories, err := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
if err != nil {
@@ -2450,16 +2483,22 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
email := getBestEmail(user)
// send email
+ domainSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, approvalList.ClaGroupName)
log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- _, emailErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
RecipientName: utils.GetBestUsername(user),
ClaType: utils.ClaTypeICLA,
ClaManager: utils.GetBestUsername(claManager),
RemovalCriteria: approvalList.Criteria,
})
- if emailErr != nil {
- log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
+ return
+ }
+ err = utils.SendEmail(domainSubject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
return
}
}(icla)
@@ -2525,7 +2564,7 @@ func (repo repository) getGerritUserByEmail(ctx context.Context, email string, g
f := logrus.Fields{
"functionName": "getGerritUserByEmailDomain",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "emai": email,
+ "email": email,
}
log.WithFields(f).Debugf("checking gerrit user for email: %s ", email)
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 86455cf77..340d6d3da 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -435,7 +435,7 @@ func (s service) UpdateApprovalList(ctx context.Context, authUser *auth.User, cl
ProjectSFID: claGroupModel.ProjectExternalID,
}
- updatedSig, err := s.repo.UpdateApprovalList(ctx, userModel, claGroupModel.ProjectID, companyModel.CompanyID, params, eventArgs)
+ updatedSig, err := s.repo.UpdateApprovalList(ctx, userModel, claGroupModel, companyModel.CompanyID, params, eventArgs)
if err != nil {
return updatedSig, err
diff --git a/cla-backend-go/tests/signatures_test.go b/cla-backend-go/tests/signatures_test.go
index d5b1ab65e..b2eed5317 100644
--- a/cla-backend-go/tests/signatures_test.go
+++ b/cla-backend-go/tests/signatures_test.go
@@ -18,10 +18,12 @@ func TestInvalidateSignatureTemplate(t *testing.T) {
ClaType: utils.ClaTypeICLA,
ClaManager: "claManager",
RemovalCriteria: "email removal",
+ ProjectName: "testProject",
}
result, err := utils.RenderTemplate(utils.V1, signatures.InvalidateSignatureTemplateName, signatures.InvalidateSignatureTemplate, params)
assert.NoError(t, err)
assert.Contains(t, result, "Hello TestUser")
- assert.Contains(t, result, "Due to this change your individual contribution authorization has been removed and you will be blocked from making subsequent code contributions on behalf of this project.")
+ assert.Contains(t, result, "The ICLA signature for TestUser has been invalidated.")
+ assert.Contains(t, result, "Please contact Project Manager for the claGroup testProject and/or CLA Manager from your company if you have more questions.")
}
From 8eb4740465ea9bef65db330f5b944fa730abafbb Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 7 Apr 2021 11:40:37 -0700
Subject: [PATCH 0219/1276] Updated GitHub Query Logic (#2858)
- Trap and handle the situation for no repositories
- Remove duplicates
Signed-off-by: David Deal
---
.../github_organizations/repository.go | 16 ++++----
.../github_organizations/service.go | 7 ++--
cla-backend-go/repositories/repository.go | 2 +-
.../v2/github_organizations/service.go | 38 +++++++++----------
4 files changed, 32 insertions(+), 31 deletions(-)
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index a6ef3adc4..e63c20257 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -65,7 +65,7 @@ func NewRepository(awsSession *session.Session, stage string) repository {
// AddGithubOrganization add github organization logic
func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
- "functionName": "AddGitHubOrganization",
+ "functionName": "v1.github_organizations.repository.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFID": projectSFID,
@@ -186,7 +186,7 @@ func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectS
// GetGithubOrganizations get github organizations based on the project SFID
func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganizations",
+ "functionName": "v1.github_organizations.repository.GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -238,7 +238,7 @@ func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID s
func (repo repository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "GetGithubOrganizationsByParent",
+ "functionName": "v1.github_organizations.repository.GetGithubOrganizationsByParent",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
}
@@ -289,7 +289,7 @@ func (repo repository) GetGithubOrganizationsByParent(ctx context.Context, paren
func (repo repository) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganizationByName",
+ "functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"githubOrganizationName": githubOrganizationName,
}
@@ -337,7 +337,7 @@ func (repo repository) GetGithubOrganizationByName(ctx context.Context, githubOr
func (repo repository) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganization",
+ "functionName": "v1.github_organizations.repository.GetGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"githubOrganizationName": githubOrganizationName,
}
@@ -371,7 +371,7 @@ func (repo repository) GetGithubOrganization(ctx context.Context, githubOrganiza
// UpdateGithubOrganization updates the specified GitHub organization based on the update model provided
func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
f := logrus.Fields{
- "functionName": "UpdateGitHubOrganization",
+ "functionName": "v1.github_organizations.repository.UpdateGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"organizationName": organizationName,
@@ -435,7 +435,7 @@ func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID
func (repo repository) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
- "functionName": "DeleteGitHubOrganization",
+ "functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"githubOrgName": githubOrgName,
@@ -475,7 +475,7 @@ func (repo repository) DeleteGithubOrganization(ctx context.Context, projectSFID
func (repo repository) DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error {
f := logrus.Fields{
- "functionName": "DeleteGitHubOrganization",
+ "functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"githubOrgName": githubOrgName,
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index 2d592a6ea..f810b7444 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -27,6 +27,7 @@ type Service interface {
GetGithubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error)
UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
+ RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization
}
type service struct {
@@ -117,7 +118,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
gitHubOrgModels.List = githubOrgs
// Remove potential duplicates
- s.removeDuplicateGHOrgs(gitHubOrgModels.List)
+ gitHubOrgModels.List = s.RemoveDuplicates(gitHubOrgModels.List)
return &gitHubOrgModels, err
}
@@ -184,8 +185,8 @@ func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
return s.repo.DeleteGithubOrganization(ctx, projectSFID, githubOrgName)
}
-// filter ghOrgs duplicates
-func (s service) removeDuplicateGHOrgs(input []*models.GithubOrganization) []*models.GithubOrganization {
+// RemoveDuplicates removes any duplicates from the specified list
+func (s service) RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization {
if input == nil {
return nil
}
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index d5275448c..3c192a5c3 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -506,7 +506,7 @@ func (r *repo) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgN
if len(results.Items) == 0 {
msg := fmt.Sprintf("no repositories found associated GitHub Organization: %s", gitHubOrgName)
- log.WithFields(f).Warn(msg)
+ log.WithFields(f).Debug(msg)
return nil, &utils.GitHubRepositoryNotFound{
Message: msg,
}
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index bf30b8650..6a65e22c9 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -81,13 +81,14 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
// Load the GitHub Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
+ log.WithFields(f).Debugf("loading GitHub organizations for projectSFID: %s", projectSFID)
orgs, err := s.ghService.GetGithubOrganizations(ctx, projectSFID)
- // log.WithFields(f).Debug("loading github organization details by projectSFID...")
- //orgs, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading github organizations from the project service")
return nil, err
}
+ log.WithFields(f).Debugf("discovered %d GitHub organizations for projectSFID: %s", len(orgs.List), projectSFID)
+ orgs.List = s.ghService.RemoveDuplicates(orgs.List)
psc := v2ProjectService.GetClient()
log.WithFields(f).Debug("loading project details from the project service...")
@@ -97,8 +98,6 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return nil, err
}
- // log.Debugf("project record: %+v ", projectServiceRecord)
-
var parentProjectSFID string
if utils.IsProjectHasRootParent(projectServiceRecord) {
parentProjectSFID = projectSFID
@@ -108,14 +107,20 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
+ // Our response model
out := &models.ProjectGithubOrganizations{
List: make([]*models.ProjectGithubOrganization, 0),
}
+
+ // Next, we need to load a bunch of additional data for the response including the github status (if it's still connected/live, not renamed/moved), the CLA Group details, etc.
+
+ // A temp data model for holding the intermediate results
type githubRepoInfo struct {
orgName string
repoInfo *v1Models.GithubRepositoryInfo
}
- // connectedRepo contains list of repositories for which github app have permission
+
+ // connectedRepo contains list of repositories for which github app have permission to see
connectedRepo := make(map[string]*githubRepoInfo)
orgmap := make(map[string]*models.ProjectGithubOrganization)
for _, org := range orgs.List {
@@ -172,28 +177,23 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
// We need to search the repository list based on two criteria
// Need to search by projectSFID and/or Organization ID????
- log.WithFields(f).Debugf("loading github repositories by projectSFID: %s...", projectSFID)
- //enabled := true
- //repos, err := s.ghRepository.ListProjectRepositories(ctx, projectSFID, &enabled)
- //if err != nil {
- // log.WithFields(f).WithError(err).Warn("problem loading github repositories")
- // return nil, err
- //}
+ log.WithFields(f).Debugf("loading github repositories from %d organizations for projectSFID: %s...", len(orgs.List), projectSFID)
var repoList []*v1Models.GithubRepository
for _, org := range orgs.List {
orgRepos, orgReposErr := s.ghRepository.GetRepositoriesByOrganizationName(ctx, org.OrganizationName)
- if orgReposErr != nil {
- log.WithFields(f).WithError(orgReposErr).Warn("problem loading github repositories by org name")
- return nil, orgReposErr
+ if orgReposErr != nil || orgRepos == nil {
+ if _, ok := orgReposErr.(*utils.GitHubRepositoryNotFound); ok {
+ log.WithFields(f).Debug(orgReposErr)
+ } else {
+ log.WithFields(f).WithError(orgReposErr).Warn("problem loading github repositories by org name")
+ }
+ } else {
+ repoList = append(repoList, orgRepos...)
}
- repoList = append(repoList, orgRepos...)
}
// Remove any duplicates
-
- //jlog.WithFields(f).Debugf("processing %d github repositories...", len(repos.List))
log.WithFields(f).Debugf("processing %d github repositories...", len(repoList))
- //for _, repo := range repos.List {
for _, repo := range repoList {
rorg, ok := orgmap[repo.RepositoryOrganizationName]
if !ok {
From 96f8c5003bec5fd750a447e8c451bc2b69d13b71 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 7 Apr 2021 14:46:28 -0700
Subject: [PATCH 0220/1276] Added Nil Check For Signature Query (#2859)
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index a68bdd63e..168f79888 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -1407,12 +1407,12 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
filter := expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true))).
And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true))))
- if criteria.GitHubUsername != "" {
+ if criteria != nil && criteria.GitHubUsername != "" {
log.WithFields(f).Debugf("adding Githubusername criteria filter for :%s ", criteria.GitHubUsername)
filter = filter.And(expression.Name("user_github_username").Equal(expression.Value(criteria.GitHubUsername)))
}
- if criteria.UserEmail != "" {
+ if criteria != nil && criteria.UserEmail != "" {
log.WithFields(f).Debugf("adding useremail criteria filter for : %s ", criteria.UserEmail)
filter = filter.And(expression.Name("user_email").Equal(expression.Value(criteria.UserEmail)))
}
From 41e08cde2ac53cbf1add0f135a936947b8266836 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 8 Apr 2021 17:29:35 -0700
Subject: [PATCH 0221/1276] Removed Duplicate CCLA Get Signature API Call
(#2862)
- Only load gerrit user list on approval list removals
- Updated logging/error handling
- Load both ICLA and CCLA gerrit users using a go routine (concurrently)
- Resolved lint errors
- Updated signature query by using the proper index
Signed-off-by: David Deal
---
cla-backend-go/gerrits/lf_group.go | 6 +-
cla-backend-go/go.mod | 6 +-
cla-backend-go/go.sum | 6 +
cla-backend-go/signatures/models.go | 9 +
cla-backend-go/signatures/repository.go | 310 ++++++++++++------------
cla-backend-go/tests/utils_test.go | 7 -
cla-backend-go/utils/signature_utils.go | 2 +-
7 files changed, 179 insertions(+), 167 deletions(-)
diff --git a/cla-backend-go/gerrits/lf_group.go b/cla-backend-go/gerrits/lf_group.go
index f3b987b72..dd73302bb 100644
--- a/cla-backend-go/gerrits/lf_group.go
+++ b/cla-backend-go/gerrits/lf_group.go
@@ -25,6 +25,7 @@ import (
// constants
const (
DefaultHTTPTimeout = 10 * time.Second
+ LongHTTPTimeout = 45 * time.Second
)
// LFGroup contains access information of lf LDAP group
@@ -186,7 +187,7 @@ func (lfg *LFGroup) GetUsersOfGroup(ctx context.Context, authUser *auth.User, cl
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+accessToken)
client := http.Client{
- Timeout: DefaultHTTPTimeout,
+ Timeout: LongHTTPTimeout,
}
// Invoke the request
@@ -209,8 +210,6 @@ func (lfg *LFGroup) GetUsersOfGroup(ctx context.Context, authUser *auth.User, cl
log.WithFields(f).Debugf("successfully fetched members from group: %s", groupName)
var result v2Models.GerritGroupResponse
- //err = json.NewDecoder(resp.Body).Decode(&result)
-
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem reading response for url: %s", url)
@@ -223,7 +222,6 @@ func (lfg *LFGroup) GetUsersOfGroup(ctx context.Context, authUser *auth.User, cl
log.WithFields(f).WithError(err).Warnf("problem unmarshalling response for url: %s", url)
return nil, err
}
- log.WithFields(f).Debugf("response body: %+v", result)
return &result, nil
}
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index ae1fb4901..a98bc3a35 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -8,8 +8,8 @@ replace github.com/awslabs/aws-lambda-go-api-proxy => github.com/LF-Engineering/
require (
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2
- github.com/LF-Engineering/lfx-kit v0.1.22
- github.com/LF-Engineering/lfx-models v0.6.34
+ github.com/LF-Engineering/lfx-kit v0.1.24
+ github.com/LF-Engineering/lfx-models v0.6.42
github.com/aws/aws-lambda-go v1.22.0
github.com/aws/aws-sdk-go v1.36.27
github.com/aymerick/raymond v2.0.2+incompatible
@@ -28,7 +28,7 @@ require (
github.com/go-openapi/swag v0.19.9
github.com/go-openapi/validate v0.19.10
github.com/go-resty/resty/v2 v2.3.0
- github.com/gofrs/uuid v3.2.0+incompatible
+ github.com/gofrs/uuid v4.0.0+incompatible
github.com/golang/mock v1.4.4
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/go-github/v33 v33.0.0
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index a29cdd858..70303eb34 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -28,8 +28,12 @@ github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUam
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
github.com/LF-Engineering/lfx-kit v0.1.22 h1:4tE1xTvu5CRWIokOo1waOfuB6vgaCpov5glhkdVzbAs=
github.com/LF-Engineering/lfx-kit v0.1.22/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
+github.com/LF-Engineering/lfx-kit v0.1.24 h1:2lfmBMWWfOwU2XEOSP7keS/CqR5mj30YmJPv6fUiOvM=
+github.com/LF-Engineering/lfx-kit v0.1.24/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
github.com/LF-Engineering/lfx-models v0.6.34 h1:K8al2aTq8nDm3qNmsTNAhZ1uDzfew/UymwbcW9gbDDs=
github.com/LF-Engineering/lfx-models v0.6.34/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
+github.com/LF-Engineering/lfx-models v0.6.42 h1:PfvP/hmcV+OHftNrzclzvwGUv864xyNcp0Kz2HUA5C0=
+github.com/LF-Engineering/lfx-models v0.6.42/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
@@ -265,6 +269,8 @@ github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 6199fe80a..3faf360b3 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -3,6 +3,8 @@
package signatures
+import v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+
// SignatureCompanyID is a simple data model to hold the signature ID and come company details for CCLA's
type SignatureCompanyID struct {
SignatureID string
@@ -33,3 +35,10 @@ type ApprovalList struct {
GHUsernames []string
GerritICLAECLAs []string
}
+
+// GerritUserResponse is a data structure to hold the gerrit user query response
+type GerritUserResponse struct {
+ gerritGroupResponse *v2Models.GerritGroupResponse
+ queryType string
+ Error error
+}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 168f79888..a3a4046c0 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -127,7 +127,7 @@ func NewRepository(awsSession *session.Session, stage string, companyRepo compan
// GetGithubOrganizationsFromWhitelist returns a list of GH organizations stored in the whitelist
func (repo repository) GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "GetGitHubOrganizationsFromWhitelist",
+ "functionName": "v1.signatures.repository.GetGitHubOrganizationsFromWhitelist",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -171,7 +171,7 @@ func (repo repository) GetGithubOrganizationsFromWhitelist(ctx context.Context,
// AddGithubOrganizationToWhitelist adds the specified GH organization to the whitelist
func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "AddGitHubOrganizationToWhitelist",
+ "functionName": "v1.signatures.repository.AddGitHubOrganizationToWhitelist",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"GitHubOrganizationID": GitHubOrganizationID,
@@ -264,7 +264,7 @@ func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, sig
// DeleteGithubOrganizationFromWhitelist removes the specified GH organization from the whitelist
func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "DeleteGitHubOrganizationFromWhitelist",
+ "functionName": "v1.signatures.repository.DeleteGitHubOrganizationFromWhitelist",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"GitHubOrganizationID": GitHubOrganizationID,
@@ -385,7 +385,7 @@ func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context
// GetSignature returns the signature for the specified signature id
func (repo repository) GetSignature(ctx context.Context, signatureID string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "GetSignature",
+ "functionName": "v1.signatures.repository.GetSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -439,7 +439,7 @@ func (repo repository) GetSignature(ctx context.Context, signatureID string) (*m
// GetIndividualSignature returns the signature record for the specified CLA Group and User
func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "GetIndividualSignature",
+ "functionName": "v1.signatures.repository.GetIndividualSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"tableName": repo.signatureTableName,
"claGroupID": claGroupID,
@@ -534,7 +534,7 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
// GetCorporateSignature returns the signature record for the specified CLA Group and Company ID
func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, companyID string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "GetCorporateSignature",
+ "functionName": "v1.signatures.repository.GetCorporateSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"tableName": repo.signatureTableName,
"claGroupID": claGroupID,
@@ -545,7 +545,7 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
"signatureSigned": "true",
}
- // These are the keys we want to match for an ICLA Signature with a given CLA Group and User ID
+ // These are the keys we want to match for an CCLA Signature with a given CLA Group and Company ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
@@ -626,7 +626,7 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
// GetSignatureACL returns the signature ACL for the specified signature id
func (repo repository) GetSignatureACL(ctx context.Context, signatureID string) ([]string, error) {
f := logrus.Fields{
- "functionName": "GetSignatureACL",
+ "functionName": "v1.signatures.repository.GetSignatureACL",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -687,7 +687,7 @@ func addConditionToFilter(filter expression.ConditionBuilder, cond expression.Co
// GetProjectSignatures returns a list of signatures for the specified project
func (repo repository) GetProjectSignatures(ctx context.Context, params signatures.GetProjectSignaturesParams) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "signatures.repository.GetProjectSignatures",
+ "functionName": "v1.signatures.repository.GetProjectSignatures",
"tableName": repo.signatureTableName,
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ProjectID,
@@ -891,7 +891,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// CreateProjectSummaryReport generates a project summary report based on the specified input
func (repo repository) CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error) { // nolint
f := logrus.Fields{
- "functionName": "signatures.repository.CreateProjectSummaryReport",
+ "functionName": "v1.signatures.repository.CreateProjectSummaryReport",
"tableName": repo.signatureTableName,
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ProjectID,
@@ -1109,7 +1109,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
// GetProjectCompanySignature returns a the signature for the specified project and specified company with the other query flags
func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "GetProjectCompanySignature",
+ "functionName": "v1.signatures.repository.GetProjectCompanySignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectID": projectID,
@@ -1118,9 +1118,12 @@ func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID
"pageSize": aws.Int64Value(pageSize),
"nextKey": aws.StringValue(nextKey),
}
+
+ log.WithFields(f).Debug("querying for project company signature...")
sortOrder := utils.SortOrderAscending
sigs, getErr := repo.GetProjectCompanySignatures(ctx, companyID, projectID, signed, approved, nextKey, &sortOrder, pageSize)
if getErr != nil {
+ log.WithFields(f).WithError(getErr).Warn("problem loading project company signatures...")
return nil, getErr
}
@@ -1133,13 +1136,14 @@ func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID
companyID, projectID)
}
+ log.WithFields(f).Debugf("returning project company signature")
return sigs.Signatures[0], nil
}
// GetProjectCompanySignatures returns a list of signatures for the specified project and specified company
func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "GetProjectCompanySignatures",
+ "functionName": "v1.signatures.repository.GetProjectCompanySignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectID": projectID,
@@ -1151,20 +1155,21 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
}
// These are the keys we want to match
- condition := expression.Key("signature_project_id").Equal(expression.Value(projectID))
- filter := expression.Name("signature_reference_id").Equal(expression.Value(companyID)).
- And(expression.Name("signature_type").Equal(expression.Value("ccla"))).
+ //condition := expression.Key("signature_project_id").Equal(expression.Value(projectID))
+ condition := expression.Key("signature_project_id").Equal(expression.Value(projectID)).
+ And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
+ filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
And(expression.Name("signature_reference_type").Equal(expression.Value("company")))
// If the caller provided a signature signed value...add the appropriate filter
if signed != nil {
- log.WithFields(f).Debugf("Filtering signature_signed: %+v", *signed)
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
}
// If the caller provided a signature approved value...add the appropriate filter
if approved != nil {
- log.WithFields(f).Debugf("Filter by signature_approved: %+v", *approved)
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
}
@@ -1172,6 +1177,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
if pageSize != nil {
limit = *pageSize
}
+ log.WithFields(f).Debugf("page size %d", limit)
// Use the nice builder to create the expression
expr, err := expression.NewBuilder().WithKeyCondition(condition).WithFilter(filter).WithProjection(buildProjection()).Build()
@@ -1189,8 +1195,9 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
TableName: aws.String(repo.signatureTableName),
- IndexName: aws.String("project-signature-index"), // Name of a secondary index to scan
+ IndexName: aws.String(SignatureProjectReferenceIndex), // Name of a secondary index to scan
Limit: aws.Int64(limit),
+ //IndexName: aws.String("project-signature-index"), // Name of a secondary index to scan
}
// If we have the next key, set the exclusive start key value
@@ -1214,14 +1221,17 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
+ log.WithFields(f).Debugf("executing query for input: %+v", queryInput)
results, errQuery := repo.dynamoDBClient.Query(queryInput)
if errQuery != nil {
- log.WithFields(f).Warnf("error retrieving project signature ID for project: %s with company: %s, error: %v",
+ log.WithFields(f).WithError(errQuery).Warnf("error retrieving project signature ID for project: %s with company: %s, error: %v",
projectID, companyID, errQuery)
return nil, errQuery
}
+ log.WithFields(f).Debugf("query response received with %d results", len(results.Items))
// Convert the list of DB models to a list of response models
+ log.WithFields(f).Debugf("building response model for %d results", len(results.Items))
signatureList, modelErr := repo.buildProjectSignatureModels(ctx, results, projectID, LoadACLDetails)
if modelErr != nil {
log.WithFields(f).Warnf("error converting DB model to response model for signatures with project %s with company: %s, error: %v",
@@ -1265,6 +1275,10 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
// Meta-data for the response
totalCount := *describeTableResult.Table.ItemCount
+ log.WithFields(f).Debugf("returing %d signatures", len(sigs))
+ if len(sigs) > 0 {
+ log.WithFields(f).Debugf("signatureID: %s", sigs[0].SignatureID)
+ }
return &models.Signatures{
ProjectID: projectID,
ResultCount: int64(len(sigs)),
@@ -1277,7 +1291,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
// Get project signatures with no pagination
func (repo repository) ProjectSignatures(ctx context.Context, projectID string) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "ProjectSignatures",
+ "functionName": "v1.signatures.repository.ProjectSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectID": projectID,
}
@@ -1347,7 +1361,7 @@ func (repo repository) ProjectSignatures(ctx context.Context, projectID string)
// InvalidateProjectRecord invalidates the specified project record by setting the signature_approved flag to false
func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID, note string) error {
f := logrus.Fields{
- "functionName": "InvalidateProjectRecord",
+ "functionName": "v1.signatures.repository.InvalidateProjectRecord",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -1391,7 +1405,7 @@ func (repo repository) InvalidateProjectRecord(ctx context.Context, signatureID,
// GetProjectCompanyEmployeeSignatures returns a list of employee signatures for the specified project and specified company
func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria, pageSize int64) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "GetProjectCompanyEmployeeSignatures",
+ "functionName": "v1.signatures.repository.GetProjectCompanyEmployeeSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectID": params.ProjectID,
"companyID": params.CompanyID,
@@ -1521,7 +1535,7 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
// GetCompanySignatures returns a list of company signatures for the specified company
func (repo repository) GetCompanySignatures(ctx context.Context, params signatures.GetCompanySignaturesParams, pageSize int64, loadACL bool) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "GetCompanySignatures",
+ "functionName": "v1.signatures.repository.GetCompanySignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
@@ -1639,7 +1653,7 @@ func (repo repository) GetCompanySignatures(ctx context.Context, params signatur
// GetCompanyIDsWithSignedCorporateSignatures returns a list of company IDs that have signed a CLA agreement
func (repo repository) GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error) {
f := logrus.Fields{
- "functionName": "GetCompanyIDsWithSignedCorporateSignatures",
+ "functionName": "v1.signatures.repository.GetCompanyIDsWithSignedCorporateSignatures",
"claGroupID": claGroupID,
"signature_project_id": claGroupID,
"signature_type": "ccla",
@@ -1721,7 +1735,7 @@ func (repo repository) GetCompanyIDsWithSignedCorporateSignatures(ctx context.Co
// GetUserSignatures returns a list of user signatures for the specified user
func (repo repository) GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams, pageSize int64) (*models.Signatures, error) {
f := logrus.Fields{
- "functionName": "GetUserSignatures",
+ "functionName": "v1.signatures.repository.GetUserSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
// This is the keys we want to match
@@ -1824,7 +1838,7 @@ func (repo repository) GetUserSignatures(ctx context.Context, params signatures.
func (repo repository) AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "AddCLAManager",
+ "functionName": "v1.signatures.repository.AddCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"claManagerID": claManagerID,
@@ -1892,7 +1906,7 @@ func (repo repository) AddCLAManager(ctx context.Context, signatureID, claManage
func (repo repository) RemoveCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "RemoveCLAManager",
+ "functionName": "v1.signatures.repository.RemoveCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"claManagerID": claManagerID,
@@ -1970,7 +1984,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
projectID := claGroupModel.ProjectID
projectName := claGroupModel.ProjectName
f := logrus.Fields{
- "functionName": "UpdateApprovalList",
+ "functionName": "v1.signatures.repository.UpdateApprovalList",
"projectID": projectID,
"companyID": companyID,
}
@@ -1978,30 +1992,11 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
signed, approved := true, true
pageSize := int64(10)
- log.WithFields(f).Debugf("querying database for approval list details using company ID: %s project ID: %s, type: ccla, signed: true, approved: true",
- companyID, projectID)
- sortOrder := utils.SortOrderAscending
- sigs, sigErr := repo.GetProjectCompanySignatures(ctx, companyID, projectID, &signed, &approved, nil, &sortOrder, &pageSize)
- if sigErr != nil {
- return nil, sigErr
- }
-
- if sigs == nil || sigs.Signatures == nil {
- msg := fmt.Sprintf("unable to locate signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- companyID, projectID, signed, approved)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
- }
-
- if len(sigs.Signatures) > 1 {
- log.WithFields(f).Warnf("more than 1 CCLA signature returned for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t - expecting zero or 1 - using first record",
- companyID, projectID, signed, approved)
- }
// Get CCLA signature - For Approval List info
cclaSignature, err := repo.GetCorporateSignature(ctx, projectID, companyID)
- if err != nil {
- msg := "unable to get corporate signature"
+ if err != nil || cclaSignature == nil {
+ msg := fmt.Sprintf("unable to get corporate signature for CLA Group: %s and company: %s", projectID, companyID)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -2015,7 +2010,6 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
// Just grab and use the first one - need to figure out conflict resolution if more than one
- sig := sigs.Signatures[0]
expressionAttributeNames := map[string]*string{}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
haveAdditions := false
@@ -2034,46 +2028,42 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Keep track of gerrit users under a give CLA Group
var gerritICLAECLAs []string
- log.WithFields(f).Debug("aggregating ICLA and ECLA gerrit users...")
- gerritIclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeICLA)
-
- if err != nil {
- msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectID, utils.ClaTypeICLA)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
- }
-
- if gerritIclaUsers != nil {
- for _, member := range gerritIclaUsers.Members {
- gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
- }
- }
-
- gerritEclaUsers, err := repo.gerritService.GetUsersOfGroup(ctx, &authUser, projectID, utils.ClaTypeECLA)
-
- if err != nil {
- msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectID, utils.ClaTypeECLA)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
- }
-
- if gerritEclaUsers != nil {
- for _, member := range gerritEclaUsers.Members {
- gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ // Only load the gerrit user information, which is costly, if we have updates to remove email or email domains
+ if params.RemoveEmailApprovalList != nil || params.RemoveDomainApprovalList != nil {
+
+ goRoutines := 2
+ gerritResultChannel := make(chan *GerritUserResponse, goRoutines)
+ gerritQueryStartTime, _ := utils.CurrentTime()
+ go repo.getGerritUsers(ctx, &authUser, projectID, utils.ClaTypeICLA, gerritResultChannel)
+ go repo.getGerritUsers(ctx, &authUser, projectID, utils.ClaTypeECLA, gerritResultChannel)
+
+ log.WithFields(f).Debug("waiting on gerrit user query results from 2 go routines...")
+ for i := 0; i < goRoutines; i++ {
+ results := <-gerritResultChannel
+ log.WithFields(f).Debugf("received gerrit user query results response for %s - took: %+v", results.queryType, time.Since(gerritQueryStartTime))
+ if results.Error != nil {
+ log.WithFields(f).WithError(results.Error).Warnf("problem retrieving gerrit users for %s, error: %+v", results.queryType, results.Error)
+ return nil, results.Error
+ }
+ for _, member := range results.gerritGroupResponse.Members {
+ gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ }
+ log.WithFields(f).Debugf("updated gerrit user query results response for %s - list size is %d...", results.queryType, len(gerritICLAECLAs))
}
+ log.WithFields(f).Debugf("received the gerrit user query results from %d go routines...", goRoutines)
}
// If we have an add or remove email list...we need to run an update for this column
if params.AddEmailApprovalList != nil || params.RemoveEmailApprovalList != nil {
columnName := "email_whitelist"
- attrList := buildApprovalAttributeList(ctx, sig.EmailApprovalList, params.AddEmailApprovalList, params.RemoveEmailApprovalList)
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.EmailApprovalList, params.AddEmailApprovalList, params.RemoveEmailApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
var rmColErr error
- sig, rmColErr = repo.removeColumn(ctx, sig.SignatureID, columnName)
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
if rmColErr != nil {
msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- columnName, companyID, projectID, signed, approved)
+ columnName, companyID, projectID, true, true)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -2118,15 +2108,15 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
if signErr != nil {
- log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
+ log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", cclaSignature.SignatureID, signErr)
return
}
// update user by email
// send email
- eclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, projectName)
- log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- body, err := utils.RenderTemplate(claGroupModel.Version, InvalidateSignatureTemplateName,
+ eclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for '%s' on project '%s'", email, projectName)
+ log.WithFields(f).Debugf("sending invalidation email to user: '%s'", email)
+ body, templateErr := utils.RenderTemplate(claGroupModel.Version, InvalidateSignatureTemplateName,
InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
RecipientName: utils.GetBestUsername(claUser),
ClaType: utils.ClaTypeCCLA,
@@ -2134,13 +2124,13 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
RemovalCriteria: approvalList.Criteria,
ProjectName: projectName,
})
- if err != nil {
- log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
+ if templateErr != nil {
+ log.WithFields(f).WithError(templateErr).Debugf("unable to render invalidation notice for user: '%s'", email)
return
}
- err = utils.SendEmail(eclaEmailSubject, body, []string{email})
- if err != nil {
- log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
+ sendEmailErr := utils.SendEmail(eclaEmailSubject, body, []string{email})
+ if sendEmailErr != nil {
+ log.WithFields(f).WithError(sendEmailErr).Warnf("unable to send email invalidation notice to user: '%s'", email)
return
}
@@ -2155,21 +2145,21 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
// invalidate icla
- log.WithFields(f).Debugf("invalidating icla...:user: %+v", claUser)
+ log.WithFields(f).Debugf("invalidating icla for user: %+v", claUser)
if claUser != nil {
icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
if iclaErr != nil || icla == nil {
- log.WithFields(f).Debugf("unable to get icla signature for user: %s ", email)
+ log.WithFields(f).Debugf("unable to get icla signature for user: '%s'", email)
}
if icla != nil {
- note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal ", utils.GetBestUsername(claManager), email)
- err := repo.InvalidateProjectRecord(ctx, icla.SignatureID, note)
- if err != nil {
- log.WithFields(f).Warnf("unable to invalidate record for user:%s ", email)
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by '%s' due to '%s' removal ", utils.GetBestUsername(claManager), email)
+ invalidateErr := repo.InvalidateProjectRecord(ctx, icla.SignatureID, note)
+ if invalidateErr != nil {
+ log.WithFields(f).WithError(invalidateErr).Warnf("unable to invalidate record for user: '%s' ", email)
}
// send email
- log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
+ log.WithFields(f).Debugf("sending invalidation email to user: '%s'", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
RecipientName: utils.GetBestUsername(claUser),
@@ -2179,23 +2169,23 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
ProjectName: projectName,
})
if renderErr != nil {
- log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
+ log.WithFields(f).Debugf("unable to render invalidation notice for user: '%s' ", email)
return
}
- iclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, projectName)
- err = utils.SendEmail(iclaEmailSubject, body, []string{email})
- if err != nil {
- log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
+ iclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for '%s' on '%s'", email, projectName)
+ sendEmailErr := utils.SendEmail(iclaEmailSubject, body, []string{email})
+ if sendEmailErr != nil {
+ log.WithFields(f).WithError(sendEmailErr).Warnf("unable to send email invalidation notice to user: '%s'", email)
return
}
}
}
//update gerrit permissions
- gerritUser, err := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
- if err != nil || gerritUser == nil {
+ gerritUser, getGerritUserErr := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
+ if getGerritUserErr != nil || gerritUser == nil {
msg := fmt.Sprintf("unable to get gerrit user by email : %s ", email)
- log.WithFields(f).Warn(msg)
+ log.WithFields(f).WithError(getGerritUserErr).Warn(msg)
return
}
iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, gerritUser.LfUsername, utils.ClaTypeICLA)
@@ -2219,14 +2209,14 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if params.AddDomainApprovalList != nil || params.RemoveDomainApprovalList != nil {
columnName := "domain_whitelist"
- attrList := buildApprovalAttributeList(ctx, sig.DomainApprovalList, params.AddDomainApprovalList, params.RemoveDomainApprovalList)
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.DomainApprovalList, params.AddDomainApprovalList, params.RemoveDomainApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
var rmColErr error
- sig, rmColErr = repo.removeColumn(ctx, sig.SignatureID, columnName)
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
if rmColErr != nil {
msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- columnName, companyID, projectID, signed, approved)
+ columnName, companyID, projectID, true, true)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -2256,14 +2246,14 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if params.AddGithubUsernameApprovalList != nil || params.RemoveGithubUsernameApprovalList != nil {
columnName := "github_whitelist"
- attrList := buildApprovalAttributeList(ctx, sig.GithubUsernameApprovalList, params.AddGithubUsernameApprovalList, params.RemoveGithubUsernameApprovalList)
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubUsernameApprovalList, params.AddGithubUsernameApprovalList, params.RemoveGithubUsernameApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
var rmColErr error
- sig, rmColErr = repo.removeColumn(ctx, sig.SignatureID, columnName)
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
if rmColErr != nil {
msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- columnName, companyID, projectID, signed, approved)
+ columnName, companyID, projectID, true, true)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -2286,7 +2276,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
log.WithFields(f).Debugf("Updating signature records for ghUsernameApporvalList: %+v ", params.RemoveGithubUsernameApprovalList)
signs, ghUserErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
- if sigErr != nil {
+ if ghUserErr != nil {
log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", ghUserErr)
return
}
@@ -2294,7 +2284,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to ghUsernames approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveGithubUsernameApprovalList)
signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
if signErr != nil {
- log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", sigs.Signatures[0].SignatureID, signErr)
+ log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", cclaSignature.SignatureID, signErr)
return
}
@@ -2315,14 +2305,14 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if params.AddGithubOrgApprovalList != nil || params.RemoveGithubOrgApprovalList != nil {
columnName := "github_org_whitelist"
- attrList := buildApprovalAttributeList(ctx, sig.GithubOrgApprovalList, params.AddGithubOrgApprovalList, params.RemoveGithubOrgApprovalList)
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubOrgApprovalList, params.AddGithubOrgApprovalList, params.RemoveGithubOrgApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
var rmColErr error
- sig, rmColErr = repo.removeColumn(ctx, sig.SignatureID, columnName)
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
if rmColErr != nil {
msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- columnName, companyID, projectID, signed, approved)
+ columnName, companyID, projectID, true, true)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -2340,10 +2330,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Action = utils.RemoveApprovals
approvalList.Version = claGroupModel.Version
// Get repositories by CLAGroup
- repositories, err := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
- if err != nil {
+ repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
+ if getRepoByCLAGroupErr != nil {
msg := fmt.Sprintf("unable to fetch repositories for claGroupID: %s ", projectID)
- log.WithFields(f).Warn(msg)
+ log.WithFields(f).WithError(getRepoByCLAGroupErr).Warn(msg)
return nil, errors.New(msg)
}
var ghOrgRepositories []*models.GithubRepository
@@ -2356,10 +2346,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
for _, ghOrgRepo := range ghOrgRepositories {
- ghOrg, err := repo.ghOrgRepo.GetGithubOrganization(ctx, ghOrgRepo.RepositoryOrganizationName)
- if err != nil {
+ ghOrg, getGHOrgErr := repo.ghOrgRepo.GetGithubOrganization(ctx, ghOrgRepo.RepositoryOrganizationName)
+ if getGHOrgErr != nil {
msg := fmt.Sprintf("unable to get gh org by name: %s ", ghOrgRepo.RepositoryOrganizationName)
- log.WithFields(f).Warn(msg)
+ log.WithFields(f).WithError(getGHOrgErr).Warn(msg)
return nil, errors.New(msg)
}
ghOrgs = append(ghOrgs, ghOrg)
@@ -2367,10 +2357,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
var ghUsernames []string
for _, ghOrg := range ghOrgs {
- ghOrgUsers, err := github.GetOrganizationMembers(ctx, ghOrg.OrganizationName, ghOrg.OrganizationInstallationID)
- if err != nil {
+ ghOrgUsers, getOrgMembersErr := github.GetOrganizationMembers(ctx, ghOrg.OrganizationName, ghOrg.OrganizationInstallationID)
+ if getOrgMembersErr != nil {
msg := fmt.Sprintf("unable to fetch ghOrgUsers for org: %s ", ghOrg.OrganizationName)
- log.WithFields(f).Warnf(msg)
+ log.WithFields(f).WithError(getOrgMembersErr).Warnf(msg)
return nil, errors.New(msg)
}
ghUsernames = append(ghUsernames, ghOrgUsers...)
@@ -2389,8 +2379,8 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Ensure at least one value is set for us to update
if !haveAdditions {
log.WithFields(f).Debugf("no updates required to any of the approved list values company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t - expecting at least something to update",
- companyID, projectID, signed, approved)
- return sig, nil
+ companyID, projectID, true, true)
+ return cclaSignature, nil
}
// Remove trailing comma from the expression, if present
@@ -2401,7 +2391,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
TableName: aws.String(repo.signatureTableName),
Key: map[string]*dynamodb.AttributeValue{
"signature_id": {
- S: aws.String(sig.SignatureID),
+ S: aws.String(cclaSignature.SignatureID),
},
},
ExpressionAttributeNames: expressionAttributeNames,
@@ -2419,34 +2409,22 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return nil, updateErr
}
- log.WithFields(f).Debugf("querying database for approval list details after update using company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- companyID, projectID, signed, approved)
-
- updatedSig, sigErr := repo.GetProjectCompanySignatures(ctx, companyID, projectID, &signed, &approved, nil, &sortOrder, &pageSize)
- if sigErr != nil {
- return nil, sigErr
- }
-
- if updatedSig == nil || updatedSig.Signatures == nil {
- msg := fmt.Sprintf("unable to locate signature after update for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
- companyID, projectID, signed, approved)
+ // Query the CCLA signature once again to load the most recent updates which include approval list updates from above
+ updatedSig, err := repo.GetCorporateSignature(ctx, projectID, companyID)
+ if err != nil || cclaSignature == nil {
+ msg := fmt.Sprintf("unable to get corporate signature for CLA Group: %s and company: %s", projectID, companyID)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
- if len(updatedSig.Signatures) > 1 {
- log.WithFields(f).Warnf("more than 1 CCLA signature returned after update for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t - expecting zero or 1 - using first record",
- companyID, projectID, signed, approved)
- }
-
// Just grab and use the first one - need to figure out conflict resolution if more than one
- return updatedSig.Signatures[0], nil
+ return updatedSig, nil
}
// invalidateSignatures is a helper function that invalidates signature records based on approval list
func (repo repository) invalidateSignatures(ctx context.Context, approvalList *ApprovalList, claManager *models.User) error {
f := logrus.Fields{
- "functionName": "invalidateSignatures",
+ "functionName": "v1.signatures.repository.invalidateSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": &approvalList,
}
@@ -2562,7 +2540,7 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
// getGerritUsersByEmail searches gerrit instances for users with given email
func (repo repository) getGerritUserByEmail(ctx context.Context, email string, gerritICLAECLAs []string) (*models.User, error) {
f := logrus.Fields{
- "functionName": "getGerritUserByEmailDomain",
+ "functionName": "v1.signatures.repository.getGerritUserByEmail",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"email": email,
}
@@ -2586,7 +2564,7 @@ func (repo repository) getGerritUserByEmail(ctx context.Context, email string, g
// verify UserApprovals checks user
func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) (*models.User, error) {
f := logrus.Fields{
- "functionName": "verifyUserApprovals",
+ "functionName": "v1.signatures.repository.verifyUserApprovals",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": userID,
}
@@ -2652,7 +2630,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
// removeColumn is a helper function to remove a given column when we need to zero out the column value - typically the approval list
func (repo repository) removeColumn(ctx context.Context, signatureID, columnName string) (*models.Signature, error) {
f := logrus.Fields{
- "functionName": "removeColumn",
+ "functionName": "v1.signatures.repository.removeColumn",
"signatureID": signatureID,
"columnName": columnName,
}
@@ -2694,7 +2672,7 @@ func (repo repository) removeColumn(ctx context.Context, signatureID, columnName
func (repo repository) AddSigTypeSignedApprovedID(ctx context.Context, signatureID string, val string) error {
f := logrus.Fields{
- "functionName": "AddSigTypeSignedApprovedID",
+ "functionName": "v1.signatures.repository.AddSigTypeSignedApprovedID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"sigtypeSignedApprovedID": val,
@@ -2726,7 +2704,7 @@ func (repo repository) AddSigTypeSignedApprovedID(ctx context.Context, signature
}
func (repo repository) AddUsersDetails(ctx context.Context, signatureID string, userID string) error {
f := logrus.Fields{
- "functionName": "AddUserDetails",
+ "functionName": "v1.signatures.repository.AddUserDetails",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"userID": userID,
@@ -2791,7 +2769,7 @@ func (repo repository) AddUsersDetails(ctx context.Context, signatureID string,
func (repo repository) AddSignedOn(ctx context.Context, signatureID string) error {
f := logrus.Fields{
- "functionName": "AddSignedOn",
+ "functionName": "v1.signatures.repository.AddSignedOn",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -2828,7 +2806,7 @@ func (repo repository) AddSignedOn(ctx context.Context, signatureID string) erro
func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
- "functionName": "GetClaGroupICLASignatures",
+ "functionName": "v1.signatures.repository.GetClaGroupICLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"searchTerm": utils.StringValue(searchTerm),
@@ -3054,7 +3032,7 @@ func (repo repository) addAdditionalICLAMetaData(f logrus.Fields, intermediateRe
func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
- "functionName": "GetClaGroupCorporateContributors",
+ "functionName": "v1.signatures.repository.GetClaGroupCorporateContributors",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"companyID": aws.StringValue(companyID),
@@ -3175,3 +3153,31 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
return out, nil
}
+
+// getGerritUsers is a helper function to fetch the list of gerrit users for the specified type - results are returned through the specified results channel
+func (repo repository) getGerritUsers(ctx context.Context, authUser *auth.User, projectSFID string, claType string, gerritResultChannel chan *GerritUserResponse) {
+ f := logrus.Fields{
+ "functionName": "v1.signatures.repository.getGerritUsers",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ }
+ log.WithFields(f).Debugf("querying gerrit for %s gerrit users...", claType)
+ gerritIclaUsers, getGerritQueryErr := repo.gerritService.GetUsersOfGroup(ctx, authUser, projectSFID, claType)
+ if getGerritQueryErr != nil {
+ msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectSFID, claType)
+ log.WithFields(f).WithError(getGerritQueryErr).Warn(msg)
+ gerritResultChannel <- &GerritUserResponse{
+ gerritGroupResponse: nil,
+ queryType: claType,
+ Error: errors.New(msg),
+ }
+ return
+ }
+
+ log.WithFields(f).Debugf("retrieved %d gerrit users for CLA type: %s...", len(gerritIclaUsers.Members), claType)
+ gerritResultChannel <- &GerritUserResponse{
+ gerritGroupResponse: gerritIclaUsers,
+ queryType: claType,
+ Error: nil,
+ }
+}
diff --git a/cla-backend-go/tests/utils_test.go b/cla-backend-go/tests/utils_test.go
index 94a8c1578..a180ad0a3 100644
--- a/cla-backend-go/tests/utils_test.go
+++ b/cla-backend-go/tests/utils_test.go
@@ -381,13 +381,6 @@ func TestIsUUIDv4True(t *testing.T) {
assert.True(t, utils.IsUUIDv4(v4.String()), fmt.Sprintf("%s is a v4 UUID", v4.String()))
}
-func TestIsUUIDv4LikeV2(t *testing.T) {
- var b byte = 'b'
- v2, err := uuid.NewV2(b)
- assert.Nil(t, err, "NewV4 UUID is nil")
- assert.False(t, utils.IsUUIDv4(v2.String()), fmt.Sprintf("%s is not a v4 UUID", v2.String()))
-}
-
func TestIsUUIDv4LikeSFID(t *testing.T) {
sfid := "0014100000TdznWAAR"
assert.False(t, utils.IsUUIDv4(sfid), fmt.Sprintf("%s is not v4 UUID", sfid))
diff --git a/cla-backend-go/utils/signature_utils.go b/cla-backend-go/utils/signature_utils.go
index 1f9f9b30c..c75da7a31 100644
--- a/cla-backend-go/utils/signature_utils.go
+++ b/cla-backend-go/utils/signature_utils.go
@@ -15,7 +15,7 @@ func CurrentUserInACL(authUser *auth.User, managers []v1Models.User) bool {
f := logrus.Fields{
"functionName": "utils.CurrentUserInACL",
}
- log.WithFields(f).Debugf("checking if user: %+v is in the Signature ACL: %+v", authUser, managers)
+ log.WithFields(f).Debugf("checking if user: %s is in the Signature ACL: %+v", authUser.UserName, managers)
var inACL = false
for _, manager := range managers {
if manager.LfUsername == authUser.UserName {
From 5db178992b084c335269deb17292e0312bc8c68e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 8 Apr 2021 17:41:22 -0700
Subject: [PATCH 0222/1276] Resolved Event Log Entry for Edit CLA Group (#2863)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/handlers.go | 31 ++++++++++++++++++------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 97370a19e..e1bcf5dde 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -121,9 +121,25 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to lookup CLA Group by ID: %s", params.ClaGroupID), err))
}
+ projectCLAGroupModels, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ if projectCLAGroupErr != nil {
+ msg := fmt.Sprintf("unable to load the Project to CLA Group mappings for CLA Group: %s - is this CLA Group configured?", params.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return cla_group.NewUpdateClaGroupInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, projectCLAGroupErr))
+ }
+ if len(projectCLAGroupModels) == 0 {
+ msg := fmt.Sprintf("unable to load the Project to CLA Group mappings for CLA Group: %s - is this CLA Group configured?", params.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return cla_group.NewUpdateClaGroupInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerError(reqID, msg))
+ }
+ var projectSFIDList []string
+ for _, model := range projectCLAGroupModels {
+ projectSFIDList = append(projectSFIDList, model.ProjectSFID)
+ }
+
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, []string{claGroupModel.ProjectExternalID}, projectClaGroupsRepo) {
- msg := fmt.Sprintf("user %s does not have access to update an existing CLA Group with project scope of: %s", authUser.UserName, claGroupModel.FoundationSFID)
+ if !isUserHaveAccessToCLAProject(ctx, authUser, projectCLAGroupModels[0].FoundationSFID, projectSFIDList, projectClaGroupsRepo) {
+ msg := fmt.Sprintf("user %s does not have access to update an existing CLA Group with project scope of: %s or any of these: %s", authUser.UserName, projectCLAGroupModels[0].FoundationSFID, strings.Join(projectSFIDList, ","))
log.WithFields(f).Warn(msg)
return cla_group.NewUpdateClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -148,14 +164,15 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ClaGroupModel: claGroupModel,
- ProjectID: claGroup.ClaGroupID,
- LfUsername: authUser.UserName,
+ EventType: events.CLAGroupUpdated,
+ ClaGroupModel: claGroupModel,
+ ProjectID: claGroup.ClaGroupID,
+ ProjectSFID: projectCLAGroupModels[0].ProjectSFID,
+ ParentProjectSFID: projectCLAGroupModels[0].FoundationSFID,
+ LfUsername: authUser.UserName,
EventData: &events.CLAGroupUpdatedEventData{
NewClaGroupName: params.Body.ClaGroupName,
NewClaGroupDescription: params.Body.ClaGroupDescription,
-
OldClaGroupName: oldCLAGroupName,
OldClaGroupDescription: oldCLAGroupDescription,
},
From 16bd5b4eeac3d2347526dae4507246b6b78cc793 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 9 Apr 2021 00:33:53 +0300
Subject: [PATCH 0223/1276] [#2792]Refactor/Individual Signature
- Refactored logic with generic helper function for individual signature invalidation
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/models.go | 7 +-
cla-backend-go/signatures/repository.go | 235 +++++++++++-------------
cla-backend-go/utils/constants.go | 3 +
3 files changed, 118 insertions(+), 127 deletions(-)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 3faf360b3..41a3d0e2e 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -3,7 +3,10 @@
package signatures
-import v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+import (
+ "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+)
// SignatureCompanyID is a simple data model to hold the signature ID and come company details for CCLA's
type SignatureCompanyID struct {
@@ -34,6 +37,8 @@ type ApprovalList struct {
EmailApprovals []string
GHUsernames []string
GerritICLAECLAs []string
+ ICLAs []*models.IclaSignature
+ ECLAs []*models.Signature
}
// GerritUserResponse is a data structure to hold the gerrit user query response
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index a3a4046c0..30d071826 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -1982,7 +1982,6 @@ func (repo repository) RemoveCLAManager(ctx context.Context, signatureID, claMan
func (repo repository) UpdateApprovalList(ctx context.Context, claManager *models.User, claGroupModel *models.ClaGroup, companyID string, params *models.ApprovalList, eventArgs *events.LogEventArgs) (*models.Signature, error) { // nolint
projectID := claGroupModel.ProjectID
- projectName := claGroupModel.ProjectName
f := logrus.Fields{
"functionName": "v1.signatures.repository.UpdateApprovalList",
"projectID": projectID,
@@ -2079,6 +2078,10 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("removing email: %+v the approval list", params.RemoveDomainApprovalList)
var wg sync.WaitGroup
wg.Add(len(params.RemoveEmailApprovalList))
+ approvalList.Criteria = utils.EmailCriteria
+ approvalList.ApprovalList = params.RemoveEmailApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ approvalList.Version = claGroupModel.Version
for _, email := range params.RemoveEmailApprovalList {
go func(email string) {
defer wg.Done()
@@ -2103,81 +2106,21 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if len(signs.Signatures) > 0 {
- log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
- note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to email approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveEmailApprovalList)
-
- signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
- if signErr != nil {
- log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", cclaSignature.SignatureID, signErr)
- return
- }
-
- // update user by email
- // send email
- eclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for '%s' on project '%s'", email, projectName)
- log.WithFields(f).Debugf("sending invalidation email to user: '%s'", email)
- body, templateErr := utils.RenderTemplate(claGroupModel.Version, InvalidateSignatureTemplateName,
- InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
- RecipientName: utils.GetBestUsername(claUser),
- ClaType: utils.ClaTypeCCLA,
- ClaManager: utils.GetBestUsername(claManager),
- RemovalCriteria: approvalList.Criteria,
- ProjectName: projectName,
- })
- if templateErr != nil {
- log.WithFields(f).WithError(templateErr).Debugf("unable to render invalidation notice for user: '%s'", email)
- return
- }
- sendEmailErr := utils.SendEmail(eclaEmailSubject, body, []string{email})
- if sendEmailErr != nil {
- log.WithFields(f).WithError(sendEmailErr).Warnf("unable to send email invalidation notice to user: '%s'", email)
- return
- }
-
- //Log Event
- eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
- SignatureID: signs.Signatures[0].SignatureID,
- Email: email,
- CLAManager: claManager,
- CLAGroupID: signs.Signatures[0].ProjectID,
- }
- repo.eventsService.LogEventWithContext(ctx, eventArgs)
+ approvalList.ECLAs = signs.Signatures
}
- // invalidate icla
- log.WithFields(f).Debugf("invalidating icla for user: %+v", claUser)
-
if claUser != nil {
icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
if iclaErr != nil || icla == nil {
log.WithFields(f).Debugf("unable to get icla signature for user: '%s'", email)
}
if icla != nil {
- note := fmt.Sprintf("Signature invalidated (approved set to false) by '%s' due to '%s' removal ", utils.GetBestUsername(claManager), email)
- invalidateErr := repo.InvalidateProjectRecord(ctx, icla.SignatureID, note)
- if invalidateErr != nil {
- log.WithFields(f).WithError(invalidateErr).Warnf("unable to invalidate record for user: '%s' ", email)
- }
- // send email
- log.WithFields(f).Debugf("sending invalidation email to user: '%s'", email)
- body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
- InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
- RecipientName: utils.GetBestUsername(claUser),
- ClaType: utils.ClaTypeICLA,
- ClaManager: utils.GetBestUsername(claManager),
- RemovalCriteria: approvalList.Criteria,
- ProjectName: projectName,
- })
- if renderErr != nil {
- log.WithFields(f).Debugf("unable to render invalidation notice for user: '%s' ", email)
- return
- }
- iclaEmailSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for '%s' on '%s'", email, projectName)
- sendEmailErr := utils.SendEmail(iclaEmailSubject, body, []string{email})
- if sendEmailErr != nil {
- log.WithFields(f).WithError(sendEmailErr).Warnf("unable to send email invalidation notice to user: '%s'", email)
- return
- }
+ // Convert to IclSignature instance to leverage invalidateSignatures helper function
+ approvalList.ICLAs = []*models.IclaSignature{{
+ GithubUsername: icla.UserGHUsername,
+ LfUsername: icla.UserLFID,
+ SignatureID: icla.SignatureID,
+ }}
}
}
@@ -2227,7 +2170,25 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
updateExpression = updateExpression + " #D = :d, "
}
if params.RemoveDomainApprovalList != nil {
- var invalidateErr error
+ // Get ICLAs
+ log.WithFields(f).Debug("getting icla records... ")
+ iclas, iclaErr := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil, 0, "")
+ if iclaErr != nil {
+ log.WithFields(f).Warn("unable to get iclas")
+ }
+ // Get ECLAs
+ log.WithFields(f).Debug("getting ecla records... ")
+ companyProjectParams := signatures.GetProjectCompanyEmployeeSignaturesParams{
+ CompanyID: approvalList.CompanyID,
+ ProjectID: approvalList.ClaGroupID,
+ }
+
+ criteria := ApprovalCriteria{}
+ eclas, eclaErr := repo.GetProjectCompanyEmployeeSignatures(ctx, companyProjectParams, &criteria, int64(10))
+ if eclaErr != nil {
+ log.WithFields(f).Warnf("unable to get cclas for company: %s and project: %s ", approvalList.CompanyID, approvalList.ClaGroupID)
+ }
+
approvalList.Criteria = utils.EmailDomainCriteria
approvalList.ApprovalList = params.RemoveDomainApprovalList
approvalList.Action = utils.RemoveApprovals
@@ -2236,11 +2197,14 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.ClaGroupName = claGroupModel.ProjectName
approvalList.CompanyID = companyID
approvalList.Version = claGroupModel.Version
- invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
- if invalidateErr != nil {
- msg := fmt.Sprintf("unable to invalidate signatures based on Approval List : %+v ", approvalList)
- log.WithFields(f).Warn(msg)
+ if iclas != nil {
+ approvalList.ICLAs = iclas.List
+ }
+ if eclas != nil {
+ approvalList.ECLAs = eclas.Signatures
}
+
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
}
}
@@ -2267,6 +2231,13 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// if email removal update signature approvals
if params.RemoveGithubUsernameApprovalList != nil {
var wg sync.WaitGroup
+ approvalList.Criteria = utils.GitHubUsernameCriteria
+ approvalList.ApprovalList = params.RemoveGithubUsernameApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ approvalList.ClaGroupID = projectID
+ approvalList.ClaGroupName = claGroupModel.ProjectName
+ approvalList.CompanyID = companyID
+ approvalList.Version = claGroupModel.Version
wg.Add(len(params.RemoveGithubUsernameApprovalList))
for _, ghUsername := range params.RemoveGithubUsernameApprovalList {
go func(ghUsername string) {
@@ -2280,22 +2251,32 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", ghUserErr)
return
}
- log.WithFields(f).Debugf("Updating signature : %s ", signs.Signatures[0].SignatureID)
- note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to ghUsernames approval list removal : %+v", utils.GetBestUsername(claManager), params.RemoveGithubUsernameApprovalList)
- signErr := repo.InvalidateProjectRecord(ctx, signs.Signatures[0].SignatureID, note)
- if signErr != nil {
- log.WithFields(f).Debugf("error invalidating signature ID: %s error: %+v ", cclaSignature.SignatureID, signErr)
+ if signs.Signatures != nil {
+ approvalList.ECLAs = signs.Signatures
+ }
+ // Get ICLAs
+ claUser, claErr := repo.usersRepo.GetUserByGitHubUsername(ghUsername)
+ if claErr != nil {
+ log.WithFields(f).Debugf("unable to get User by GH Username: %s ", ghUsername)
return
}
-
- // Log Event
- eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
- SignatureID: signs.Signatures[0].SignatureID,
- GHUsername: ghUsername,
- CLAManager: claManager,
- CLAGroupID: signs.Signatures[0].ProjectID,
+ if claUser != nil {
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
+ if iclaErr != nil || icla == nil {
+ log.WithFields(f).Debugf("unable to get icla signature for user with ghUsername: %s ", ghUsername)
+ }
+ if icla != nil {
+ // Convert to IclSignature instance to leverage invalidateSignatrues helper function
+ approvalList.ICLAs = []*models.IclaSignature{{
+ GithubUsername: icla.UserGHUsername,
+ LfUsername: icla.UserLFID,
+ SignatureID: icla.SignatureID,
+ }}
+ }
}
- repo.eventsService.LogEventWithContext(ctx, eventArgs)
+
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+
}(ghUsername)
}
wg.Wait()
@@ -2324,7 +2305,6 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if params.RemoveGithubOrgApprovalList != nil {
- var invalidateErr error
approvalList.Criteria = utils.GitHubOrgCriteria
approvalList.ApprovalList = params.RemoveGithubOrgApprovalList
approvalList.Action = utils.RemoveApprovals
@@ -2367,12 +2347,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
approvalList.GHUsernames = utils.RemoveDuplicates(ghUsernames)
- invalidateErr = repo.invalidateSignatures(ctx, &approvalList, claManager)
- if invalidateErr != nil {
- msg := fmt.Sprintf("unable to invalidate signatures based on Approval List: %+v ", approvalList)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
- }
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
}
}
@@ -2422,26 +2397,19 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
// invalidateSignatures is a helper function that invalidates signature records based on approval list
-func (repo repository) invalidateSignatures(ctx context.Context, approvalList *ApprovalList, claManager *models.User) error {
+func (repo repository) invalidateSignatures(ctx context.Context, approvalList *ApprovalList, claManager *models.User, eventArgs *events.LogEventArgs) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.invalidateSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": &approvalList,
}
- // Get ICLAs
- log.WithFields(f).Debug("getting icla records... ")
- iclas, err := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil, 0, "")
- if err != nil {
- log.WithFields(f).Warn("unable to get iclas")
- }
-
- if iclas != nil {
+ if approvalList.ICLAs != nil {
var iclaWg sync.WaitGroup
//Iterate iclas
- iclaWg.Add(len(iclas.List))
+ iclaWg.Add(len(approvalList.ICLAs))
log.WithFields(f).Debug("invalidating signature icla records... ")
- for _, icla := range iclas.List {
+ for _, icla := range approvalList.ICLAs {
go func(icla *models.IclaSignature) {
defer iclaWg.Done()
signature, sigErr := repo.GetSignature(ctx, icla.SignatureID)
@@ -2461,7 +2429,7 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
email := getBestEmail(user)
// send email
- domainSubject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, approvalList.ClaGroupName)
+ subject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, approvalList.ClaGroupName)
log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
@@ -2474,36 +2442,31 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
return
}
- err = utils.SendEmail(domainSubject, body, []string{email})
+ err := utils.SendEmail(subject, body, []string{email})
if err != nil {
log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
return
}
+
+ // Log Event
+ eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
+ SignatureID: icla.SignatureID,
+ CLAManager: claManager,
+ CLAGroupID: signature.ProjectID,
+ Email: email,
+ }
+ repo.eventsService.LogEventWithContext(ctx, eventArgs)
}(icla)
}
iclaWg.Wait()
}
- // Get ECLAs
- log.WithFields(f).Debug("getting ecla records... ")
- companyProjectParams := signatures.GetProjectCompanyEmployeeSignaturesParams{
- CompanyID: approvalList.CompanyID,
- ProjectID: approvalList.ClaGroupID,
- }
-
- criteria := ApprovalCriteria{}
- eclas, err := repo.GetProjectCompanyEmployeeSignatures(ctx, companyProjectParams, &criteria, int64(10))
- if err != nil {
- log.WithFields(f).Warnf("unable to get cclas for company: %s and project: %s ", approvalList.CompanyID, approvalList.ClaGroupID)
- return err
- }
-
- if eclas != nil {
+ if approvalList.ECLAs != nil {
var eclaWg sync.WaitGroup
log.WithFields(f).Debug("invalidating signature ecla records... ")
// Iterate eclas
- eclaWg.Add(len(eclas.Signatures))
- for _, ecla := range eclas.Signatures {
+ eclaWg.Add(len(approvalList.ECLAs))
+ for _, ecla := range approvalList.ECLAs {
go func(ecla *models.Signature) {
defer eclaWg.Done()
// Grab user record
@@ -2518,23 +2481,36 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
email := getBestEmail(user)
// send email
+ subject := fmt.Sprintf("EasyCLA: Employee Acknowledgement invalidated for %s on %s", email, approvalList.ClaGroupName)
log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- _, err := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
RecipientName: utils.GetBestUsername(user),
ClaType: utils.ClaTypeECLA,
ClaManager: utils.GetBestUsername(claManager),
RemovalCriteria: approvalList.Criteria,
})
- if err != nil {
+ if renderErr != nil {
log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
return
}
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
+ return
+ }
+ // Log Event
+ eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
+ SignatureID: ecla.SignatureID,
+ CLAManager: claManager,
+ CLAGroupID: ecla.ProjectID,
+ Email: email,
+ }
+ repo.eventsService.LogEventWithContext(ctx, eventArgs)
}(ecla)
}
eclaWg.Wait()
}
- return nil
}
// getGerritUsersByEmail searches gerrit instances for users with given email
@@ -2622,6 +2598,13 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
}
}
}
+ } else if approvalList.Criteria == utils.GitHubUsernameCriteria || approvalList.Criteria == utils.EmailCriteria {
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), approvalList.Criteria)
+ err := repo.InvalidateProjectRecord(ctx, signatureID, note)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
+ return user, err
+ }
}
return user, nil
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 2336e11db..c91af9f7a 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -164,3 +164,6 @@ const AddApprovals = "AddApprovals"
//RemoveApprovals is an action for removing approvals
const RemoveApprovals = "RemoveApprovals"
+
+//GitHubUsernameCriteria represents criteria based on GH username
+const GitHubUsernameCriteria = "GitHubUsername"
From 71b020cf81c911c607fd47b868d58205c9fbcf50 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Sat, 10 Apr 2021 21:20:51 +0300
Subject: [PATCH 0224/1276] [#2792] Bug/Update Approval List (#2864)
- Resolved nil pointer issue caused with no gerrit users found
- Resolved wrong user search by using user_email string set field rather than lf_email
Signed-off-by: Harold Wanyama
---
cla-backend-go/go.sum | 1 +
cla-backend-go/signatures/models.go | 6 +++
cla-backend-go/signatures/repository.go | 60 ++++++++++++++++---------
3 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 70303eb34..d094865eb 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -103,6 +103,7 @@ github.com/communitybridge/easycla v1.0.145 h1:ikhBSsOeEL2u3/EoyDsufh/j3HkjfFTiX
github.com/communitybridge/easycla v2.0.10+incompatible h1:6eRJ5fxrMxRZHBkg8piYo+zHTcSowMrP85nZXzp5mpA=
github.com/communitybridge/easycla v2.0.16+incompatible h1:I0hEApDh4IvlwRPyHV1LOsSYlSPbqBsGszjSTHwkdak=
github.com/communitybridge/easycla v2.0.19+incompatible h1:HLaNt3jGDXPh3Au+rW/HKbJNkQf3daboVIrP9G6WYQ4=
+github.com/communitybridge/easycla v2.0.29+incompatible h1:kEply0yEyhsflUFDo7DZ0XBpncNPnsdgy6zn6ujkReY=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 41a3d0e2e..ae46a2304 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -47,3 +47,9 @@ type GerritUserResponse struct {
queryType string
Error error
}
+
+// ICLAUserResponse is struct that supports ICLAUsers
+type ICLAUserResponse struct {
+ ICLASignature *models.IclaSignature
+ Error error
+}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 30d071826..a8c9158b6 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2042,12 +2042,12 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("received gerrit user query results response for %s - took: %+v", results.queryType, time.Since(gerritQueryStartTime))
if results.Error != nil {
log.WithFields(f).WithError(results.Error).Warnf("problem retrieving gerrit users for %s, error: %+v", results.queryType, results.Error)
- return nil, results.Error
- }
- for _, member := range results.gerritGroupResponse.Members {
- gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ } else {
+ for _, member := range results.gerritGroupResponse.Members {
+ gerritICLAECLAs = append(gerritICLAECLAs, member.Username)
+ }
+ log.WithFields(f).Debugf("updated gerrit user query results response for %s - list size is %d...", results.queryType, len(gerritICLAECLAs))
}
- log.WithFields(f).Debugf("updated gerrit user query results response for %s - list size is %d...", results.queryType, len(gerritICLAECLAs))
}
log.WithFields(f).Debugf("received the gerrit user query results from %d go routines...", goRoutines)
}
@@ -2086,8 +2086,8 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
go func(email string) {
defer wg.Done()
log.WithFields(f).Debugf("getting cla user record for email: %s ", email)
- claUser, userErr := repo.usersRepo.GetUserByEmail(email)
- if userErr != nil || claUser == nil {
+ userSearch, userErr := repo.usersRepo.SearchUsers("user_emails", email, false)
+ if userErr != nil || userSearch == nil {
log.WithFields(f).Debugf("error getting user by email: %s ", email)
return
}
@@ -2109,21 +2109,41 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.ECLAs = signs.Signatures
}
- if claUser != nil {
- icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
- if iclaErr != nil || icla == nil {
- log.WithFields(f).Debugf("unable to get icla signature for user: '%s'", email)
- }
- if icla != nil {
- // Convert to IclSignature instance to leverage invalidateSignatures helper function
- approvalList.ICLAs = []*models.IclaSignature{{
- GithubUsername: icla.UserGHUsername,
- LfUsername: icla.UserLFID,
- SignatureID: icla.SignatureID,
- }}
+ if len(userSearch.Users) > 0 {
+ // Try and grab iclaSignature records for users
+ results := make(chan *ICLAUserResponse, len(userSearch.Users))
+ go func() {
+ defer close(results)
+ for _, user := range userSearch.Users {
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, user.UserID)
+ if iclaErr != nil || icla == nil {
+ results <- &ICLAUserResponse{
+ Error: fmt.Errorf("unable to get icla for user: %s ", user.UserID),
+ }
+ } else {
+ results <- &ICLAUserResponse{
+ ICLASignature: &models.IclaSignature{
+ GithubUsername: icla.UserGHUsername,
+ LfUsername: icla.UserLFID,
+ SignatureID: icla.SignatureID,
+ },
+ }
+ }
+ }
+ }()
+
+ for result := range results {
+ if result.Error == nil {
+ log.WithFields(f).Debug("processing icla...")
+ approvalList.ICLAs = append(approvalList.ICLAs, result.ICLASignature)
+ }
}
+
}
+ // Invalidate signatures
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+
//update gerrit permissions
gerritUser, getGerritUserErr := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
if getGerritUserErr != nil || gerritUser == nil {
@@ -3146,7 +3166,7 @@ func (repo repository) getGerritUsers(ctx context.Context, authUser *auth.User,
}
log.WithFields(f).Debugf("querying gerrit for %s gerrit users...", claType)
gerritIclaUsers, getGerritQueryErr := repo.gerritService.GetUsersOfGroup(ctx, authUser, projectSFID, claType)
- if getGerritQueryErr != nil {
+ if getGerritQueryErr != nil || gerritIclaUsers == nil {
msg := fmt.Sprintf("unable to fetch gerrit users for claGroup: %s , claType: %s ", projectSFID, claType)
log.WithFields(f).WithError(getGerritQueryErr).Warn(msg)
gerritResultChannel <- &GerritUserResponse{
From 5221e5aba50bc6854448ae1aafeb6d329ba18e1f Mon Sep 17 00:00:00 2001
From: David Deal
Date: Sat, 10 Apr 2021 11:34:34 -0700
Subject: [PATCH 0225/1276] Added/Updated Logging for GitHub/Repository APIs
(#2865)
Signed-off-by: David Deal
---
.../github_organizations/helpers.go | 6 ++--
cla-backend-go/repositories/repository.go | 28 ++++++++---------
.../utils/utils_user_auth_lambda.go | 2 +-
.../utils/utils_user_auth_standalone.go | 4 +--
cla-backend-go/v2/repositories/handlers.go | 1 +
cla-backend-go/v2/repositories/service.go | 30 +++++++++++--------
6 files changed, 39 insertions(+), 32 deletions(-)
diff --git a/cla-backend-go/github_organizations/helpers.go b/cla-backend-go/github_organizations/helpers.go
index 94ae540ce..29906cce9 100644
--- a/cla-backend-go/github_organizations/helpers.go
+++ b/cla-backend-go/github_organizations/helpers.go
@@ -34,7 +34,7 @@ func buildGithubOrganizationListModels(ctx context.Context, githubOrganizations
go func(ghorg *models.GithubOrganization) {
defer wg.Done()
ghorg.GithubInfo = &models.GithubOrganizationGithubInfo{}
- log.WithFields(f).Debugf("Loading GitHub organization details: %s...", ghorg.OrganizationName)
+ log.WithFields(f).Debugf("loading GitHub organization details: %s...", ghorg.OrganizationName)
user, err := github.GetUserDetails(ghorg.OrganizationName)
if err != nil {
ghorg.GithubInfo.Error = err.Error()
@@ -59,7 +59,7 @@ func buildGithubOrganizationListModels(ctx context.Context, githubOrganizations
}
if ghorg.OrganizationInstallationID != 0 {
- log.WithFields(f).Debugf("Loading GitHub repository list directly from GitHub based on the installation id: %d...", ghorg.OrganizationInstallationID)
+ log.WithFields(f).Debugf("loading GitHub repository list directly from GitHub based on the installation id: %d...", ghorg.OrganizationInstallationID)
list, err := github.GetInstallationRepositories(ctx, ghorg.OrganizationInstallationID)
if err != nil {
log.WithFields(f).Warnf("unable to get repositories from GitHub for the installation id: %d", ghorg.OrganizationInstallationID)
@@ -67,7 +67,7 @@ func buildGithubOrganizationListModels(ctx context.Context, githubOrganizations
return
}
- log.WithFields(f).Debugf("Found %d repositories from GitHUb using the installation id: %d...",
+ log.WithFields(f).Debugf("found %d repositories from GitHUb using the installation id: %d...",
len(list), ghorg.OrganizationInstallationID)
for _, repoInfo := range list {
ghorg.Repositories.List = append(ghorg.Repositories.List, &models.GithubRepositoryInfo{
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index 3c192a5c3..beaeeb1b6 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -85,7 +85,7 @@ type repo struct {
// AddGithubRepository adds the specified repository
func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.AddGitHubRepository",
+ "functionName": "v1.repositories.repository.AddGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"externalProjectID": externalProjectID,
"projectSFID": projectSFID,
@@ -158,7 +158,7 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
note := input.Note
f := logrus.Fields{
- "functionName": "repositories.repository.UpdateGitHubRepository",
+ "functionName": "v1.repositories.repository.UpdateGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
"externalProjectID": externalID,
@@ -331,7 +331,7 @@ func (r repo) DisableRepositoriesOfGithubOrganization(ctx context.Context, exter
// GetRepository by repository id
func (r *repo) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.GetRepository",
+ "functionName": "v1.repositories.repository.GetRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
}
@@ -368,7 +368,7 @@ func (r *repo) GetRepository(ctx context.Context, repositoryID string) (*models.
// GetRepositoryByName fetches the repository by repository name
func (r *repo) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.GetRepositoryByName",
+ "functionName": "v1.repositories.repository.GetRepositoryByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryName": repositoryName,
}
@@ -399,7 +399,7 @@ func (r *repo) GetRepositoryByName(ctx context.Context, repositoryName string) (
}
if len(results.Items) == 0 {
- log.WithFields(f).Warn("no repositories found with repository name")
+ log.WithFields(f).Warnf("no repositories found with repository name: %s", repositoryName)
return nil, &utils.GitHubRepositoryNotFound{
RepositoryName: repositoryName,
}
@@ -422,7 +422,7 @@ func (r *repo) GetRepositoryByName(ctx context.Context, repositoryName string) (
// GetRepositoryByCLAGroup gets the list of repositories based on the CLA Group ID
func (r *repo) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) ([]*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.GetRepositoryByCLAGroup",
+ "functionName": "v1.repositories.repository.GetRepositoryByCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"enabled": enabled,
@@ -473,7 +473,7 @@ func (r *repo) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string,
func (r *repo) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.GetRepositoriesByOrganizationName",
+ "functionName": "v1.repositories.repository.GetRepositoriesByOrganizationName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitHubOrgName": gitHubOrgName,
}
@@ -547,7 +547,7 @@ func (r repo) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID
// List github repositories of project by external/salesforce project id
func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.ListProjectRepositories",
+ "functionName": "v1.repositories.repository.ListProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"enabled": utils.BoolValue(enabled),
@@ -601,7 +601,7 @@ func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, e
// getProjectRepositories returns an array of GH repositories for the specified project ID
func (r repo) getProjectRepositories(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.getProjectRepositories",
+ "functionName": "v1.repositories.repository.getProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectID": projectID,
"enabled": enabled,
@@ -649,7 +649,7 @@ func (r repo) getProjectRepositories(ctx context.Context, projectID string, enab
// getRepositoriesByGithubOrg returns an array of GH repositories for the specified project ID
func (r repo) getRepositoriesByGithubOrg(ctx context.Context, githubOrgName string) ([]*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.getRepositoriesByGitHubOrg",
+ "functionName": "v1.repositories.repository.getRepositoriesByGitHubOrg",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"githubOrgName": githubOrgName,
}
@@ -694,7 +694,7 @@ func (r repo) getRepositoriesByGithubOrg(ctx context.Context, githubOrgName stri
// GetRepositoryByGithubID fetches the repository model by its external github id
func (r repo) GetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.repository.GetRepositoryByGitHubID",
+ "functionName": "v1.repositories.repository.GetRepositoryByGitHubID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"externalID": externalID,
"enabled": enabled,
@@ -757,7 +757,7 @@ func (r repo) disableGithubRepository(ctx context.Context, repositoryID string)
// setEnabledGithubRepository updates the existing repository record by setting the enabled flag to false
func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID string, enabled bool) error {
f := logrus.Fields{
- "functionName": "repositories.repository.setEnabledGitHubRepository",
+ "functionName": "v1.repositories.repository.setEnabledGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
"enabled": enabled,
@@ -827,7 +827,7 @@ func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID strin
// setEnabledGithubRepositoryWithCLAGroupID updates the existing repository record by setting the enabled flag to false
func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string, enabled bool) error {
f := logrus.Fields{
- "functionName": "repositories.repository.setEnabledGitHubRepositoryWithCLAGroupID",
+ "functionName": "v1.repositories.repository.setEnabledGitHubRepositoryWithCLAGroupID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
"claGroupID": claGroupID,
@@ -902,7 +902,7 @@ func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repo
// setEnabledGithubRepository updates the existing repository record by setting the enabled flag to false
func (r repo) setClaGroupIDGithubRepository(ctx context.Context, repositoryID, claGroupID string) error {
f := logrus.Fields{
- "functionName": "repositories.repository.setClaGroupIDGitHubRepository",
+ "functionName": "v1.repositories.repository.setClaGroupIDGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
"claGroupID": claGroupID,
diff --git a/cla-backend-go/utils/utils_user_auth_lambda.go b/cla-backend-go/utils/utils_user_auth_lambda.go
index 8e8570386..9b0ccee84 100644
--- a/cla-backend-go/utils/utils_user_auth_lambda.go
+++ b/cla-backend-go/utils/utils_user_auth_lambda.go
@@ -54,7 +54,7 @@ func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projec
log.WithFields(f).Debug("checking scope...")
val := user.IsUserAuthorized(auth.Project, projectSFID, true)
- log.WithFields(f).Debugf("user allowed: %T", val)
+ log.WithFields(f).Debugf("user allowed: %t", val)
return val
}
diff --git a/cla-backend-go/utils/utils_user_auth_standalone.go b/cla-backend-go/utils/utils_user_auth_standalone.go
index 09820aebf..b29e6a413 100644
--- a/cla-backend-go/utils/utils_user_auth_standalone.go
+++ b/cla-backend-go/utils/utils_user_auth_standalone.go
@@ -83,7 +83,7 @@ func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projec
log.WithFields(f).Debug("checking scope...")
val := user.IsUserAuthorized(auth.Project, projectSFID, true)
- log.WithFields(f).Debugf("user allowed: %T", val)
+ log.WithFields(f).Debugf("user allowed: %t", val)
return val
}
@@ -111,7 +111,7 @@ func IsUserAuthorizedForProject(ctx context.Context, user *auth.User, projectSFI
log.WithFields(f).Debug("checking scope...")
val := user.IsUserAuthorizedForProjectScope(projectSFID)
- log.WithFields(f).Debugf("user allowed: %T", val)
+ log.WithFields(f).Debugf("user allowed: %t", val)
return val
}
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 9ca7c850e..0d1800f69 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -113,6 +113,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequest(reqID, msg))
}
+ log.WithFields(f).Debugf("Adding GitHub repositories for project: %s", params.ProjectSFID)
results, err := service.AddGithubRepositories(ctx, params.ProjectSFID, params.GithubRepositoryInput)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryExists); ok {
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 92bc2b48c..ce5e73db2 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -72,7 +72,7 @@ func NewService(repo v1Repositories.Repository, pcgRepo projects_cla_groups.Repo
func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "AddGithubRepositories",
+ "functionName": "v2.repositories.service.AddGithubRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"claGroupID": utils.StringValue(input.ClaGroupID),
@@ -81,6 +81,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
"repositoryGithubIds": input.RepositoryGithubIds,
}
+ log.WithFields(f).Debugf("loading project by SFID: %s", projectSFID)
psc := v2ProjectService.GetClient()
project, err := psc.GetProject(projectSFID)
if err != nil {
@@ -88,12 +89,12 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
return nil, err
}
- var externalProjectID string
+ var parentProjectSFID string
if project.Parent == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
- externalProjectID = projectSFID
+ parentProjectSFID = projectSFID
} else {
- externalProjectID = project.Parent
+ parentProjectSFID = project.Parent
}
allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(aws.StringValue(input.ClaGroupID))
@@ -111,6 +112,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
if !valid {
return nil, fmt.Errorf("provided cla group id %s is not linked to project sfid %s", utils.StringValue(input.ClaGroupID), projectSFID)
}
+
org, err := s.ghOrgRepo.GetGithubOrganizationByName(ctx, utils.StringValue(input.GithubOrganizationName))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to get organization by name")
@@ -147,6 +149,9 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
log.WithFields(f).WithError(err).Warnf("unable to load repository by external ID: %d", repoGithubID)
return nil, err
}
+ f["repositoryName"] = ghRepo.FullName
+ f["repositoryURL"] = ghRepo.URL
+ f["repositoryGitHubID"] = repoGithubID
log.WithFields(f).Debugf("loaded GitHub repository by external id: %d - url: %s", repoGithubID, utils.StringValue(ghRepo.URL))
// Check if this repository exists in our database
@@ -206,8 +211,9 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
RepositoryURL: ghRepo.HTMLURL,
}
- addedModel, addErr := s.repo.AddGithubRepository(ctx, externalProjectID, projectSFID, in)
+ addedModel, addErr := s.repo.AddGithubRepository(ctx, parentProjectSFID, projectSFID, in)
if addErr != nil {
+ log.WithFields(f).WithError(addErr).Warnf("unable to add github repository: %s for project: %s", *ghRepo.FullName, projectSFID)
return nil, addErr
}
@@ -229,7 +235,7 @@ func (s *service) DisableRepository(ctx context.Context, repositoryID string) er
func (s *service) ListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.ListGithubRepositories, error) {
f := logrus.Fields{
- "functionName": "ListProjectRepositories",
+ "functionName": "v2.repositories.service.ListProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -348,7 +354,7 @@ func (s *service) GetRepositoryByName(ctx context.Context, repositoryName string
func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error) {
f := logrus.Fields{
- "functionName": "repositories.service.GetProtectedBranch",
+ "functionName": "v2.repositories.service.GetProtectedBranch",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
@@ -402,7 +408,7 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error) {
f := logrus.Fields{
- "functionName": "repositories.service.UpdateProtectedBranch",
+ "functionName": "v2.repositories.service.UpdateProtectedBranch",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
@@ -477,7 +483,7 @@ func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, reposi
func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID string) (*v1Models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.service.getGitHubRepo",
+ "functionName": "v2.repositories.service.getGitHubRepo",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
@@ -506,7 +512,7 @@ func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID s
func (s *service) getBranchProtectionRepositoryForOrgName(ctx context.Context, githubOrgName string) (*branch_protection.BranchProtectionRepository, error) {
f := logrus.Fields{
- "functionName": "repositories.service.getGitHubClientForOrgName",
+ "functionName": "v2.repositories.service.getGitHubClientForOrgName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"githubOrgName": githubOrgName,
}
@@ -541,7 +547,7 @@ func (s *service) getGithubOwner(ctx context.Context, branchProtectionRepository
// getRequiredProtectedBranchCheckStatus
func (s *service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *branch_protection.BranchProtectionRule, requiredChecks []string) []*v2Models.GithubRepositoryBranchProtectionStatusChecks {
f := logrus.Fields{
- "functionName": "repositories.service.getRequiredProtectedBranchCheckStatus",
+ "functionName": "v2.repositories.service.getRequiredProtectedBranchCheckStatus",
}
log.WithFields(f).Debug("querying GitHub for status checks...")
@@ -575,7 +581,7 @@ func (s *service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *br
func (s *service) DisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
f := logrus.Fields{
- "functionName": "DisableCLAGroupRepositories",
+ "functionName": "v2.repositories.service.DisableCLAGroupRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
}
From ea3627c42f9358c8e3cef4db798ca134b75794ed Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 12 Apr 2021 20:28:44 +0300
Subject: [PATCH 0226/1276] make default pageSize=100 for icla signature
listing (#2867)
Signed-off-by: makkalot
---
cla-backend-go/signatures/repository.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index a8c9158b6..9e9d563bf 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -52,8 +52,8 @@ const (
SignatureReferenceSearchIndex = "reference-signature-search-index"
HugePageSize = 10000
- DefaultPageSize = 10
- BigPageSize = 100
+ DefaultPageSize = 100
+ BigPageSize = 200
)
// SignatureRepository interface defines the functions for the github whitelist service
From 5705a35e40f5b87ac02c4be814df234ba7a8fb7d Mon Sep 17 00:00:00 2001
From: makkalot
Date: Tue, 13 Apr 2021 18:38:05 +0300
Subject: [PATCH 0227/1276] adding extra checks in case the old values are
still passed
Signed-off-by: makkalot
---
cla-backend-go/events/event_data.go | 8 ++---
cla-backend-go/events/event_data_test.go | 38 ++++++++++++++++++++++++
cla-backend-go/v2/cla_groups/handlers.go | 7 +++++
3 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 2a770422e..ca0da3437 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -681,12 +681,12 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
var nameUpdated bool
data := fmt.Sprintf("CLA Group ID: %s was updated by: %s", args.ProjectID, args.UserName)
- if ed.NewClaGroupName != "" {
+ if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName{
data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
nameUpdated = true
}
- if ed.NewClaGroupDescription != "" {
+ if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription{
if nameUpdated {
data = data + ","
} else {
@@ -1477,12 +1477,12 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
var nameUpdated, descriptionUpdated bool
message := "The CLA Group"
- if ed.NewClaGroupName != "" {
+ if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName{
message = message + " name was updated to : " + ed.NewClaGroupName
nameUpdated = true
}
- if ed.NewClaGroupDescription != "" {
+ if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription{
descriptionUpdated = true
if nameUpdated {
message = message + " and the description was updated to : " + ed.NewClaGroupDescription
diff --git a/cla-backend-go/events/event_data_test.go b/cla-backend-go/events/event_data_test.go
index 76c525572..e594bb0a0 100644
--- a/cla-backend-go/events/event_data_test.go
+++ b/cla-backend-go/events/event_data_test.go
@@ -32,6 +32,15 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
},
summaryStr: "The CLA Group name was updated to : updatedNameValue by the user john.",
},
+ {
+ name: "only name updated but old description still passed",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ NewClaGroupDescription: "oldDescriptionValue",
+ OldClaGroupDescription: "oldDescriptionValue",
+ },
+ summaryStr: "The CLA Group name was updated to : updatedNameValue by the user john.",
+ },
{
name: "only description updated",
eventData: &CLAGroupUpdatedEventData{
@@ -39,6 +48,15 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
},
summaryStr: "The CLA Group description was updated to : updatedDescriptionValue by the user john.",
},
+ {
+ name: "only description updated but old name still passed",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupDescription: "updatedDescriptionValue",
+ NewClaGroupName: "oldNameValue",
+ OldClaGroupName: "oldNameValue",
+ },
+ summaryStr: "The CLA Group description was updated to : updatedDescriptionValue by the user john.",
+ },
{
name: "name and description updated",
eventData: &CLAGroupUpdatedEventData{
@@ -78,6 +96,16 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
},
detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue.",
},
+ {
+ name: "only name updated but old description still passed",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupName: "updatedNameValue",
+ OldClaGroupName: "oldNameValue",
+ NewClaGroupDescription: "oldDescriptionValue",
+ OldClaGroupDescription: "oldDescriptionValue",
+ },
+ detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue.",
+ },
{
name: "only description updated",
eventData: &CLAGroupUpdatedEventData{
@@ -86,6 +114,16 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
},
detailStr: "CLA Group ID: projectIDValue was updated by: john with Description from : oldDescriptionValue to : updatedDescriptionValue.",
},
+ {
+ name: "only description updated but old name still passed",
+ eventData: &CLAGroupUpdatedEventData{
+ NewClaGroupDescription: "updatedDescriptionValue",
+ OldClaGroupDescription: "oldDescriptionValue",
+ NewClaGroupName: "oldNameValue",
+ OldClaGroupName: "oldNameValue",
+ },
+ detailStr: "CLA Group ID: projectIDValue was updated by: john with Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ },
{
name: "name and description updated",
eventData: &CLAGroupUpdatedEventData{
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index e1bcf5dde..801e5ab2d 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -121,6 +121,13 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to lookup CLA Group by ID: %s", params.ClaGroupID), err))
}
+ // check if there's any change at all
+ if params.Body.ClaGroupName == claGroupModel.ProjectName && params.Body.ClaGroupDescription == claGroupModel.ProjectDescription {
+ log.WithFields(f).Warn("no new values passed, nothing to change, aborting.")
+ return cla_group.NewUpdateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequest(reqID, "no new values passed, nothing to change, aborting."))
+ }
+
projectCLAGroupModels, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
if projectCLAGroupErr != nil {
msg := fmt.Sprintf("unable to load the Project to CLA Group mappings for CLA Group: %s - is this CLA Group configured?", params.ClaGroupID)
From 0dd618f8139d04d33dc13060fa0dda8823cf4fa0 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 14 Apr 2021 17:35:10 -0700
Subject: [PATCH 0228/1276] Added Additional Debugging for User Permissions
Issues (#2874)
---
cla-backend-go/events/event_data.go | 8 +-
cla-backend-go/v2/project/handlers.go | 112 ++++++++-------
cla-backend-go/v2/signatures/handlers.go | 173 +++++++++++++----------
3 files changed, 165 insertions(+), 128 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index ca0da3437..a99ffb2e3 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -681,12 +681,12 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
var nameUpdated bool
data := fmt.Sprintf("CLA Group ID: %s was updated by: %s", args.ProjectID, args.UserName)
- if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName{
+ if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName {
data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
nameUpdated = true
}
- if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription{
+ if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription {
if nameUpdated {
data = data + ","
} else {
@@ -1477,12 +1477,12 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
var nameUpdated, descriptionUpdated bool
message := "The CLA Group"
- if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName{
+ if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName {
message = message + " name was updated to : " + ed.NewClaGroupName
nameUpdated = true
}
- if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription{
+ if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription {
descriptionUpdated = true
if nameUpdated {
message = message + " and the description was updated to : " + ed.NewClaGroupDescription
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 4bc2ba68d..a02d48683 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -31,9 +31,11 @@ import (
// Configure establishes the middleware handlers for the project service
func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service Service, eventsService events.Service) { //nolint
// Get Projects
- api.ProjectGetProjectsHandler = project.GetProjectsHandlerFunc(func(params project.GetProjectsParams, user *auth.User) middleware.Responder {
+ api.ProjectGetProjectsHandler = project.GetProjectsHandlerFunc(func(params project.GetProjectsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+
// No auth checks - anyone can request the list of projects
projects, err := service.GetCLAGroups(ctx, &v1ProjectOps.GetProjectsParams{
HTTPRequest: params.HTTPRequest,
@@ -56,10 +58,18 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
})
// Get Project By ID
- api.ProjectGetProjectByIDHandler = project.GetProjectByIDHandlerFunc(func(params project.GetProjectByIDParams, user *auth.User) middleware.Responder {
+ api.ProjectGetProjectByIDHandler = project.GetProjectByIDHandlerFunc(func(params project.GetProjectByIDParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.project.handlers.ProjectGetProjectByIDHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSfdcID,
+ "userEmail": authUser.Email,
+ "userName": authUser.UserName,
+ }
+
claGroupModel, err := service.GetCLAGroupByID(ctx, params.ProjectSfdcID)
if err != nil {
@@ -73,34 +83,39 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewGetProjectByIDNotFound().WithXRequestID(reqID)
}
- if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
- return project.NewGetProjectByIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Project By ID with Project scope of %s",
- user.UserName, claGroupModel.ProjectExternalID),
- XRequestID: reqID,
- })
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user '%s' does not have access to Get Project By ID with Project scope of %s",
+ authUser.UserName, claGroupModel.ProjectExternalID)
+ return project.NewGetProjectByIDForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
result, err := v2ProjectModel(claGroupModel)
if err != nil {
- return project.NewGetProjectByIDInternalServerError().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
+ msg := fmt.Sprintf("unable to convert CLA Group '%s' with ID: '%s' to a response model", claGroupModel.ProjectName, claGroupModel.ProjectID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return project.NewGetProjectByIDInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
}
return project.NewGetProjectByIDOK().WithXRequestID(reqID).WithPayload(result)
})
- api.ProjectGetProjectsByExternalIDHandler = project.GetProjectsByExternalIDHandlerFunc(func(params project.GetProjectsByExternalIDParams, user *auth.User) middleware.Responder {
+ api.ProjectGetProjectsByExternalIDHandler = project.GetProjectsByExternalIDHandlerFunc(func(params project.GetProjectsByExternalIDParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForProjectTree(ctx, user, params.ExternalID, utils.ALLOW_ADMIN_SCOPE) {
- return project.NewGetProjectsByExternalIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Projects By External ID with Project scope of %s",
- user.UserName, params.ExternalID),
- XRequestID: reqID,
- })
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.project.handlers.ProjectGetProjectsByExternalIDHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "externalID": params.ExternalID,
+ "userEmail": authUser.Email,
+ "userName": authUser.UserName,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user '%s' does not have access to Get Projects By External ID with Project scope of '%s'",
+ authUser.UserName, params.ExternalID)
+ log.WithFields(f).Debug(msg)
+ return project.NewGetProjectsByExternalIDForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
claGroupModel, err := service.GetCLAGroupsByExternalID(ctx, &v1ProjectOps.GetProjectsByExternalIDParams{
@@ -119,20 +134,25 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewGetProjectsByExternalIDInternalServerError().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
if results.Projects == nil {
- return project.NewGetProjectsByExternalIDNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "404",
- Message: fmt.Sprintf("project not found with id. [%s]", params.ExternalID),
- XRequestID: reqID,
- })
+ msg := fmt.Sprintf("project not found with id: '%s]", params.ExternalID)
+ log.WithFields(f).Debug(msg)
+ return project.NewGetProjectsByExternalIDNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
}
return project.NewGetProjectsByExternalIDOK().WithXRequestID(reqID).WithPayload(results)
})
// Get Project By Name
- api.ProjectGetProjectByNameHandler = project.GetProjectByNameHandlerFunc(func(params project.GetProjectByNameParams, user *auth.User) middleware.Responder {
+ api.ProjectGetProjectByNameHandler = project.GetProjectByNameHandlerFunc(func(params project.GetProjectByNameParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.project.handlers.ProjectGetProjectByNameHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectName": params.ProjectName,
+ "userEmail": authUser.Email,
+ "userName": authUser.UserName,
+ }
claGroupModel, err := service.GetCLAGroupByName(ctx, params.ProjectName)
if err != nil {
@@ -142,13 +162,11 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewGetProjectByNameNotFound().WithXRequestID(reqID)
}
- if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
- return project.NewGetProjectByNameForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Get Project By Name with Project scope of %s",
- user.UserName, claGroupModel.ProjectExternalID),
- XRequestID: reqID,
- })
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user '%s' does not have access to Get Projects By Name with Project scope of '%s'",
+ authUser.UserName, claGroupModel.ProjectExternalID)
+ log.WithFields(f).Debug(msg)
+ return project.NewGetProjectByNameForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
result, err := v2ProjectModel(claGroupModel)
@@ -159,18 +177,18 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
})
// Delete Project By ID
- api.ProjectDeleteProjectByIDHandler = project.DeleteProjectByIDHandlerFunc(func(params project.DeleteProjectByIDParams, user *auth.User) middleware.Responder {
+ api.ProjectDeleteProjectByIDHandler = project.DeleteProjectByIDHandlerFunc(func(params project.DeleteProjectByIDParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "ProjectDeleteProjectByIDHandler",
+ "functionName": "v2.project.handlers.ProjectDeleteProjectByIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSfdcID,
- "userEmail": user.Email,
- "userName": user.UserName,
+ "userEmail": authUser.Email,
+ "userName": authUser.UserName,
}
log.WithFields(f).Debug("Processing delete request")
- utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
claGroupModel, err := service.GetCLAGroupByID(ctx, params.ProjectSfdcID)
if err != nil {
if err == ErrCLAGroupDoesNotExist {
@@ -179,13 +197,11 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
return project.NewDeleteProjectByIDBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
- if !utils.IsUserAuthorizedForProjectTree(ctx, user, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
- return project.NewDeleteProjectByIDForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Code: "403",
- Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Delete Project By ID with Project scope of %s",
- user.UserName, claGroupModel.ProjectExternalID),
- XRequestID: reqID,
- })
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, claGroupModel.ProjectExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user '%s' does not have access to Delete Project By ID with Project scope of %s",
+ authUser.UserName, claGroupModel.ProjectExternalID)
+ log.WithFields(f).Debug(msg)
+ return project.NewDeleteProjectByIDForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
err = service.DeleteCLAGroup(ctx, params.ProjectSfdcID)
@@ -198,7 +214,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLAGroupDeleted,
ClaGroupModel: claGroupModel,
- LfUsername: user.UserName,
+ LfUsername: authUser.UserName,
EventData: &events.CLAGroupDeletedEventData{},
})
@@ -283,7 +299,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "ProjectGetSFProjectInfoByIDHandler",
+ "functionName": "v2.project.handlers.ProjectGetSFProjectInfoByIDHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"userEmail": user.Email,
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 2ee9830b7..3364d6c09 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -50,8 +50,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetSignatureHandler = signatures.GetSignatureHandlerFunc(func(params signatures.GetSignatureParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetGitHubOrgWhitelistHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetGitHubOrgWhitelistHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -95,7 +96,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesUpdateApprovalListHandler",
+ "functionName": "v2.signatures.handlers.SignaturesUpdateApprovalListHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"projectSFID": params.ProjectSFID,
@@ -120,7 +121,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
// Must be in the Project|Organization Scope to see this - signature ACL is double-checked in the service level when the signature is loaded
if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, companyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to update Project Company Approval List with Project|Organization scope of %s | %s",
+ msg := fmt.Sprintf("user '%s' does not have access to update Project Company Approval List with Project|Organization scope of %s | %s",
authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return signatures.NewUpdateApprovalListForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -189,8 +190,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetGitHubOrgWhitelistHandler = signatures.GetGitHubOrgWhitelistHandlerFunc(func(params signatures.GetGitHubOrgWhitelistParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetGitHubOrgWhitelistHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetGitHubOrgWhitelistHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -226,8 +228,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesAddGitHubOrgWhitelistHandler = signatures.AddGitHubOrgWhitelistHandlerFunc(func(params signatures.AddGitHubOrgWhitelistParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesAddGitHubOrgWhitelistHandler",
+ "functionName": "v2.signatures.handlers.SignaturesAddGitHubOrgWhitelistHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -294,8 +297,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDeleteGitHubOrgWhitelistHandler = signatures.DeleteGitHubOrgWhitelistHandlerFunc(func(params signatures.DeleteGitHubOrgWhitelistParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDeleteGitHubOrgWhitelistHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDeleteGitHubOrgWhitelistHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -359,8 +363,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetProjectSignaturesHandler = signatures.GetProjectSignaturesHandlerFunc(func(params signatures.GetProjectSignaturesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetProjectSignaturesHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetProjectSignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"signatureType": params.SignatureType,
@@ -396,7 +401,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
if false {
log.WithFields(f).Debug("checking access control permissions for user...")
if !isUserHaveAccessToCLAGroupProjects(ctx, authUser, params.ClaGroupID, projectClaGroupsRepo, projectRepo) {
- msg := fmt.Sprintf("user %s is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
+ msg := fmt.Sprintf("user '%s' is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
log.Warn(msg)
return signatures.NewGetProjectSignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -438,8 +443,10 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetProjectCompanySignaturesHandler = signatures.GetProjectCompanySignaturesHandlerFunc(func(params signatures.GetProjectCompanySignaturesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetProjectCompanySignaturesHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetProjectCompanySignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
@@ -461,16 +468,11 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
})
}
- // Must be in the one of the above scopes to see this
- // - if project scope (like a PM)
- // - if project|organization scope (like CLA Manager, CLA Signatory)
- // - if organization scope (like company admin)
if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
msg := fmt.Sprintf("user %s is not authorized to view project company signatures any scope of project: %s, organization %s",
authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
- return signatures.NewGetProjectCompanySignaturesForbidden().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseForbidden(reqID, msg))
+ return signatures.NewGetProjectCompanySignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
log.WithFields(f).Debug("loading project company signatures...")
@@ -489,8 +491,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetProjectCompanyEmployeeSignaturesHandler = signatures.GetProjectCompanyEmployeeSignaturesHandlerFunc(func(params signatures.GetProjectCompanyEmployeeSignaturesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetProjectCompanyEmployeeSignaturesHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetProjectCompanyEmployeeSignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
@@ -500,33 +503,25 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
companyModel, err := companyService.GetCompany(ctx, params.CompanyID)
if err != nil {
- msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
+ msg := fmt.Sprintf("user lookup for company by ID: '%s' failed : %v", params.CompanyID, err)
log.Warn(msg)
if _, ok := err.(*utils.CompanyNotFound); ok {
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
- Code: "404",
- })
+ return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
- Code: "400",
- })
+ return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
if companyModel == nil {
msg := fmt.Sprintf("problem loading company by ID: %s", params.CompanyID)
log.WithFields(f).WithError(err).Warn(msg)
- return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFound(reqID, msg))
+ return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
}
log.WithFields(f).Debug("checking access control permissions...")
if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, params.ProjectSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
- msg := fmt.Sprintf("user %s is not authorized to view project company signatures any scope of project: %s, organization %s",
+ msg := fmt.Sprintf("user '%s' is not authorized to view project company signatures any scope of project or project|organization for project: '%s', organization '%s'",
authUser.UserName, params.ProjectSFID, params.CompanyID)
log.Warn(msg)
- return signatures.NewGetProjectCompanyEmployeeSignaturesForbidden().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseForbidden(reqID, msg))
+ return signatures.NewGetProjectCompanyEmployeeSignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
// Locate the CLA Group for the provided project SFID
@@ -576,7 +571,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetCompanySignaturesHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetCompanySignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": params.CompanyID,
"companyName": aws.StringValue(params.CompanyName),
@@ -663,8 +658,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesGetUserSignaturesHandler = signatures.GetUserSignaturesHandlerFunc(func(params signatures.GetUserSignaturesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetUserSignaturesHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetUserSignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": params.UserID,
"userName": aws.StringValue(params.UserName),
@@ -702,8 +698,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDownloadProjectSignatureEmployeeAsCSVHandler = signatures.DownloadProjectSignatureEmployeeAsCSVHandlerFunc(func(params signatures.DownloadProjectSignatureEmployeeAsCSVParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDownloadProjectSignatureEmployeeAsCSVHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDownloadProjectSignatureEmployeeAsCSVHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"companyID": params.CompanyID,
@@ -757,10 +754,20 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
})
}
+ // Lookup the Project to CLA Group mapping table entries - this will have the correct details
+ projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ // Should have at least one entry if we're setup correctly - it will have the foundation (parent project/project group) and project details set
+ if projectCLAGroupErr != nil || len(projectCLAGroupEntries) == 0 {
+ msg := fmt.Sprintf("unable to load project CLA Group mappings for CLA Group: %s - has this project been migrated to v2?", params.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ }
+ // All the records will point to the same parent SFID
+ f["foundationSFID"] = projectCLAGroupEntries[0].FoundationSFID
+
log.WithFields(f).Debug("checking access control permissions for user...")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
- msg := fmt.Sprintf(" user %s is not authorized to view project employee signatures any scope of project",
- authUser.UserName)
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, projectCLAGroupEntries[0].FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
+ msg := fmt.Sprintf(" user %s is not authorized to view project employee signatures any scope of project", authUser.UserName)
log.Warn(msg)
return signatures.NewDownloadProjectSignatureEmployeeAsCSVForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -799,8 +806,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesListClaGroupIclaSignatureHandler = signatures.ListClaGroupIclaSignatureHandlerFunc(func(params signatures.ListClaGroupIclaSignatureParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesListClaGroupIclaSignatureHandler",
+ "functionName": "v2.signatures.handlers.SignaturesListClaGroupIclaSignatureHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"searchTerm": utils.StringValue(params.SearchTerm),
@@ -867,8 +875,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesListClaGroupCorporateContributorsHandler = signatures.ListClaGroupCorporateContributorsHandlerFunc(func(params signatures.ListClaGroupCorporateContributorsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesListClaGroupCorporateContributorsHandler",
+ "functionName": "v2.signatures.handlers.SignaturesListClaGroupCorporateContributorsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"companyID": params.CompanyID,
@@ -919,14 +928,22 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
return signatures.NewListClaGroupCorporateContributorsOK().WithXRequestID(reqID).WithPayload(&models.CorporateContributorList{
List: []*models.CorporateContributor{}, // empty list
})
- //return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(
- // utils.ErrorResponseBadRequest(reqID, msg))
}
- f["foundationSFID"] = claGroupModel.FoundationSFID
+
+ // Lookup the Project to CLA Group mapping table entries - this will have the correct details
+ projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ // Should have at least one entry if we're setup correctly - it will have the foundation (parent project/project group) and project details set
+ if projectCLAGroupErr != nil || len(projectCLAGroupEntries) == 0 {
+ msg := fmt.Sprintf("unable to load project CLA Group mappings for CLA Group: %s - has this project been migrated to v2?", params.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ }
+ // All the records will point to the same parent SFID
+ f["foundationSFID"] = projectCLAGroupEntries[0].FoundationSFID
log.WithFields(f).Debug("checking access control permissions for user...")
- if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, claGroupModel.FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
- msg := fmt.Sprintf("user %s is not authorized to view project CCLA signatures any scope of project or project|organization scope with company ID: %s",
+ if !isUserHaveAccessToCLAProjectOrganization(ctx, authUser, projectCLAGroupEntries[0].FoundationSFID, companyModel.CompanyExternalID, projectClaGroupsRepo) {
+ msg := fmt.Sprintf("user '%s' is not authorized to view project CCLA signatures project scope or project|organization scope for company ID: %s",
authUser.UserName, companyModel.CompanyID)
log.Warn(msg)
return signatures.NewListClaGroupCorporateContributorsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -955,7 +972,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesGetSignatureSignedDocumentHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetSignatureSignedDocumentHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -998,8 +1015,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDownloadProjectSignatureICLAsHandler = signatures.DownloadProjectSignatureICLAsHandlerFunc(func(params signatures.DownloadProjectSignatureICLAsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDownloadProjectSignatureICLAsHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDownloadProjectSignatureICLAsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
}
@@ -1052,8 +1070,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDownloadProjectSignatureICLAAsCSVHandler = signatures.DownloadProjectSignatureICLAAsCSVHandlerFunc(func(params signatures.DownloadProjectSignatureICLAAsCSVParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDownloadProjectSignatureICLAAsCSVHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDownloadProjectSignatureICLAAsCSVHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
}
@@ -1089,7 +1108,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
log.WithFields(f).Debug("checking access control permissions for user...")
if !isUserHaveAccessToCLAGroupProjects(ctx, authUser, params.ClaGroupID, projectClaGroupsRepo, projectRepo) {
- msg := fmt.Sprintf("user %s is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
+ msg := fmt.Sprintf("user '%s' is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
log.Warn(msg)
return signatures.NewDownloadProjectSignatureICLAAsCSVForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -1119,8 +1138,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDownloadProjectSignatureCCLAsHandler = signatures.DownloadProjectSignatureCCLAsHandlerFunc(func(params signatures.DownloadProjectSignatureCCLAsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDownloadProjectSignatureCCLAsHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDownloadProjectSignatureCCLAsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
}
@@ -1172,8 +1192,9 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
api.SignaturesDownloadProjectSignatureCCLAAsCSVHandler = signatures.DownloadProjectSignatureCCLAAsCSVHandlerFunc(func(params signatures.DownloadProjectSignatureCCLAAsCSVParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "SignaturesDownloadProjectSignatureCCLAAsCSVHandler",
+ "functionName": "v2.signatures.handlers.SignaturesDownloadProjectSignatureCCLAAsCSVHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
}
@@ -1209,7 +1230,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
log.WithFields(f).Debug("checking access control permissions for user...")
if !isUserHaveAccessToCLAGroupProjects(ctx, authUser, params.ClaGroupID, projectClaGroupsRepo, projectRepo) {
- msg := fmt.Sprintf("user %s is not authorized to view project CCLA signatures any scope of project", authUser.UserName)
+ msg := fmt.Sprintf("user '%s' is not authorized to view project CCLA signatures any scope of project", authUser.UserName)
log.Warn(msg)
return signatures.NewDownloadProjectSignatureCCLAAsCSVForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -1253,7 +1274,7 @@ func getProjectIDsFromModels(f logrus.Fields, foundationSFID string, projectCLAG
// isUserHaveAccessOfSignedSignaturePDF returns true if the specified user has access to the provided signature, false otherwise
func isUserHaveAccessOfSignedSignaturePDF(ctx context.Context, authUser *auth.User, signature *v1Models.Signature, companyService company.IService, projectClaGroupRepo projects_cla_groups.Repository, projectRepo project.ProjectRepository) (bool, error) {
f := logrus.Fields{
- "functionName": "isUserHaveAccessOfSignedSignaturePDF",
+ "functionName": "v2.signatures.handlers.isUserHaveAccessOfSignedSignaturePDF",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUserName": authUser.UserName,
"authUserEmail": authUser.Email,
@@ -1364,7 +1385,7 @@ func errorResponse(reqID string, err error) *models.ErrorResponse {
// isUserHaveAccessToCLAGroupProjects is a helper function to determine if the user has access to the specified CLA Group projects
func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User, claGroupID string, projectClaGroupsRepo projects_cla_groups.Repository, projectRepo project.ProjectRepository) bool {
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAGroupProjects",
+ "functionName": "v2.signatures.handlers.isUserHaveAccessToCLAGroupProjects",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"userName": authUser.UserName,
@@ -1431,7 +1452,7 @@ func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User
// isUserHaveAccessToCLAProject is a helper function to determine if the user has access to the specified project
func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, projectSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAProject",
+ "functionName": "v2.signatures.handlers.isUserHaveAccessToCLAProject",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"userName": authUser.UserName,
@@ -1489,7 +1510,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
// isUserHaveAccessToCLAProjectOrganization is a helper function to determine if the user has access to the specified project and organization
func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *auth.User, projectSFID, organizationSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool {
f := logrus.Fields{
- "functionName": "isUserHaveAccessToCLAProjectOrganization",
+ "functionName": "v2.signatures.handlers.isUserHaveAccessToCLAProjectOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"organizationSFID": organizationSFID,
@@ -1497,40 +1518,40 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
"userEmail": authUser.Email,
}
- log.WithFields(f).Debug("testing if user has access to project SFID...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to project SFID...")
+ log.WithFields(f).Debugf("user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
return true
}
- log.WithFields(f).Debug("testing if user has access to project SFID tree...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID tree...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to project SFID tree...")
+ log.WithFields(f).Debugf("user %s/%s has access to project SFID tree...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to project SFID and organization SFID...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID and organization SFID...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectOrganization(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to project SFID and organization SFID...")
+ log.WithFields(f).Debugf("user %s/%s has access to project SFID and organization SFID...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to project SFID and organization SFID tree...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID and organization SFID tree...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to project SFID and organization SFID tree...")
+ log.WithFields(f).Debugf("user %s/%s has access to project SFID and organization SFID tree...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to organization SFID...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to organization SFID...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForOrganization(authUser, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to organization SFID...")
+ log.WithFields(f).Debugf("user %s/%s has access to organization SFID...", authUser.UserName, authUser.Email)
return true
}
// No luck so far...let's load up the Project => CLA Group mapping and check to see if the user has access to the
// other projects or the parent project group/foundation
- log.WithFields(f).Debug("user doesn't have direct access to the project only, project + organization, or organization only - loading CLA Group from project id...")
+ log.WithFields(f).Debugf("user %s/%s doesn't have direct access to the project only, project + organization, or organization only - loading CLA Group from project id...", authUser.UserName, authUser.Email)
projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
@@ -1543,26 +1564,26 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Check the foundation permissions
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
- log.WithFields(f).Debug("testing if user has access to parent foundation...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to parent foundation...")
+ log.WithFields(f).Debugf("user %s/%s has access to parent foundation...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to parent foundation tree...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation tree...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to parent foundation tree...")
+ log.WithFields(f).Debugf("user %s/%s has access to parent foundation tree...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to foundation SFID and organization SFID...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID and organization SFID...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectOrganization(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to foundation SFID and organization SFID...")
+ log.WithFields(f).Debugf("user %s/%s has access to foundation SFID and organization SFID...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("testing if user has access to foundation SFID and organization SFID tree...")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID and organization SFID tree...", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to foundation SFID and organization SFID tree...")
+ log.WithFields(f).Debugf("user %s/%s has access to foundation SFID and organization SFID tree...", authUser.UserName, authUser.Email)
return true
}
@@ -1576,12 +1597,12 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
f["projectIDs"] = strings.Join(projectSFIDs, ",")
- log.WithFields(f).Debug("testing if user has access to any cla group project + organization")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group project + organization", authUser.UserName, authUser.Email)
if utils.IsUserAuthorizedForAnyProjectOrganization(authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to at least of of the projects...")
+ log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects...", authUser.UserName, authUser.Email)
return true
}
- log.WithFields(f).Debug("exhausted project checks - user does not have access to project")
+ log.WithFields(f).Debugf("exhausted project checks - user %s/%s does not have access to project", authUser.UserName, authUser.Email)
return false
}
From c1dcb10e705b78f2b88e226634921260683af573 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 16 Apr 2021 00:48:45 +0300
Subject: [PATCH 0229/1276] [#2866] Bug/UnEnforce CLA (#2875)
- Updated Get GH project org repos logic that resolves the UI unenforcement of CLA
Signed-off-by: Harold Wanyama
---
cla-backend-go/github_organizations/service.go | 9 ++++++++-
cla-backend-go/repositories/service.go | 15 ++++++++++++++-
cla-backend-go/v2/github_organizations/service.go | 4 ++--
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index f810b7444..3fbdfb722 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -101,7 +101,14 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return nil, projErr
}
- if parentProjectSFID != projectSFID && parentProjectSFID != "" {
+ //Get SF Project
+ projectDetails, projDetailsErr := v2ProjectService.GetClient().GetProject(projectSFID)
+ if projDetailsErr != nil {
+ log.WithFields(f).Warnf("problem fetching parent project details for :%s ", projectSFID)
+ return nil, projDetailsErr
+ }
+
+ if parentProjectSFID != projectSFID && (projectDetails != nil && !utils.IsProjectHasRootParent(projectDetails)) {
log.WithFields(f).Debugf("found parent of projectSFID: %s to be %s. Searching github organization by parent SFID: %s...", projectSFID, parentProjectSFID, parentProjectSFID)
parentGithubModels, parentErr := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
if parentErr != nil {
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index 9c2cd36ed..4edfbcf59 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -156,7 +156,20 @@ func (s *service) ListProjectRepositories(ctx context.Context, externalProjectID
}
func (s *service) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
- return s.repo.GetRepository(ctx, repositoryID)
+ f := logrus.Fields{
+ "functionName": "v1.repository.GetRepository",
+ "repositoryID": repositoryID,
+ }
+ log.WithFields(f).Debug("Searching for repository...")
+ ghRepo, err := s.repo.GetRepository(ctx, repositoryID)
+ if err != nil || ghRepo != nil {
+ log.WithFields(f).WithError(err).Debug("unable to get repository")
+ return nil, err
+ }
+
+ log.WithFields(f).Debugf("Found repository : %+v ", ghRepo)
+
+ return ghRepo, nil
}
func (s *service) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 6a65e22c9..94e4350c2 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -208,7 +208,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
ConnectionStatus: Connected,
- Enabled: true,
+ Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
RepositoryGithubID: repoGithubID,
@@ -223,7 +223,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
} else {
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
ConnectionStatus: ConnectionFailure,
- Enabled: true,
+ Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
ClaGroupID: repo.RepositoryProjectID,
From 4a50ad301b892214a6f6884fbf39d63b054287b9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 15 Apr 2021 17:42:21 -0700
Subject: [PATCH 0230/1276] Added Date Created/Modified to Event Log (#2879)
- Added missing fields to match python event log
Signed-off-by: David Deal
---
cla-backend-go/events/repository.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index 5d8cec251..bfce950a4 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -88,7 +88,7 @@ func toDateFormat(t time.Time) string {
return t.Format("02-01-2006")
}
-// Create event will create event in database.
+// CreateEvent event will create event in database.
func (repo *repository) CreateEvent(event *models.Event) error {
f := logrus.Fields{
"functionName": "v1.events.repository.CreateEvent",
@@ -145,6 +145,8 @@ func (repo *repository) CreateEvent(event *models.Event) error {
addAttribute(input.Item, "event_time", currentTimeString)
addAttribute(input.Item, "event_date", toDateFormat(currentTime))
addAttribute(input.Item, "event_date_and_contains_pii", eventDateAndContainsPII)
+ addAttribute(input.Item, "date_created", toDateFormat(currentTime))
+ addAttribute(input.Item, "date_modified", toDateFormat(currentTime))
input.Item["contains_pii"] = &dynamodb.AttributeValue{BOOL: &event.ContainsPII}
input.Item["event_time_epoch"] = &dynamodb.AttributeValue{N: aws.String(strconv.FormatInt(currentTime.Unix(), 10))}
From 268ae5f18ba50a5cb5c8febe8d7fc4efd057df32 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 15 Apr 2021 19:00:20 -0700
Subject: [PATCH 0231/1276] Resolved [#2651] Event Log Updates (#2880)
- Updated event log text with correct user information
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 432 ++++++++++++++++++-----
cla-backend-go/events/event_data_test.go | 12 +-
2 files changed, 352 insertions(+), 92 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index a99ffb2e3..cfa984ff9 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -384,92 +384,153 @@ type ClaManagerRoleDeletedData struct {
// GetEventDetailsString . . .
func (ed *CLAGroupEnrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s (%s/%s) enabled the the project %s (%s) from the CLA Group %s (%s).",
- args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
+ data := fmt.Sprintf("The project %s (%s) was enrolled into the CLA Group %s (%s)", args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupUnenrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s (%s/%s) unenrolled the the project %s (%s) from the CLA Group %s (%s).",
- args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID), false
+ data := fmt.Sprintf("The project %s (%s) was unenrolled from the CLA Group %s (%s)", args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLAEnabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s (%s/%s) enabled the CLA Service for the project %s (%s)",
- args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+ data := fmt.Sprintf("The CLA Service for the project %s (%s) was enabled", args.ProjectName, args.ProjectID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLADisabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("%s (%s/%s) disabled the CLA Service for the project %s (%s)",
- args.UserName, args.LFUser.Name, args.UserModel.LfEmail, args.ProjectName, args.ProjectID), false
+ data := fmt.Sprintf("The CLA Service for the project %s (%s) was disabled", args.ProjectName, args.ProjectID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository: %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository: %s was added for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository %s was updated for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s by the user %s.", ed.RepositoryName, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s", ed.RepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *UserCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s added. User Details: %+v.", args.UserName, args.UserModel)
+ data := fmt.Sprintf("User was added : %+v", args.UserModel)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *UserUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("User: %s updated. User Details: %+v.", args.UserName, *args.UserModel), true
+ data := fmt.Sprintf("User details updated: %+v", *args.UserModel)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *UserDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s deleted. User ID: %s.", args.UserName, ed.DeletedUserID)
+ data := fmt.Sprintf("User ID: %s was deleted", ed.DeletedUserID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLRequestAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s added pending invite with ID: %s and Email: %s for Company: %s.",
+ data := fmt.Sprintf("User: %s added pending invite with ID: %s and Email: %s for Company: %s",
ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Access Aproved for User: %s, ID: %s, Email: %s Company Group: %s.",
+ data := fmt.Sprintf("Access Aproved for User: %s, ID: %s, Email: %s Company Group: %s",
ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -477,19 +538,31 @@ func (ed *CompanyACLRequestApprovedEventData) GetEventDetailsString(args *LogEve
func (ed *CompanyACLRequestDeniedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Access Denied for User: %s, ID: %s, Email: %s Company Group: %s.",
ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User with LF Username: %s added to the ACL for Company: %s by: %s.",
- args.LFUser.Name, args.CompanyName, args.UserName)
+ data := fmt.Sprintf("User with LF Username: %s added to the ACL for Company: %s",
+ args.LFUser.Name, args.CompanyName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLATemplateCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("PDF Templates created for Project: %s by: %s.", args.UserName, args.ProjectName)
+ data := fmt.Sprintf("PDF Templates created for Project: %s", args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -500,14 +573,20 @@ func (ed *GitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEvent
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf(" by: %s.", args.UserName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization: %s was deleted by: %s.",
- ed.GitHubOrganizationName, args.UserName)
+ data := fmt.Sprintf("GitHub Organization: %s was deleted ", ed.GitHubOrganizationName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -518,7 +597,10 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEve
if ed.AutoEnabledClaGroupID != "" {
data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
}
- data = data + fmt.Sprintf("by: %s.", args.UserName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -540,6 +622,10 @@ func (ed *CCLAApprovalListRequestRejectedEventData) GetEventDetailsString(args *
func (ed *CLAManagerRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s, LFID: %s, Email: %s added CLA Manager Request: %s for Company: %s, Project: %s.",
ed.UserName, ed.UserLFID, ed.UserEmail, ed.RequestID, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -547,111 +633,175 @@ func (ed *CLAManagerRequestCreatedEventData) GetEventDetailsString(args *LogEven
func (ed *CLAManagerCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s, LFID: %s, Email: %s was added as CLA Manager for Company: %s, Project: %s.",
ed.UserName, ed.UserLFID, ed.UserEmail, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAManagerDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s LFID: %s, Email: %s was removed as CLA Manager for Company: %s, Project: %s.",
+ data := fmt.Sprintf("User: %s LFID: %s, Email: %s was removed as CLA Manager for Company: %s, Project: %s ",
ed.UserName, ed.UserLFID, ed.UserEmail, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAManagerRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s was approved for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager Request: %s was approved for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAManagerRequestDeniedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s was denied for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager Request: %s was denied for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAManagerRequestDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager Request: %s was deleted for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager Request: %s was deleted for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Email: %s to the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Email: %s to the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Email: %s from the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Email: %s from the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Domain: %s to the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Domain: %s to the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Domain %s from the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Domain %s from the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s.",
+ data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s",
ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s created a CCLA Approval Request for Project: %s, Company: %s with Request ID: %s.",
- args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
+ data := fmt.Sprintf("The CCLA Approval Request was created for the Project: %s, Company: %s with Request ID: %s",
+ args.ProjectName, args.CompanyName, ed.RequestID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s added GitHub Organization: %s to the whitelist for Company %s, Project: %s.",
- args.UserName, ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub Organization: %s was added to the approval list for the Company %s, Project: %s",
+ ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s removed GitHub Organization: %s from the whitelist for Company: %s, Project: %s.",
- args.UserName, ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub Organization: %s was removed from the approval list for the Company: %s, Project: %s",
+ ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -671,8 +821,11 @@ func (ed *ClaManagerAccessRequestDeletedEventData) GetEventDetailsString(args *L
// GetEventDetailsString . . .
func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Group ID: %s, Name: %s was created by: %s.",
- args.ProjectID, args.ProjectName, args.UserName)
+ data := fmt.Sprintf("CLA Group ID: %s, Name: %s was created", args.ProjectID, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -680,7 +833,7 @@ func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (s
func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
var nameUpdated bool
- data := fmt.Sprintf("CLA Group ID: %s was updated by: %s", args.ProjectID, args.UserName)
+ data := fmt.Sprintf("CLA Group ID: %s was updated", args.ProjectID)
if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName {
data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
nameUpdated = true
@@ -694,59 +847,93 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
}
data = fmt.Sprintf("%s Description from : %s to : %s", data, ed.OldClaGroupDescription, ed.NewClaGroupDescription)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Group ID: %s was deleted by: %s.",
- args.ProjectID, args.UserName)
+ data := fmt.Sprintf("CLA Group ID: %s was deleted", args.ProjectID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d Gerrit Repositories were deleted due to CLA Group/Project: %s deletion.",
+ data := fmt.Sprintf("%d Gerrit Repositories were deleted due to CLA Group/Project: %s deletion",
ed.DeletedCount, args.ProjectName)
- return data, false
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *GerritAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Gerrit Repository: %s was added by: %s.", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("Gerrit Repository: %s was added", ed.GerritRepositoryName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Gerrit Repository: %s was deleted by: %s.", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("Gerrit Repository: %s was deleted", ed.GerritRepositoryName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The username %s was add to the gerrit group %s by the user %s.", ed.Username, ed.GroupName, args.UserName)
+ data := fmt.Sprintf("The username %s was add to the gerrit group %s", ed.Username, ed.GroupName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GerritUserRemovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The username %s was removed from the gerrit group %s by the user %s.", ed.Username, ed.GroupName, args.UserName)
+ data := fmt.Sprintf("The username %s was removed from the gerrit group %s", ed.Username, ed.GroupName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *GitHubProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d GitHub Repositories were deleted due to CLA Group/Project: [%s] deletion.",
+ data := fmt.Sprintf("%d GitHub Repositories were deleted due to CLA Group/Project: [%s] deletion",
ed.DeletedCount, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *SignatureProjectInvalidatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d Signatures were invalidated (approved set to false) due to CLA Group/Project: %s deletion.",
+ data := fmt.Sprintf("%d Signatures were invalidated (approved set to false) due to CLA Group/Project: %s deletion",
ed.InvalidatedCount, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -759,6 +946,10 @@ func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventDetailsString(
reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
}
data := fmt.Sprintf("Signature ID: %s invalidated by %s (approved set to false) due to %s ", utils.GetBestUsername(ed.CLAManager), ed.SignatureID, reason)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -780,24 +971,28 @@ func (ed *ContributorNotifyCLADesignee) GetEventDetailsString(args *LogEventArgs
// GetEventDetailsString . . .
func (ed *ContributorAssignCLADesignee) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User Name: %s, Email: %s was assigned as CLA Manager Designee for project Name: %s, ID: %s and Company Name: %s, ID: %s by: %s.",
+ data := fmt.Sprintf("User Name: %s, Email: %s was assigned as CLA Manager Designee for project Name: %s, ID: %s and Company Name: %s, ID: %s",
ed.DesigneeName, ed.DesigneeEmail,
args.ProjectName, args.ProjectSFID,
- args.CompanyName, args.CompanyID, args.UserName)
+ args.CompanyName, args.CompanyID)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
// GetEventDetailsString . . .
func (ed *UserConvertToContactData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s was converted to Contact state for Project: %s with ID: %s.",
- args.LFUser.Name, args.ProjectName, args.ProjectSFID)
+ args.UserName, args.ProjectName, args.ProjectSFID)
return data, true
}
// GetEventDetailsString . . .
func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s was assigned Scope: %s with Role: %s for Project: %s with ID: %s.",
- args.LFUser.Name,
+ args.UserName,
ed.Scope, ed.Role, args.ProjectName, args.ProjectSFID)
return data, true
}
@@ -818,19 +1013,27 @@ func (ed *ClaManagerRoleDeletedData) GetEventDetailsString(args *LogEventArgs) (
// GetEventDetailsString . . .
func (ed *CLAGroupEnrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("The user %s enabled the the project %s from the CLA Group %s.",
- args.UserName, args.ProjectName, args.CLAGroupName), false
+ data := fmt.Sprintf("The project %s was enrolled into the CLA Group %s", args.ProjectName, args.CLAGroupName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *CLAGroupUnenrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- return fmt.Sprintf("The user %s unenrolled the the project %s from the CLA Group %s.",
- args.UserName, args.ProjectName, args.CLAGroupName), false
+ data := fmt.Sprintf("The project %s was unenrolled from the CLA Group %s", args.ProjectName, args.CLAGroupName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
}
// GetEventDetailsString . . .
func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s enabled the CLA Service", args.UserName)
+ data := "CLA Service was enabled"
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -840,13 +1043,16 @@ func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
- return data, false
+ return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString function for ProjectServiceCLADisabledData
func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s disabled the CLA Service", args.UserName)
+ data := "CLA Service was disabled"
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -856,8 +1062,11 @@ func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArg
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
- return data, false
+ return data, true
}
// GetEventSummaryString . . .
@@ -1021,6 +1230,9 @@ func (ed *CompanyACLRequestApprovedEventData) GetEventSummaryString(args *LogEve
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
@@ -1038,6 +1250,9 @@ func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEvent
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
@@ -1220,6 +1435,9 @@ func (ed *CLAManagerRequestApprovedEventData) GetEventSummaryString(args *LogEve
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
@@ -1260,7 +1478,7 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEven
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the email %s to the approval list", ed.UserName, ed.ApprovalListEmail)
+ data := fmt.Sprintf("The CLA Manager %s added the email %s to the approval list", args.UserName, ed.ApprovalListEmail)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1276,7 +1494,7 @@ func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs)
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the email %s from the approval list", ed.UserName, ed.ApprovalListEmail)
+ data := fmt.Sprintf("The CLA Manager %s removed the email %s from the approval list", args.UserName, ed.ApprovalListEmail)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1292,7 +1510,7 @@ func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventAr
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the domain %s to the approval list", ed.UserName, ed.ApprovalListDomain)
+ data := fmt.Sprintf("The CLA Manager %s added the domain %s to the approval list", args.UserName, ed.ApprovalListDomain)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1308,7 +1526,7 @@ func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the domain %s from the approval list", ed.UserName, ed.ApprovalListDomain)
+ data := fmt.Sprintf("The CLA Manager %s removed the domain %s from the approval list", args.UserName, ed.ApprovalListDomain)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1324,7 +1542,7 @@ func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventA
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the GitHub username %s to the approval list", ed.UserName, ed.ApprovalListGitHubUsername)
+ data := fmt.Sprintf("The CLA Manager %s added the GitHub username %s to the approval list", args.UserName, ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1340,7 +1558,7 @@ func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogE
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the GitHub username %s from the approval list", ed.UserName, ed.ApprovalListGitHubUsername)
+ data := fmt.Sprintf("The CLA Manager %s removed the GitHub username %s from the approval list", args.UserName, ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1356,7 +1574,7 @@ func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *L
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", ed.UserName, ed.ApprovalListGitHubOrg)
+ data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", args.UserName, ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1372,7 +1590,7 @@ func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventA
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", ed.UserName, ed.ApprovalListGitHubOrg)
+ data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", args.UserName, ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1502,7 +1720,11 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString . . .
func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was deleted by the user %s.", args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The CLA Group %s was deleted", args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -1515,32 +1737,41 @@ func (ed *GerritProjectDeletedEventData) GetEventSummaryString(args *LogEventArg
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
- return data, false
+ return data, true
}
// GetEventSummaryString . . .
func (ed *GerritAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was added by the user %s", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("The Gerrit repository %s was added", ed.GerritRepositoryName)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The Gerrit repository %s was deleted by the user %s", ed.GerritRepositoryName, args.UserName)
+ data := fmt.Sprintf("The Gerrit repository %s was deleted", ed.GerritRepositoryName)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
@@ -1587,14 +1818,30 @@ func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArg
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("%d signatures were invalidated (approved set to false) due to CLA Group/Project %s deletion.",
+ data := fmt.Sprintf("%d signatures were invalidated (approved set to false) due to CLA Group/Project %s deletion",
ed.InvalidatedCount, args.ProjectName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -1606,7 +1853,20 @@ func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventSummaryString(
} else if ed.GHUsername != "" {
reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
}
- data := fmt.Sprintf("Signature ID: %s invalidated (approved set to false) due to %s ", ed.SignatureID, reason)
+ data := fmt.Sprintf("Signature ID: %s invalidated (approved set to false) due to %s", ed.SignatureID, reason)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -1664,7 +1924,7 @@ func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs
// GetEventSummaryString . . .
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was converted to a contact", args.LFUser.Name)
+ data := fmt.Sprintf("The user %s was converted to a contact", args.UserName)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1680,7 +1940,7 @@ func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString . . .
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s", args.LFUser.Name, ed.Role)
+ data := fmt.Sprintf("The user %s was added to the role %s", args.UserName, ed.Role)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
diff --git a/cla-backend-go/events/event_data_test.go b/cla-backend-go/events/event_data_test.go
index e594bb0a0..e81d3d27c 100644
--- a/cla-backend-go/events/event_data_test.go
+++ b/cla-backend-go/events/event_data_test.go
@@ -86,7 +86,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
{
name: "empty",
eventData: &CLAGroupUpdatedEventData{},
- detailStr: "CLA Group ID: projectIDValue was updated by: john.",
+ detailStr: "CLA Group ID: projectIDValue was updated by the user john.",
},
{
name: "only name updated",
@@ -94,7 +94,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupName: "updatedNameValue",
OldClaGroupName: "oldNameValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue.",
+ detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue by the user john.",
},
{
name: "only name updated but old description still passed",
@@ -104,7 +104,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "oldDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue.",
+ detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue by the user john.",
},
{
name: "only description updated",
@@ -112,7 +112,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "updatedDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated by: john with Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ detailStr: "CLA Group ID: projectIDValue was updated with Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
},
{
name: "only description updated but old name still passed",
@@ -122,7 +122,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupName: "oldNameValue",
OldClaGroupName: "oldNameValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated by: john with Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ detailStr: "CLA Group ID: projectIDValue was updated with Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
},
{
name: "name and description updated",
@@ -132,7 +132,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "updatedDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated by: john with Name from : oldNameValue to : updatedNameValue, Description from : oldDescriptionValue to : updatedDescriptionValue.",
+ detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue, Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
},
}
From 25a05f15f4a55c4ae6a1edaef734601a73a0d975 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 19 Apr 2021 17:59:38 +0300
Subject: [PATCH 0232/1276] [#2872] removing extra cla_group.updated event
which is confusing for UI (#2881)
event since it's confusing on the UI
Signed-off-by: makkalot
---
cla-backend-go/v2/cla_groups/handlers.go | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 801e5ab2d..9541a4a66 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -385,16 +385,6 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseInternalServerErrorWithError(reqID, fmt.Sprintf("unable to unenroll projects for CLA Group ID: %s", params.ClaGroupID), err))
}
- eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupUpdated,
- ClaGroupModel: cg,
- LfUsername: authUser.UserName,
- EventData: &events.CLAGroupUpdatedEventData{
- OldClaGroupName: cg.ProjectName,
- OldClaGroupDescription: cg.ProjectDescription,
- },
- })
-
return cla_group.NewUnenrollProjectsOK().WithXRequestID(reqID)
})
From 30b9a7c3a287595e3c2c785295bb070d46b47ea9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 19 Apr 2021 15:32:28 -0700
Subject: [PATCH 0233/1276] Added Logging (#2885)
- Added request_employee_signature_gerrit and request_employee_signature
logging to help track down issue with employee ack for onap
Signed-off-by: David Deal
---
cla-backend/cla/controllers/signing.py | 10 +++
cla-backend/cla/models/docusign_models.py | 79 +++++++++++++----------
cla-backend/cla/models/event_types.py | 5 +-
3 files changed, 57 insertions(+), 37 deletions(-)
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index 4cd846b3d..1b761a184 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -104,13 +104,23 @@ def request_employee_signature(project_id, company_id, user_id, return_url_type,
:type return_url_type: string
:param return_url: The URL to return the user to after signing is complete.
"""
+ fn = 'cla.controllers.signing.request_employee_signature'
signing_service = get_signing_service()
if return_url_type is not None and return_url_type.lower() == "gerrit":
+ cla.log.error(f'{fn} - return type is gerrit - invoking: request_employee_signature_gerrit')
return signing_service.request_employee_signature_gerrit(str(project_id), str(company_id), str(user_id),
return_url)
elif return_url_type is not None and return_url_type.lower() == "github":
+ cla.log.error(f'{fn} - return type is github - invoking: request_employee_signature')
return signing_service.request_employee_signature(str(project_id), str(company_id), str(user_id), return_url)
+ else:
+ msg = (f'{fn} - unsupported return type {return_url_type} for '
+ f'cla group: {project_id}, '
+ f'company: {company_id}, '
+ f'user: {user_id}')
+ cla.log.error(msg)
+ raise falcon.HTTPBadRequest(title=msg)
def check_and_prepare_employee_signature(project_id, company_id, user_id):
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 836187325..3404dbe28 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -26,7 +26,7 @@
from cla.models import signing_service_interface, DoesNotExist
from cla.models.dynamo_models import Signature, User, \
Project, Company, Gerrit, \
- Document, Event, ProjectCLAGroupModel
+ Document, Event
from cla.models.event_types import EventType
from cla.models.s3_storage import S3Storage
from cla.user_service import UserService
@@ -631,13 +631,14 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
def request_employee_signature(self, project_id, company_id, user_id, return_url=None):
- request_info = f'project: {project_id}, company: {company_id}, user: {user_id} with return_url: {return_url}'
- cla.log.info(f'Processing request_employee_signature request with {request_info}')
+ fn = 'docusign_models.check_and_prepare_employee_signature'
+ request_info = f'cla group: {project_id}, company: {company_id}, user: {user_id} with return_url: {return_url}'
+ cla.log.info(f'{fn} - processing request_employee_signature request with {request_info}')
check_and_prepare_signature = self.check_and_prepare_employee_signature(project_id, company_id, user_id)
# Check if there are any errors while preparing the signature.
if 'errors' in check_and_prepare_signature:
- cla.log.warning(f'Error in check_and_prepare_signature with: {request_info} - '
+ cla.log.warning(f'{fn} - error in check_and_prepare_signature with: {request_info} - '
f'signatures: {check_and_prepare_signature}')
return check_and_prepare_signature
@@ -645,28 +646,30 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
company_id=company_id, project_id=project_id, user_id=user_id)
# Return existing signature if employee has signed it
if employee_signature is not None:
- cla.log.info(f'Employee has signed for company: {company_id}, '
- f'request_info: {request_info} - signature: {employee_signature}')
+ cla.log.info(f'{fn} - employee has previously acknowledged their company affiliation '
+ f'for request_info: {request_info} - signature: {employee_signature}')
return employee_signature.to_dict()
- cla.log.info(f'Employee has NOT signed it for: {request_info}')
+ cla.log.info(f'{fn} - employee has NOT previously acknowledged their company affiliation for : {request_info}')
# Requires us to know where the user came from.
signature_metadata = cla.utils.get_active_signature_metadata(user_id)
if return_url is None:
- cla.log.info(f'No return URL for: {request_info}')
+ cla.log.debug(f'{fn} - no return URL for: {request_info}')
return_url = cla.utils.get_active_signature_return_url(user_id, signature_metadata)
- cla.log.info(f'Set return URL for: {request_info} to: {return_url}')
+ cla.log.debug(f'{fn} - set return URL for: {request_info} to: {return_url}')
# project has already been checked from check_and_prepare_employee_signature. Load project with project ID.
project = Project()
+ cla.log.info(f'{fn} - loading cla group details for: {request_info}')
project.load(project_id)
- cla.log.info(f'Loaded project details for: {request_info}')
+ cla.log.info(f'{fn} - loaded cla group details for: {request_info}')
# company has already been checked from check_and_prepare_employee_signature. Load company with company ID.
company = Company()
+ cla.log.info(f'{fn} - loading company details for: {request_info}')
company.load(company_id)
- cla.log.info(f'Loaded company details for: {request_info}')
+ cla.log.info(f'{fn} - loaded company details for: {request_info}')
# user has already been checked from check_and_prepare_employee_signature. Load user with user ID.
user = User()
@@ -674,9 +677,10 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
# Get project's latest corporate document to get major/minor version numbers.
last_document = project.get_latest_corporate_document()
- cla.log.info(f'Loaded last project document details for: {request_info}')
+ cla.log.info(f'{fn} - loaded the current cla document document details for: {request_info}')
# return_url may still be empty at this point - the console will deal with it
+ cla.log.info(f'{fn} - creating a new signature document for: {request_info}')
new_signature = Signature(signature_id=str(uuid.uuid4()),
signature_project_id=project_id,
signature_document_minor_version=last_document.get_document_minor_version(),
@@ -689,20 +693,22 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
signature_approved=True,
signature_return_url=return_url,
signature_user_ccla_company_id=company_id)
- cla.log.info(f'Created new signature document for: {request_info} - signature: {new_signature}')
+ cla.log.info(f'{fn} - created new signature document for: {request_info} - signature: {new_signature}')
# Set signature ACL
- new_signature.set_signature_acl(f'github:{user.get_user_github_id()}')
+ acl_value = f'github:{user.get_user_github_id()}'
+ cla.log.info(f'{fn} - assigning signature acl with value: {acl_value} for: {request_info}')
+ new_signature.set_signature_acl(acl_value)
# Save signature
new_signature.save()
- cla.log.info(f'Set and saved signature for: {request_info}')
- event_data = (f'The user {user.get_user_name()} acknowledged the CLA affiliation for '
+ cla.log.info(f'{fn} - saved signature for: {request_info}')
+ event_data = (f'The user {user.get_user_name()} acknowledged the CLA employee affiliation for '
f'company {company.get_company_name()} with ID {company.get_company_id()}, '
- f'project {project.get_project_name()} with ID {project.get_project_id()}.')
- event_summary = (f'The user {user.get_user_name()} acknowledged the CLA affiliation for '
+ f'cla group {project.get_project_name()} with ID {project.get_project_id()}.')
+ event_summary = (f'The user {user.get_user_name()} acknowledged the CLA employee affiliation for '
f'company {company.get_company_name()} and '
- f'project {project.get_project_name()}.')
+ f'cla group {project.get_project_name()}.')
Event.create_event(
event_type=EventType.EmployeeSignatureCreated,
event_company_id=company_id,
@@ -717,7 +723,7 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
# If the project does not require an ICLA to be signed, update the pull request and remove the active
# signature metadata.
if not project.get_project_ccla_requires_icla_signature():
- cla.log.info('Project does not require ICLA signature from the employee - updating PR')
+ cla.log.info(f'{fn} - cla group does not require a separate ICLA signature from the employee - updating PR')
github_repository_id = signature_metadata['repository_id']
change_request_id = signature_metadata['pull_request_id']
@@ -727,23 +733,23 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
return {'errors': {'github_repository_id': 'The given github repository ID does not exist. '}}
update_repository_provider(installation_id, github_repository_id, change_request_id)
-
cla.utils.delete_active_signature_metadata(user_id)
else:
- cla.log.info('Project requires ICLA signature from employee - PR has been left unchanged')
+ cla.log.info(f'{fn} - cla group requires ICLA signature from employee - PR has been left unchanged')
- cla.log.info(f'Returning new signature for: {request_info} - signature: {new_signature}')
+ cla.log.info(f'{fn} - returning new signature for: {request_info} - signature: {new_signature}')
return new_signature.to_dict()
def request_employee_signature_gerrit(self, project_id, company_id, user_id, return_url=None):
- request_info = f'project: {project_id}, company: {company_id}, user: {user_id} with return_url: {return_url}'
- cla.log.info(f'Processing request_employee_signature_gerrit request with {request_info}')
+ fn = 'docusign_models.request_employee_signature_gerrit'
+ request_info = f'cla group: {project_id}, company: {company_id}, user: {user_id} with return_url: {return_url}'
+ cla.log.info(f'{fn} - processing request_employee_signature_gerrit request with {request_info}')
check_and_prepare_signature = self.check_and_prepare_employee_signature(project_id, company_id, user_id)
# Check if there are any errors while preparing the signature.
if 'errors' in check_and_prepare_signature:
- cla.log.warning(f'Error in request_employee_signature_gerrit with: {request_info} - '
+ cla.log.warning(f'{fn} - error in request_employee_signature_gerrit with: {request_info} - '
f'signatures: {check_and_prepare_signature}')
return check_and_prepare_signature
@@ -752,27 +758,31 @@ def request_employee_signature_gerrit(self, project_id, company_id, user_id, ret
company_id=company_id, project_id=project_id, user_id=user_id)
# Return existing signature if employee has signed it
if employee_signature is not None:
- cla.log.info(f'Employee has signed for company: {company_id}, '
+ cla.log.info(f'{fn} - employee has signed for company: {company_id}, '
f'request_info: {request_info} - signature: {employee_signature}')
return employee_signature.to_dict()
- cla.log.info(f'Employee has NOT signed it for: {request_info}')
+ cla.log.info(f'{fn} - employee has NOT previously acknowledged their company affiliation for : {request_info}')
# Retrieve Gerrits by Project reference ID
try:
+ cla.log.info(f'{fn} - loading gerrits for: {request_info}')
gerrits = Gerrit().get_gerrit_by_project_id(project_id)
except DoesNotExist as err:
- cla.log.error(f'Cannot load Gerrit instance for: {request_info}')
+ cla.log.error(f'{fn} - cannot load Gerrit instance for: {request_info}')
return {'errors': {'missing_gerrit': str(err)}}
# project has already been checked from check_and_prepare_employee_signature. Load project with project ID.
project = Project()
+ cla.log.info(f'{fn} - loading cla group for: {request_info}')
project.load(project_id)
- cla.log.info(f'Loaded project for: {request_info}')
+ cla.log.info(f'{fn} - loaded cla group for: {request_info}')
+
# company has already been checked from check_and_prepare_employee_signature. Load company with company ID.
company = Company()
+ cla.log.info(f'{fn} - loading company details for: {request_info}')
company.load(company_id)
- cla.log.info(f'Loaded company details for: {request_info}')
+ cla.log.info(f'{fn} - loaded company details for: {request_info}')
# user has already been checked from check_and_prepare_employee_signature. Load user with user ID.
user = User()
@@ -798,7 +808,7 @@ def request_employee_signature_gerrit(self, project_id, company_id, user_id, ret
# Save signature before adding user to the LDAP Group.
new_signature.save()
- cla.log.info(f'Set and saved signature for: {request_info}')
+ cla.log.info(f'{fn} - saved signature for: {request_info}')
event_data = (f'The user {user.get_user_name()} acknowledged the CLA company affiliation for '
f'company {company.get_company_name()} with ID {company.get_company_id()}, '
f'project {project.get_project_name()} with ID {project.get_project_id()}.')
@@ -822,9 +832,10 @@ def request_employee_signature_gerrit(self, project_id, company_id, user_id, ret
group_id = gerrit.get_group_id_ccla()
# Add the user to the LDAP Group
try:
+ cla.log.debug(f'{fn} - adding user to group: {group_id}')
lf_group.add_user_to_group(group_id, user.get_lf_username())
except Exception as e:
- cla.log.error('Failed in adding user to the LDAP group.{} - {}'.format(e, request_info))
+ cla.log.error(f'{fn} - failed in adding user to the LDAP group.{e} - {request_info}')
return
return new_signature.to_dict()
@@ -1295,7 +1306,7 @@ def populate_sign_url(self, signature, callback_url=None,
cla_manager_email=cla_manager_email,
company_name=company_name,
project_version=project.get_version(),
- project_names=project_names))
+ project_names=project_names))
cla.log.debug(f'populate_sign_url - {sig_type} - generating a docusign signer object form email with'
f'name: {signatory_name}, email: {signatory_email}, subject: {email_subject}')
signer = pydocusign.Signer(email=signatory_email,
diff --git a/cla-backend/cla/models/event_types.py b/cla-backend/cla/models/event_types.py
index cd6d03fe6..4f6ca5d66 100644
--- a/cla-backend/cla/models/event_types.py
+++ b/cla-backend/cla/models/event_types.py
@@ -6,9 +6,8 @@
class EventType(Enum):
"""
- Enumeraters representing type of CLA events
+ Enumerator representing type of CLA events
across projects, users, signatures, whitelists
-
"""
CreateUser = "Create User"
UpdateUser = "Update User"
@@ -24,7 +23,7 @@ class EventType(Enum):
CreateProjectDocumentTemplate = "Create Project Document with Template"
DeleteProjectDocument = "Delete Project Document"
AddPermission = "Add Permission"
- RemovePermission = "Remove Pemrission"
+ RemovePermission = "Remove Permission"
AddProjectManager = "Add Project Manager"
RemoveProjectManager = "Remove Project Manager"
RequestCompanyWL = "Request Company Whitelist"
From 2cab1d8a612771c494d0cc837deb579d15fa796d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 20 Apr 2021 13:20:05 -0700
Subject: [PATCH 0234/1276] Added String to DateTime Python Helper Function
(#2888)
- Added string to datetime python helper function to assist with event
log processing - added get_events_type_by_week in dynamo events class
- Added additional logging to docusign routines
Signed-off-by: David Deal
---
cla-backend/cla/models/docusign_models.py | 2 ++
cla-backend/cla/models/dynamo_models.py | 27 +++++++++++++++++++++++
cla-backend/cla/utils.py | 16 ++++++++++++++
3 files changed, 45 insertions(+)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 3404dbe28..83cc5aed9 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -1476,6 +1476,7 @@ def signed_individual_callback(self, content, installation_id, github_repository
# Log the event
try:
# Load the Project by ID and send audit event
+ cla.log.debug(f'{fn} - creating an event log entry for event_type: {EventType.IndividualSignatureSigned}')
project = Project()
project.load(signature.get_signature_project_id())
event_data = (f'The user {user.get_user_name()} signed an individual CLA for '
@@ -1492,6 +1493,7 @@ def signed_individual_callback(self, content, installation_id, github_repository
event_summary=event_summary,
contains_pii=False,
)
+ cla.log.debug(f'{fn} - created an event log entry for event_type: {EventType.IndividualSignatureSigned}')
except DoesNotExist as err:
msg = (f'{fn} - unable to load project by CLA Group ID: {signature.get_signature_project_id()}, '
f'unable to send audit event, error: {err}')
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 4ef900489..36949775c 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -4420,6 +4420,33 @@ def search_missing_event_data_lower(self, limit: Optional[int] = None, last_eval
# ret.append(evt)
# return ret, result_iterator.last_evaluated_key, result_iterator.total_count
+ def get_events_type_by_week(self, event_type: EventType) -> dict:
+ filter_attributes = {
+ "event_type": event_type.name,
+ }
+ filter_condition = create_filter(filter_attributes, EventModel)
+ projection = ["event_id", "event_type", "date_created"]
+ cla.log.debug(f'querying events using filter: {filter_condition}...')
+ result_iterator = self.model.scan(filter_condition=filter_condition, attributes_to_get=projection)
+
+ ret = {}
+
+ for event_record in result_iterator:
+ date_time_value = cla.utils.get_time_from_string(str(event_record.date_created))
+ year = date_time_value.year
+ week_number = date_time_value.isocalendar()[1]
+ cla.log.debug(f'processing events - '
+ f'{event_record.event_id} - '
+ f'{event_record.event_type} - '
+ f'{event_record.date_created} - '
+ f'{year} - {week_number:02d}')
+ key = f'{year} {week_number:02d}'
+ if key in ret:
+ ret[key] += 1
+ else:
+ ret[key] = 1
+ return ret
+
def set_event_data(self, event_data: str):
self.model.event_data = event_data
self.model.event_data_lower = event_data.lower()
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 1f9a9a0d4..9c7431d34 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1602,6 +1602,22 @@ def get_formatted_time(the_time: datetime) -> str:
return the_time.strftime("%Y-%m-%dT%H:%M:%S.%f%z") + "+0000"
+def get_time_from_string(date_string: str) -> Optional[datetime]:
+ """
+ Helper function to return the specified datetime object from an ISO standard format string
+ :return:
+ """
+ # Try these formats
+ formats = ['%Y-%m-%d %H:%M:%S.%f%z', '%Y-%m-%dT%H:%M:%S%z', '%Y-%m-%dT%H:%M:%S.%f%z']
+ for fmt in formats:
+ try:
+ return datetime.strptime(date_string, fmt)
+ except (ValueError, TypeError) as e:
+ pass
+ # print(f'unable to parse time {date_string} using {fmt}, error: {e}')
+ return None
+
+
def get_public_email(user):
"""
Helper function to return public user email to send emails
From 54d6b5e52c824c4f94f66909ce6fea9e76147ddd Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Tue, 20 Apr 2021 23:55:25 +0300
Subject: [PATCH 0235/1276] [#2842] Feature/Email for Approval Update (#2887)
- Added email for CCLA+ICLA, CCLA+ICLA+ECLA, CCLA cases
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/email.go | 74 ++++++++++-
cla-backend-go/signatures/models.go | 14 ++
cla-backend-go/signatures/repository.go | 166 ++++++++++++++++++------
cla-backend-go/tests/signatures_test.go | 23 ++--
4 files changed, 219 insertions(+), 58 deletions(-)
diff --git a/cla-backend-go/signatures/email.go b/cla-backend-go/signatures/email.go
index 84a69759e..9a5b62eeb 100644
--- a/cla-backend-go/signatures/email.go
+++ b/cla-backend-go/signatures/email.go
@@ -3,6 +3,12 @@
package signatures
+// ClaManagerInfoParams represents the CLAManagerInfo used inside of the Email Templates
+type ClaManagerInfoParams struct {
+ Username string
+ Email string
+}
+
//InvalidateSignatureTemplateParams representing params when invalidating icla/ecla
type InvalidateSignatureTemplateParams struct {
RecipientName string
@@ -10,16 +16,70 @@ type InvalidateSignatureTemplateParams struct {
ClaManager string
RemovalCriteria string
ProjectName string
+ CLAManagers []ClaManagerInfoParams
+ CLaManager string
+ CLAGroupName string
+ Company string
}
const (
- //InvalidateSignatureTemplateName is email template for InvalidateSignatureTemplate
- InvalidateSignatureTemplateName = "InvalidateSignatureTemplate"
- //InvalidateSignatureTemplate ...
- InvalidateSignatureTemplate = `
+ //InvalidateCCLAICLASignatureTemplateName is email template for InvalidateSignatureTemplate
+ InvalidateCCLAICLASignatureTemplateName = "InvalidateSignatureTemplate"
+ //InvalidateCCLAICLASignatureTemplate ...
+ InvalidateCCLAICLASignatureTemplate = `
+
Hello {{.RecipientName}}
+
This is a notification email from EasyCLA regarding the CLA Group {{.ProjectName}}
+
You were previously authorized to contribute on behalf of your company {{COMPANY-NAME}} under its CLA. However, a CLA Manager has now removed you from the authorization list. This has additionally resulted in invalidating your current signed Individual CLA (ICLA).
+
As a result, you will no longer be able to contribute until you are again authorized under another signed CLA.
+
Please contact one of the CLA Managers from your company if you have questions about why you were removed. The CLA Managers from your company for this CLA are:
+
+ {{range .CLAManagers}}
+
{{.Username}} {{.Email}}
+ {{end}}
+
+ `
+
+ //InvalidateCCLASignatureTemplateName is email template upon approval list removal for ccla use case
+ InvalidateCCLASignatureTemplateName = "InvalidateCCLAICLASignatureTemplate"
+ //InvalidateCCLASignatureTemplate ...
+ InvalidateCCLASignatureTemplate = `
+
Hello {{.RecipientName}}
+
This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.
+
You were previously authorized to contribute on behalf of your company {{.Company}} under its CLA. However, a CLA Manager {{.ClaManager}} has now removed you from the authorization list.
+
As a result, you will no longer be able to contribute until you are again authorized under another signed CLA.
+
Please contact one of the CLA Managers from your company if you have questions about why you were removed. The CLA Managers from your company for this CLA are:
+
+ {{range .CLAManagers}}
+
{{.Username}} {{.Email}}
+ {{end}}
+
+ `
+
+ //InvalidateICLASignatureTemplateName is email template upon approval list removal for ccla use case
+ InvalidateICLASignatureTemplateName = "InvalidateICLASignatureTemplate"
+ //InvalidateICLASignatureTemplate ...
+ InvalidateICLASignatureTemplate = `
+
Hello {{.RecipientName}}
+
This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.
+
You had previously signed an Individual CLA (ICLA) to contribute to the project on your own behalf. However, the Project Manager has marked your ICLA as invalidated. This might be because the ICLA may have been signed in error, if your contributions should have been on behalf of your employer rather than on your own behalf.
+
As a result, you will no longer be able to contribute until you are again authorized under another signed CLA.
+
Please contact the Project Manager for this project if you have questions about why you were removed.
+
+ `
+
+ //InvalidateCCLAICLAECLASignatureTemplateName is email template upon approval list removal for ccla use case
+ InvalidateCCLAICLAECLASignatureTemplateName = "InvalidateCCLAICLAECLASignatureTemplate"
+ //InvalidateCCLAICLAECLASignatureTemplate ...
+ InvalidateCCLAICLAECLASignatureTemplate = `
Hello {{.RecipientName}}
-
This is a notification email from EasyCLA regarding the claGroup {{.ProjectName}}
-
The ICLA signature for {{.RecipientName}} has been invalidated.
-
Please contact Project Manager for the claGroup {{.ProjectName}} and/or CLA Manager from your company if you have more questions.
+
This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.
+
You were previously authorized to contribute on behalf of your company {{.Company}} under its CLA. However, a CLA Manager has now removed you from the authorization list. This has additionally resulted in invalidating your current signed Individual CLA (ICLA) and your acknowledgement.
+
As a result, you will no longer be able to contribute until you are again authorized under another signed CLA.
+
Please contact one of the CLA Managers from your company if you have questions about why you were removed. The CLA Managers from your company for this CLA are:
+
+ {{range .CLAManagers}}
+
{{.Username}} {{.Email}}
+ {{end}}
+
`
)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index ae46a2304..e3d8c6468 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -39,6 +39,9 @@ type ApprovalList struct {
GerritICLAECLAs []string
ICLAs []*models.IclaSignature
ECLAs []*models.Signature
+ CLAManager *models.User
+ ManagersInfo []ClaManagerInfoParams
+ CCLASignature *models.Signature
}
// GerritUserResponse is a data structure to hold the gerrit user query response
@@ -53,3 +56,14 @@ type ICLAUserResponse struct {
ICLASignature *models.IclaSignature
Error error
}
+
+const (
+ //CCLAICLA representing user removal under CCLA + ICLA
+ CCLAICLA = "CCLAICLA"
+ //CCLAICLAECLA representing user removal under CCLA + ICLA +ECLA
+ CCLAICLAECLA = "CCLAICLAECLA"
+ //CCLA representing normal use case of user under CCLA
+ CCLA = "ICLA"
+ //ICLA representing individual use case
+ ICLA = "ICLA"
+)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 9e9d563bf..ba294813b 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2000,12 +2000,26 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return nil, errors.New(msg)
}
+ // Get CLA Manager
+ var cclaManagers []ClaManagerInfoParams
+ for i := range cclaSignature.SignatureACL {
+ cclaManagers = append(cclaManagers, ClaManagerInfoParams{
+ Username: utils.GetBestUsername(&cclaSignature.SignatureACL[i]),
+ Email: getBestEmail(&cclaSignature.SignatureACL[i]),
+ })
+ }
+
// Keep track of existing company approvals
approvalList := ApprovalList{
DomainApprovals: cclaSignature.DomainApprovalList,
GHOrgApprovals: cclaSignature.GithubOrgApprovalList,
GitHubUsernameApprovals: cclaSignature.GithubUsernameApprovalList,
EmailApprovals: cclaSignature.EmailApprovalList,
+ CLAManager: claManager,
+ ICLAs: make([]*models.IclaSignature, 0),
+ ECLAs: make([]*models.Signature, 0),
+ ManagersInfo: cclaManagers,
+ CCLASignature: cclaSignature,
}
// Just grab and use the first one - need to figure out conflict resolution if more than one
@@ -2085,6 +2099,8 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
for _, email := range params.RemoveEmailApprovalList {
go func(email string) {
defer wg.Done()
+ var iclas []*models.IclaSignature
+ var eclas []*models.Signature
log.WithFields(f).Debugf("getting cla user record for email: %s ", email)
userSearch, userErr := repo.usersRepo.SearchUsers("user_emails", email, false)
if userErr != nil || userSearch == nil {
@@ -2107,6 +2123,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if len(signs.Signatures) > 0 {
approvalList.ECLAs = signs.Signatures
+ eclas = signs.Signatures
}
if len(userSearch.Users) > 0 {
@@ -2136,6 +2153,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if result.Error == nil {
log.WithFields(f).Debug("processing icla...")
approvalList.ICLAs = append(approvalList.ICLAs, result.ICLASignature)
+ iclas = append(iclas, result.ICLASignature)
}
}
@@ -2144,6 +2162,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Invalidate signatures
repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+ // Send email
+ repo.sendEmail(ctx, email, &approvalList, iclas, eclas)
+
//update gerrit permissions
gerritUser, getGerritUserErr := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
if getGerritUserErr != nil || gerritUser == nil {
@@ -2262,6 +2283,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
for _, ghUsername := range params.RemoveGithubUsernameApprovalList {
go func(ghUsername string) {
defer wg.Done()
+ var iclas []*models.IclaSignature
+ var eclas []*models.Signature
+
criteria := &ApprovalCriteria{
GitHubUsername: ghUsername,
}
@@ -2273,6 +2297,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if signs.Signatures != nil {
approvalList.ECLAs = signs.Signatures
+ eclas = signs.Signatures
}
// Get ICLAs
claUser, claErr := repo.usersRepo.GetUserByGitHubUsername(ghUsername)
@@ -2286,7 +2311,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
log.WithFields(f).Debugf("unable to get icla signature for user with ghUsername: %s ", ghUsername)
}
if icla != nil {
- // Convert to IclSignature instance to leverage invalidateSignatrues helper function
+ // Convert to IclSignature instance to leverage invalidateSignatures helper function
approvalList.ICLAs = []*models.IclaSignature{{
GithubUsername: icla.UserGHUsername,
LfUsername: icla.UserLFID,
@@ -2297,6 +2322,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+ // Send Email
+ repo.sendEmail(ctx, getBestEmail(claUser), &approvalList, iclas, eclas)
+
}(ghUsername)
}
wg.Wait()
@@ -2416,6 +2444,95 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return updatedSig, nil
}
+// sendEmail is a helper function used to render email for (CCLA, ICLA, ECLA cases)
+func (repo repository) sendEmail(ctx context.Context, email string, approvalList *ApprovalList, iclas []*models.IclaSignature, eclas []*models.Signature) {
+ f := logrus.Fields{
+ "functionName": "v1.signatures.repository.sendEmail",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+ companyName := ""
+ company, companyErr := repo.companyRepo.GetCompany(ctx, approvalList.CompanyID)
+ if companyErr != nil {
+ log.WithFields(f).Debugf("unable to get company")
+ }
+ if company != nil {
+ companyName = company.CompanyName
+ }
+
+ params := InvalidateSignatureTemplateParams{
+ Company: companyName,
+ RecipientName: email,
+ ClaManager: utils.GetBestUsername(approvalList.CLAManager),
+ CLAManagers: approvalList.ManagersInfo,
+ CLAGroupName: approvalList.ClaGroupName,
+ }
+
+ // check for signature type (CCLA, ICLA, ECLA)
+ var removalType string = ""
+
+ // case 1 CCLA
+ if len(iclas) == 0 && len(eclas) == 0 {
+ removalType = CCLA
+ } else if len(iclas) > 0 && len(eclas) == 0 {
+ // case 2 ccla + icla
+ removalType = CCLAICLA
+ } else if len(iclas) > 0 && len(eclas) > 0 {
+ // case 3 ccla + icla + ecla
+ removalType = CCLAICLAECLA
+ }
+
+ // Send CCLA Email
+ if removalType == CCLA {
+ subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
+ log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLASignatureTemplateName, InvalidateCCLASignatureTemplate, params)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
+ } else {
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send approval list update email to : %s ", email)
+ }
+ }
+ } else if removalType == ICLA {
+ subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
+ log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateICLASignatureTemplateName, InvalidateICLASignatureTemplate, params)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
+ } else {
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send approval list update email to : %s ", email)
+ }
+ }
+ } else if removalType == CCLAICLA {
+ subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
+ log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLAICLASignatureTemplateName, InvalidateCCLASignatureTemplate, params)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
+ } else {
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send approval list update email to : %s ", email)
+ }
+ }
+ } else if removalType == CCLAICLAECLA {
+ subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
+ log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLAICLAECLASignatureTemplateName, InvalidateCCLAICLAECLASignatureTemplate, params)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
+ } else {
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send approval list update email to : %s ", email)
+ }
+ }
+ }
+}
+
// invalidateSignatures is a helper function that invalidates signature records based on approval list
func (repo repository) invalidateSignatures(ctx context.Context, approvalList *ApprovalList, claManager *models.User, eventArgs *events.LogEventArgs) {
f := logrus.Fields{
@@ -2442,32 +2559,14 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
log.WithFields(f).Warnf("no signatureReferenceID for signature: %+v ", signature)
return
}
+
user, verifyErr := repo.verifyUserApprovals(ctx, signature.SignatureReferenceID, signature.SignatureID, claManager, approvalList)
if verifyErr != nil {
log.WithFields(f).Warnf("unable to verify user: %s ", signature.SignatureReferenceID)
return
}
+ // Map representing CLA types against email ....
email := getBestEmail(user)
- // send email
- subject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s on %s", email, approvalList.ClaGroupName)
- log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
- InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
- RecipientName: utils.GetBestUsername(user),
- ClaType: utils.ClaTypeICLA,
- ClaManager: utils.GetBestUsername(claManager),
- RemovalCriteria: approvalList.Criteria,
- })
- if renderErr != nil {
- log.WithFields(f).Debugf("unable to render invalidation notice for user : %s ", email)
- return
- }
- err := utils.SendEmail(subject, body, []string{email})
- if err != nil {
- log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
- return
- }
-
// Log Event
eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
SignatureID: icla.SignatureID,
@@ -2500,25 +2599,6 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
return
}
email := getBestEmail(user)
- // send email
- subject := fmt.Sprintf("EasyCLA: Employee Acknowledgement invalidated for %s on %s", email, approvalList.ClaGroupName)
- log.WithFields(f).Debugf("sending invalidation email to user: %s ", email)
- body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateSignatureTemplateName,
- InvalidateSignatureTemplate, InvalidateSignatureTemplateParams{
- RecipientName: utils.GetBestUsername(user),
- ClaType: utils.ClaTypeECLA,
- ClaManager: utils.GetBestUsername(claManager),
- RemovalCriteria: approvalList.Criteria,
- })
- if renderErr != nil {
- log.WithFields(f).Debugf("unable to send invalidation signature email to : %s ", email)
- return
- }
- err := utils.SendEmail(subject, body, []string{email})
- if err != nil {
- log.WithFields(f).Debugf("unable to send email invalidation notice to user: %s", email)
- return
- }
// Log Event
eventArgs.EventData = &events.SignatureInvalidatedApprovalRejectionEventData{
SignatureID: ecla.SignatureID,
@@ -2570,6 +2650,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
log.WithFields(f).Warnf("unable to get user record for ID: %s ", userID)
return nil, err
}
+ email := getBestEmail(user)
authUser := auth.User{
Email: claManager.LfEmail,
@@ -2578,10 +2659,9 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
if approvalList.Criteria == utils.EmailDomainCriteria {
// Handle Domains
- email := getBestEmail(user)
domain := strings.Split(email, "@")[1]
if utils.StringInSlice(domain, approvalList.DomainApprovals) {
- if !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) && !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) {
+ if !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) && !utils.StringInSlice(email, approvalList.EmailApprovals) {
//Invalidate record
note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.EmailDomainCriteria)
err := repo.InvalidateProjectRecord(ctx, signatureID, note)
@@ -2610,6 +2690,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
if utils.StringInSlice(user.GithubUsername, approvalList.GHUsernames) {
if !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) && !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) {
//Invalidate record
+
note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.GitHubOrgCriteria)
err := repo.InvalidateProjectRecord(ctx, signatureID, note)
if err != nil {
@@ -2625,6 +2706,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
log.WithFields(f).Warnf("unable to invalidate record for signatureID: %s ", signatureID)
return user, err
}
+
}
return user, nil
diff --git a/cla-backend-go/tests/signatures_test.go b/cla-backend-go/tests/signatures_test.go
index b2eed5317..fac60faa7 100644
--- a/cla-backend-go/tests/signatures_test.go
+++ b/cla-backend-go/tests/signatures_test.go
@@ -11,19 +11,24 @@ import (
"github.com/stretchr/testify/assert"
)
-//TestInvalidateSignatureTemplate validates email sent when signature is invalidated
-func TestInvalidateSignatureTemplate(t *testing.T) {
+func TestCCLAInvalidateSignatureTemplate(t *testing.T) {
params := signatures.InvalidateSignatureTemplateParams{
- RecipientName: "TestUser",
- ClaType: utils.ClaTypeICLA,
+ RecipientName: "CCLATest",
+ ClaType: utils.ClaTypeCCLA,
ClaManager: "claManager",
+ CLAGroupName: "claGroup test",
RemovalCriteria: "email removal",
- ProjectName: "testProject",
+ CLAManagers: []signatures.ClaManagerInfoParams{
+ {Username: "mgr_one", Email: "mgr_one_email"},
+ {Username: "mgr_two", Email: "mgr_two_email"},
+ },
+ Company: "TestCompany",
}
- result, err := utils.RenderTemplate(utils.V1, signatures.InvalidateSignatureTemplateName, signatures.InvalidateSignatureTemplate, params)
+ result, err := utils.RenderTemplate(utils.V2, signatures.InvalidateCCLASignatureTemplateName, signatures.InvalidateCCLASignatureTemplate, params)
assert.NoError(t, err)
- assert.Contains(t, result, "Hello TestUser")
- assert.Contains(t, result, "The ICLA signature for TestUser has been invalidated.")
- assert.Contains(t, result, "Please contact Project Manager for the claGroup testProject and/or CLA Manager from your company if you have more questions.")
+ assert.Contains(t, result, "This is a notification email from EasyCLA regarding the CLA Group claGroup test")
+ assert.Contains(t, result, "You were previously authorized to contribute on behalf of your company TestCompany under its CLA. However, a CLA Manager claManager has now removed you from the authorization list")
+ assert.Contains(t, result, "
This is a notification email from EasyCLA regarding the Github Repository {{.RepositoryName}} associated with the CLA Group {{.CLAGroupName}}.
+
EasyCLA was notified that the Github Repository {{.OldRepositoryName}} was renamed to {{.NewRepositoryName}} from Github. The change was reflected to EasyCLA platform.
This is a notification email from EasyCLA regarding the Github Repository {{.RepositoryName}} associated with the CLA Group {{.CLAGroupName}}.
+
EasyCLA was notified that the Github Repository {{.RepositoryName}} was transferred from {{.OldGithubOrgName}} Organization to {{.NewGithubOrgName}} Organization from Github. The change was reflected to EasyCLA platform.
+`
+)
+
+const (
+ // GithubRepositoryTransferredFailedTemplateName is email template name for GithubRepositoryTransferredFailedTemplate
+ GithubRepositoryTransferredFailedTemplateName = "GithubRepositoryTransferredFailedTemplate"
+ // GithubRepositoryTransferredFailedTemplate is email template for
+ GithubRepositoryTransferredFailedTemplate = `
+
Hello {{.RecipientName}},
+
This is a notification email from EasyCLA regarding the Github Repository {{.RepositoryName}} associated with the CLA Group {{.CLAGroupName}}.
+
EasyCLA was notified that the Github Repository {{.RepositoryName}} was transferred from {{.OldGithubOrgName}} Organization to {{.NewGithubOrgName}} Organization from Github.
+
However, we detected that EasyCLA is not enabled for the new Github Organization {{.NewGithubOrgName}}. The Github Repository {{.RepositoryName}} is now disabled from EasyCLA platform.
+`
+)
+
+// RenderGithubRepositoryTransferredTemplate renders GithubRepositoryTransferredFailedTemplate or GithubRepositoryTransferredTemplate
+func RenderGithubRepositoryTransferredTemplate(svc EmailTemplateService, claGroupID string, params GithubRepositoryTransferredTemplateParams, success bool) (string, error) {
+ claGroupParams, err := svc.GetCLAGroupTemplateParamsFromCLAGroup(claGroupID)
+ if err != nil {
+ return "", err
+ }
+
+ // assign the prefilled struct
+ params.CLAGroupTemplateParams = claGroupParams
+ if success {
+ return RenderTemplate(params.CLAGroupTemplateParams.Version, GithubRepositoryTransferredTemplateName, GithubRepositoryTransferredTemplate, params)
+ }
+ return RenderTemplate(params.CLAGroupTemplateParams.Version, GithubRepositoryTransferredFailedTemplateName, GithubRepositoryTransferredFailedTemplate, params)
+
+}
diff --git a/cla-backend-go/emails/github_actions_test.go b/cla-backend-go/emails/github_actions_test.go
new file mode 100644
index 000000000..89418ee98
--- /dev/null
+++ b/cla-backend-go/emails/github_actions_test.go
@@ -0,0 +1,130 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package emails
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGithubRepositoryDisabledTemplate(t *testing.T) {
+ params := GithubRepositoryDisabledTemplateParams{
+ GithubRepositoryActionTemplateParams: GithubRepositoryActionTemplateParams{
+ CommonEmailParams: CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ CLAGroupTemplateParams: CLAGroupTemplateParams{
+ CLAGroupName: "JohnsProject",
+ },
+ RepositoryName: "johnsRepository",
+ },
+ GithubAction: "deleted",
+ }
+
+ result, err := RenderTemplate(utils.V2, GithubRepositoryDisabledTemplateName, GithubRepositoryDisabledTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello CLA Manager")
+ assert.Contains(t, result, "regarding the Github Repository johnsRepository")
+ assert.Contains(t, result, "associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "Github Repository johnsRepository was deleted")
+}
+
+func TestGithubRepositoryArchivedTemplate(t *testing.T) {
+ params := GithubRepositoryArchivedTemplateParams{
+ GithubRepositoryActionTemplateParams: GithubRepositoryActionTemplateParams{
+ CommonEmailParams: CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ CLAGroupTemplateParams: CLAGroupTemplateParams{
+ CLAGroupName: "JohnsProject",
+ },
+ RepositoryName: "johnsRepository",
+ },
+ }
+
+ result, err := RenderTemplate(utils.V2, GithubRepositoryArchivedTemplateName, GithubRepositoryArchivedTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello CLA Manager")
+ assert.Contains(t, result, "regarding the Github Repository johnsRepository")
+ assert.Contains(t, result, "associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "Github Repository johnsRepository was archived")
+}
+
+func TestGithubRepositoryRenamedTemplate(t *testing.T) {
+ params := GithubRepositoryRenamedTemplateParams{
+ GithubRepositoryActionTemplateParams: GithubRepositoryActionTemplateParams{
+ CommonEmailParams: CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ CLAGroupTemplateParams: CLAGroupTemplateParams{
+ CLAGroupName: "JohnsProject",
+ },
+ RepositoryName: "johnsNewRepository",
+ },
+ OldRepositoryName: "johnsOldRepository",
+ NewRepositoryName: "johnsNewRepository",
+ }
+
+ result, err := RenderTemplate(utils.V2, GithubRepositoryRenamedTemplateName, GithubRepositoryRenamedTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello CLA Manager")
+ assert.Contains(t, result, "regarding the Github Repository johnsNewRepository")
+ assert.Contains(t, result, "associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "Github Repository johnsOldRepository was renamed to johnsNewRepository")
+}
+
+func TestGithubRepositoryTransferredTemplate(t *testing.T) {
+ params := GithubRepositoryTransferredTemplateParams{
+ GithubRepositoryActionTemplateParams: GithubRepositoryActionTemplateParams{
+ CommonEmailParams: CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ CLAGroupTemplateParams: CLAGroupTemplateParams{
+ CLAGroupName: "JohnsProject",
+ },
+ RepositoryName: "johnsNewRepository",
+ },
+ OldGithubOrgName: "johnsOldGithubOrg",
+ NewGithubOrgName: "johnsNewGithubOrg",
+ }
+
+ result, err := RenderTemplate(utils.V2, GithubRepositoryTransferredTemplateName, GithubRepositoryTransferredTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello CLA Manager")
+ assert.Contains(t, result, "regarding the Github Repository johnsNewRepository")
+ assert.Contains(t, result, "associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "Github Repository johnsNewRepository was transferred from johnsOldGithubOrg Organization to johnsNewGithubOrg Organization")
+}
+
+func TestGithubRepositoryTransferredFailedTemplate(t *testing.T) {
+ params := GithubRepositoryTransferredTemplateParams{
+ GithubRepositoryActionTemplateParams: GithubRepositoryActionTemplateParams{
+ CommonEmailParams: CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ CLAGroupTemplateParams: CLAGroupTemplateParams{
+ CLAGroupName: "JohnsProject",
+ },
+ RepositoryName: "johnsNewRepository",
+ },
+ OldGithubOrgName: "johnsOldGithubOrg",
+ NewGithubOrgName: "johnsNewGithubOrg",
+ }
+
+ result, err := RenderTemplate(utils.V2, GithubRepositoryTransferredFailedTemplateName, GithubRepositoryTransferredFailedTemplate,
+ params)
+ assert.NoError(t, err)
+ assert.Contains(t, result, "Hello CLA Manager")
+ assert.Contains(t, result, "regarding the Github Repository johnsNewRepository")
+ assert.Contains(t, result, "associated with the CLA Group JohnsProject")
+ assert.Contains(t, result, "Github Repository johnsNewRepository was transferred from johnsOldGithubOrg Organization to johnsNewGithubOrg Organization")
+ assert.Contains(t, result, "EasyCLA is not enabled for the new Github Organization johnsNewGithubOrg")
+ assert.Contains(t, result, "The Github Repository johnsNewRepository is now disabled")
+}
diff --git a/cla-backend-go/emails/prefill.go b/cla-backend-go/emails/prefill.go
index 790a1ef14..507330f15 100644
--- a/cla-backend-go/emails/prefill.go
+++ b/cla-backend-go/emails/prefill.go
@@ -20,6 +20,7 @@ import (
type EmailTemplateService interface {
PrefillV2CLAProjectParams(projectSFIDs []string) ([]CLAProjectParams, error)
GetCLAGroupTemplateParamsFromProjectSFID(claGroupVersion, projectSFID string) (CLAGroupTemplateParams, error)
+ GetCLAGroupTemplateParamsFromCLAGroup(claGroupID string) (CLAGroupTemplateParams, error)
}
type emailTemplateServiceProvider struct {
@@ -86,6 +87,22 @@ func (s *emailTemplateServiceProvider) GetCLAGroupTemplateParamsFromProjectSFID(
return s.getV1CLAGroupTemplateParamsFromProjectSFID(projectSFID)
}
+// GetCLAGroupTemplateParamsFromCLAGroup fills up the CLAGroupTemplateParams with the basic information, it's missing the
+// project information, if needed can be added later on...
+func (s *emailTemplateServiceProvider) GetCLAGroupTemplateParamsFromCLAGroup(claGroupID string) (CLAGroupTemplateParams, error) {
+ claGroupModel, err := s.claGroupRepository.GetCLAGroupByID(context.Background(), claGroupID, false)
+ if err != nil {
+ return CLAGroupTemplateParams{}, err
+ }
+
+ params := CLAGroupTemplateParams{}
+ params.CLAGroupName = claGroupModel.ProjectName
+ params.CorporateConsole = s.corporateConsoleV2
+ params.Version = claGroupModel.Version
+
+ return params, nil
+}
+
func (s *emailTemplateServiceProvider) getV2CLAGroupTemplateParamsFromProjectSFID(projectSFID string) (CLAGroupTemplateParams, error) {
projectCLAGroup, err := s.repository.GetClaGroupIDForProject(projectSFID)
if err != nil {
diff --git a/cla-backend-go/emails/service.go b/cla-backend-go/emails/service.go
new file mode 100644
index 000000000..dfefe72a3
--- /dev/null
+++ b/cla-backend-go/emails/service.go
@@ -0,0 +1,49 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package emails
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+)
+
+// Service is a service with some helper functions for rendering templates and also sending emails
+type Service interface {
+ EmailTemplateService
+ NotifyClaManagersForClaGroupID(ctx context.Context, claGrpoupID, subject, body string) error
+}
+
+type service struct {
+ EmailTemplateService
+ claService project.Service
+}
+
+// NewService is constructor for emails.Service
+func NewService(emailTemplateService EmailTemplateService, claService project.Service) Service {
+ return &service{
+ EmailTemplateService: emailTemplateService,
+ claService: claService,
+ }
+}
+
+func (s *service) NotifyClaManagersForClaGroupID(ctx context.Context, claGrpoupID, subject, body string) error {
+ claManagers, err := s.claService.GetCLAManagers(ctx, claGrpoupID)
+ if err != nil {
+ return fmt.Errorf("fetching cla manager for cla group : %s failed : %v", claGrpoupID, err)
+ }
+
+ if len(claManagers) == 0 {
+ return fmt.Errorf("no cla managers registered for the claGroup : %s, none to notify", claGrpoupID)
+ }
+
+ var recipientEmails []string
+ for _, claManager := range claManagers {
+ recipientEmails = append(recipientEmails, claManager.UserEmail)
+ }
+
+ return utils.SendEmail(subject, body, recipientEmails)
+}
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index cfa984ff9..e06b6ffca 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -38,6 +38,19 @@ type RepositoryDisabledEventData struct {
RepositoryName string
}
+// RepositoryRenamedEventData . . .
+type RepositoryRenamedEventData struct {
+ NewRepositoryName string
+ OldRepositoryName string
+}
+
+// RepositoryTransferredEventData . . .
+type RepositoryTransferredEventData struct {
+ RepositoryName string
+ OldGithubOrgName string
+ NewGithubOrgName string
+}
+
// RepositoryUpdatedEventData . . .
type RepositoryUpdatedEventData struct {
RepositoryName string
@@ -442,6 +455,26 @@ func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
+// GetEventDetailsString . . .
+func (ed *RepositoryRenamedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The GitHub repository renamed from %s to %s for the project %s", ed.OldRepositoryName, ed.NewRepositoryName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString . . .
+func (ed *RepositoryTransferredEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The GitHub repository : %s transferred from %s to %s Github Organization for the project %s", ed.RepositoryName, ed.OldGithubOrgName, ed.NewGithubOrgName, args.ProjectName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventDetailsString . . .
func (ed *RepositoryUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was updated for the project %s", ed.RepositoryName, args.ProjectName)
@@ -1107,6 +1140,44 @@ func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
+// GetEventSummaryString . . .
+func (ed *RepositoryRenamedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The GitHub repository was renamed from %s to %s", ed.OldRepositoryName, ed.NewRepositoryName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString . . .
+func (ed *RepositoryTransferredEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The GitHub repository : %s was transferred from %s to %s Github Organization", ed.RepositoryName, ed.OldGithubOrgName, ed.NewGithubOrgName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventSummaryString . . .
func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was updated", ed.RepositoryName)
diff --git a/cla-backend-go/events/event_types.go b/cla-backend-go/events/event_types.go
index ae6f84e73..4f36d5459 100644
--- a/cla-backend-go/events/event_types.go
+++ b/cla-backend-go/events/event_types.go
@@ -32,6 +32,8 @@ const (
UserDeleted = "user.deleted"
RepositoryAdded = "repository.added"
+ RepositoryRenamed = "repository.renamed"
+ RepositoryTransferred = "repository.transferred"
RepositoryDisabled = "repository.disabled"
RepositoryUpdated = "repository.updated"
RepositoryBranchProtectionAdded = "repository.branchprotection.updated"
diff --git a/cla-backend-go/events/mock.go b/cla-backend-go/events/mock.go
new file mode 100644
index 000000000..93c0baa68
--- /dev/null
+++ b/cla-backend-go/events/mock.go
@@ -0,0 +1,169 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+//
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/communitybridge/easycla/cla-backend-go/events (interfaces: Service)
+
+// Package events is a generated GoMock package.
+package events
+
+import (
+ context "context"
+ models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ events0 "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
+)
+
+// MockService is a mock of Service interface
+type MockService struct {
+ ctrl *gomock.Controller
+ recorder *MockServiceMockRecorder
+}
+
+// MockServiceMockRecorder is the mock recorder for MockService
+type MockServiceMockRecorder struct {
+ mock *MockService
+}
+
+// NewMockService creates a new mock instance
+func NewMockService(ctrl *gomock.Controller) *MockService {
+ mock := &MockService{ctrl: ctrl}
+ mock.recorder = &MockServiceMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockService) EXPECT() *MockServiceMockRecorder {
+ return m.recorder
+}
+
+// GetClaGroupEvents mocks base method
+func (m *MockService) GetClaGroupEvents(arg0 string, arg1 *string, arg2 *int64, arg3 bool, arg4 *string) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetClaGroupEvents", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetClaGroupEvents indicates an expected call of GetClaGroupEvents
+func (mr *MockServiceMockRecorder) GetClaGroupEvents(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClaGroupEvents", reflect.TypeOf((*MockService)(nil).GetClaGroupEvents), arg0, arg1, arg2, arg3, arg4)
+}
+
+// GetCompanyClaGroupEvents mocks base method
+func (m *MockService) GetCompanyClaGroupEvents(arg0, arg1, arg2 string, arg3 *string, arg4 *int64, arg5 bool) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetCompanyClaGroupEvents", arg0, arg1, arg2, arg3, arg4, arg5)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetCompanyClaGroupEvents indicates an expected call of GetCompanyClaGroupEvents
+func (mr *MockServiceMockRecorder) GetCompanyClaGroupEvents(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCompanyClaGroupEvents", reflect.TypeOf((*MockService)(nil).GetCompanyClaGroupEvents), arg0, arg1, arg2, arg3, arg4, arg5)
+}
+
+// GetCompanyEvents mocks base method
+func (m *MockService) GetCompanyEvents(arg0, arg1 string, arg2 *string, arg3 *int64, arg4 bool) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetCompanyEvents", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetCompanyEvents indicates an expected call of GetCompanyEvents
+func (mr *MockServiceMockRecorder) GetCompanyEvents(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCompanyEvents", reflect.TypeOf((*MockService)(nil).GetCompanyEvents), arg0, arg1, arg2, arg3, arg4)
+}
+
+// GetCompanyFoundationEvents mocks base method
+func (m *MockService) GetCompanyFoundationEvents(arg0, arg1, arg2 string, arg3 *string, arg4 *int64, arg5 bool) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetCompanyFoundationEvents", arg0, arg1, arg2, arg3, arg4, arg5)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetCompanyFoundationEvents indicates an expected call of GetCompanyFoundationEvents
+func (mr *MockServiceMockRecorder) GetCompanyFoundationEvents(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCompanyFoundationEvents", reflect.TypeOf((*MockService)(nil).GetCompanyFoundationEvents), arg0, arg1, arg2, arg3, arg4, arg5)
+}
+
+// GetFoundationEvents mocks base method
+func (m *MockService) GetFoundationEvents(arg0 string, arg1 *string, arg2 *int64, arg3 bool, arg4 *string) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetFoundationEvents", arg0, arg1, arg2, arg3, arg4)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetFoundationEvents indicates an expected call of GetFoundationEvents
+func (mr *MockServiceMockRecorder) GetFoundationEvents(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFoundationEvents", reflect.TypeOf((*MockService)(nil).GetFoundationEvents), arg0, arg1, arg2, arg3, arg4)
+}
+
+// GetRecentEvents mocks base method
+func (m *MockService) GetRecentEvents(arg0 *int64) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetRecentEvents", arg0)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetRecentEvents indicates an expected call of GetRecentEvents
+func (mr *MockServiceMockRecorder) GetRecentEvents(arg0 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRecentEvents", reflect.TypeOf((*MockService)(nil).GetRecentEvents), arg0)
+}
+
+// LogEvent mocks base method
+func (m *MockService) LogEvent(arg0 *LogEventArgs) {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "LogEvent", arg0)
+}
+
+// LogEvent indicates an expected call of LogEvent
+func (mr *MockServiceMockRecorder) LogEvent(arg0 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogEvent", reflect.TypeOf((*MockService)(nil).LogEvent), arg0)
+}
+
+// LogEventWithContext mocks base method
+func (m *MockService) LogEventWithContext(arg0 context.Context, arg1 *LogEventArgs) {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "LogEventWithContext", arg0, arg1)
+}
+
+// LogEventWithContext indicates an expected call of LogEventWithContext
+func (mr *MockServiceMockRecorder) LogEventWithContext(arg0, arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogEventWithContext", reflect.TypeOf((*MockService)(nil).LogEventWithContext), arg0, arg1)
+}
+
+// SearchEvents mocks base method
+func (m *MockService) SearchEvents(arg0 *events0.SearchEventsParams) (*models.EventList, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "SearchEvents", arg0)
+ ret0, _ := ret[0].(*models.EventList)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// SearchEvents indicates an expected call of SearchEvents
+func (mr *MockServiceMockRecorder) SearchEvents(arg0 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchEvents", reflect.TypeOf((*MockService)(nil).SearchEvents), arg0)
+}
diff --git a/cla-backend-go/github/branch_protection/mock.go b/cla-backend-go/github/branch_protection/mock.go
index 456925c0e..ca127ac4e 100644
--- a/cla-backend-go/github/branch_protection/mock.go
+++ b/cla-backend-go/github/branch_protection/mock.go
@@ -10,11 +10,10 @@ package branch_protection
import (
context "context"
- reflect "reflect"
-
gomock "github.com/golang/mock/gomock"
github "github.com/google/go-github/v33/github"
githubv4 "github.com/shurcooL/githubv4"
+ reflect "reflect"
)
// MockCombinedRepository is a mock of CombinedRepository interface
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index e446e0434..8eb3841ba 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -1,18 +1,18 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT
+//
// Code generated by MockGen. DO NOT EDIT.
-// Source: repository.go
+// Source: github.com/communitybridge/easycla/cla-backend-go/github_organizations (interfaces: Repository)
// Package github_organizations is a generated GoMock package.
package github_organizations
import (
context "context"
- reflect "reflect"
-
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
)
// MockRepository is a mock of Repository interface
@@ -39,118 +39,118 @@ func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
}
// AddGithubOrganization mocks base method
-func (m *MockRepository) AddGithubOrganization(ctx context.Context, parentProjectSFID, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+func (m *MockRepository) AddGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddGithubOrganization", ctx, parentProjectSFID, projectSFID, input)
+ ret := m.ctrl.Call(m, "AddGithubOrganization", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*models.GithubOrganization)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddGithubOrganization indicates an expected call of AddGithubOrganization
-func (mr *MockRepositoryMockRecorder) AddGithubOrganization(ctx, parentProjectSFID, projectSFID, input interface{}) *gomock.Call {
+func (mr *MockRepositoryMockRecorder) AddGithubOrganization(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubOrganization", reflect.TypeOf((*MockRepository)(nil).AddGithubOrganization), ctx, parentProjectSFID, projectSFID, input)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubOrganization", reflect.TypeOf((*MockRepository)(nil).AddGithubOrganization), arg0, arg1, arg2, arg3)
}
-// GetGithubOrganizations mocks base method
-func (m *MockRepository) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+// DeleteGithubOrganization mocks base method
+func (m *MockRepository) DeleteGithubOrganization(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizations", ctx, projectSFID)
- ret0, _ := ret[0].(*models.GithubOrganizations)
- ret1, _ := ret[1].(error)
- return ret0, ret1
+ ret := m.ctrl.Call(m, "DeleteGithubOrganization", arg0, arg1, arg2)
+ ret0, _ := ret[0].(error)
+ return ret0
}
-// GetGithubOrganizations indicates an expected call of GetGithubOrganizations
-func (mr *MockRepositoryMockRecorder) GetGithubOrganizations(ctx, projectSFID interface{}) *gomock.Call {
+// DeleteGithubOrganization indicates an expected call of DeleteGithubOrganization
+func (mr *MockRepositoryMockRecorder) DeleteGithubOrganization(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizations", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizations), ctx, projectSFID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganization", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganization), arg0, arg1, arg2)
}
-// GetGithubOrganizationsByParent mocks base method
-func (m *MockRepository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
+// DeleteGithubOrganizationByParent mocks base method
+func (m *MockRepository) DeleteGithubOrganizationByParent(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizationsByParent", ctx, parentProjectSFID)
- ret0, _ := ret[0].(*models.GithubOrganizations)
- ret1, _ := ret[1].(error)
- return ret0, ret1
+ ret := m.ctrl.Call(m, "DeleteGithubOrganizationByParent", arg0, arg1, arg2)
+ ret0, _ := ret[0].(error)
+ return ret0
}
-// GetGithubOrganizationsByParent indicates an expected call of GetGithubOrganizationsByParent
-func (mr *MockRepositoryMockRecorder) GetGithubOrganizationsByParent(ctx, parentProjectSFID interface{}) *gomock.Call {
+// DeleteGithubOrganizationByParent indicates an expected call of DeleteGithubOrganizationByParent
+func (mr *MockRepositoryMockRecorder) DeleteGithubOrganizationByParent(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationsByParent", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationsByParent), ctx, parentProjectSFID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganizationByParent", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganizationByParent), arg0, arg1, arg2)
}
// GetGithubOrganization mocks base method
-func (m *MockRepository) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
+func (m *MockRepository) GetGithubOrganization(arg0 context.Context, arg1 string) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganization", ctx, githubOrganizationName)
+ ret := m.ctrl.Call(m, "GetGithubOrganization", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganization)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetGithubOrganization indicates an expected call of GetGithubOrganization
-func (mr *MockRepositoryMockRecorder) GetGithubOrganization(ctx, githubOrganizationName interface{}) *gomock.Call {
+func (mr *MockRepositoryMockRecorder) GetGithubOrganization(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganization", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganization), ctx, githubOrganizationName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganization", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganization), arg0, arg1)
}
// GetGithubOrganizationByName mocks base method
-func (m *MockRepository) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
+func (m *MockRepository) GetGithubOrganizationByName(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizationByName", ctx, githubOrganizationName)
+ ret := m.ctrl.Call(m, "GetGithubOrganizationByName", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetGithubOrganizationByName indicates an expected call of GetGithubOrganizationByName
-func (mr *MockRepositoryMockRecorder) GetGithubOrganizationByName(ctx, githubOrganizationName interface{}) *gomock.Call {
+func (mr *MockRepositoryMockRecorder) GetGithubOrganizationByName(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationByName", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationByName), ctx, githubOrganizationName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationByName", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationByName), arg0, arg1)
}
-// UpdateGithubOrganization mocks base method
-func (m *MockRepository) UpdateGithubOrganization(ctx context.Context, projectSFID, organizationName string, autoEnabled, branchProtectionEnabled bool) error {
+// GetGithubOrganizations mocks base method
+func (m *MockRepository) GetGithubOrganizations(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateGithubOrganization", ctx, projectSFID, organizationName, autoEnabled, branchProtectionEnabled)
- ret0, _ := ret[0].(error)
- return ret0
+ ret := m.ctrl.Call(m, "GetGithubOrganizations", arg0, arg1)
+ ret0, _ := ret[0].(*models.GithubOrganizations)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
}
-// UpdateGithubOrganization indicates an expected call of UpdateGithubOrganization
-func (mr *MockRepositoryMockRecorder) UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, branchProtectionEnabled interface{}) *gomock.Call {
+// GetGithubOrganizations indicates an expected call of GetGithubOrganizations
+func (mr *MockRepositoryMockRecorder) GetGithubOrganizations(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGithubOrganization), ctx, projectSFID, organizationName, autoEnabled, branchProtectionEnabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizations", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizations), arg0, arg1)
}
-// DeleteGithubOrganization mocks base method
-func (m *MockRepository) DeleteGithubOrganization(ctx context.Context, projectSFID, githubOrgName string) error {
+// GetGithubOrganizationsByParent mocks base method
+func (m *MockRepository) GetGithubOrganizationsByParent(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DeleteGithubOrganization", ctx, projectSFID, githubOrgName)
- ret0, _ := ret[0].(error)
- return ret0
+ ret := m.ctrl.Call(m, "GetGithubOrganizationsByParent", arg0, arg1)
+ ret0, _ := ret[0].(*models.GithubOrganizations)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
}
-// DeleteGithubOrganization indicates an expected call of DeleteGithubOrganization
-func (mr *MockRepositoryMockRecorder) DeleteGithubOrganization(ctx, projectSFID, githubOrgName interface{}) *gomock.Call {
+// GetGithubOrganizationsByParent indicates an expected call of GetGithubOrganizationsByParent
+func (mr *MockRepositoryMockRecorder) GetGithubOrganizationsByParent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganization", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganization), ctx, projectSFID, githubOrgName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationsByParent", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationsByParent), arg0, arg1)
}
-// DeleteGithubOrganizationByParent mocks base method
-func (m *MockRepository) DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID, githubOrgName string) error {
+// UpdateGithubOrganization mocks base method
+func (m *MockRepository) UpdateGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 string, arg5 bool) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DeleteGithubOrganizationByParent", ctx, parentProjectSFID, githubOrgName)
+ ret := m.ctrl.Call(m, "UpdateGithubOrganization", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(error)
return ret0
}
-// DeleteGithubOrganizationByParent indicates an expected call of DeleteGithubOrganizationByParent
-func (mr *MockRepositoryMockRecorder) DeleteGithubOrganizationByParent(ctx, parentProjectSFID, githubOrgName interface{}) *gomock.Call {
+// UpdateGithubOrganization indicates an expected call of UpdateGithubOrganization
+func (mr *MockRepositoryMockRecorder) UpdateGithubOrganization(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganizationByParent", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganizationByParent), ctx, parentProjectSFID, githubOrgName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGithubOrganization), arg0, arg1, arg2, arg3, arg4, arg5)
}
diff --git a/cla-backend-go/repositories/mock/mock_repository.go b/cla-backend-go/repositories/mock/mock_repository.go
index 88862f212..568756836 100644
--- a/cla-backend-go/repositories/mock/mock_repository.go
+++ b/cla-backend-go/repositories/mock/mock_repository.go
@@ -10,10 +10,9 @@ package mock
import (
context "context"
- reflect "reflect"
-
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
)
// MockRepository is a mock of Repository interface
@@ -244,16 +243,16 @@ func (mr *MockRepositoryMockRecorder) GetCLAGroupRepositoriesGroupByOrgs(ctx, pr
}
// ListProjectRepositories mocks base method
-func (m *MockRepository) ListProjectRepositories(ctx context.Context, externalProjectID, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
+func (m *MockRepository) ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ListProjectRepositories", ctx, externalProjectID, projectSFID, enabled)
+ ret := m.ctrl.Call(m, "ListProjectRepositories", ctx, projectSFID, enabled)
ret0, _ := ret[0].(*models.ListGithubRepositories)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListProjectRepositories indicates an expected call of ListProjectRepositories
-func (mr *MockRepositoryMockRecorder) ListProjectRepositories(ctx, externalProjectID, projectSFID, enabled interface{}) *gomock.Call {
+func (mr *MockRepositoryMockRecorder) ListProjectRepositories(ctx, projectSFID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectRepositories", reflect.TypeOf((*MockRepository)(nil).ListProjectRepositories), ctx, externalProjectID, projectSFID, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectRepositories", reflect.TypeOf((*MockRepository)(nil).ListProjectRepositories), ctx, projectSFID, enabled)
}
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index e5c1a9ce0..339b28dff 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -10,10 +10,9 @@ package mock
import (
context "context"
- reflect "reflect"
-
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
+ reflect "reflect"
)
// MockService is a mock of Service interface
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index f4682e6b4..af29b3f98 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -7,6 +7,9 @@ import (
"context"
"errors"
"fmt"
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/communitybridge/easycla/cla-backend-go/emails"
+ v1GithubOrg "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
"strconv"
"github.com/sirupsen/logrus"
@@ -33,18 +36,36 @@ type Service interface {
type eventHandlerService struct {
githubRepo repositories.Repository
+ githubOrgRepo v1GithubOrg.Repository
eventService events.Service
autoEnableService dynamo_events.AutoEnableService
+ emailService emails.Service
+ sendEmail bool
}
// NewService creates a new instance of the Event Handler Service
func NewService(githubRepo repositories.Repository,
+ githubOrgRepo v1GithubOrg.Repository,
eventService events.Service,
- autoEnableService dynamo_events.AutoEnableService) Service {
+ autoEnableService dynamo_events.AutoEnableService,
+ emailService emails.Service) Service {
+
+ return newService(githubRepo, githubOrgRepo, eventService, autoEnableService, emailService, true)
+}
+
+func newService(githubRepo repositories.Repository,
+ githubOrgRepo v1GithubOrg.Repository,
+ eventService events.Service,
+ autoEnableService dynamo_events.AutoEnableService,
+ emailService emails.Service,
+ sendEmail bool) Service {
return &eventHandlerService{
githubRepo: githubRepo,
+ githubOrgRepo: githubOrgRepo,
eventService: eventService,
autoEnableService: autoEnableService,
+ emailService: emailService,
+ sendEmail: sendEmail,
}
}
@@ -54,15 +75,29 @@ func (s *eventHandlerService) ProcessRepositoryEvent(event *github.RepositoryEve
"functionName": "v2.github_activity.service.ProcessRepositoryEvent",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
- log.Debugf("ProcessRepositoryEvent called for action : %s", *event.Action)
if event.Action == nil {
return fmt.Errorf("no action found in event payload")
}
+
+ if event.Repo == nil {
+ return fmt.Errorf("missing repository object in event payload")
+ }
+
+ log.Debugf("ProcessRepositoryEvent called for action : %s", *event.Action)
switch *event.Action {
case "created":
return s.handleRepositoryAddedAction(ctx, event.Sender, event.Repo)
+ case "renamed":
+ return s.handleRepositoryRenamedAction(ctx, event.Sender, event.Repo)
+ case "transferred":
+ if event.Org == nil {
+ return fmt.Errorf("missing org object in event payload")
+ }
+ return s.handleRepositoryTransferredAction(ctx, event.Sender, event.Repo, event.Org)
case "deleted":
return s.handleRepositoryRemovedAction(ctx, event.Sender, event.Repo)
+ case "archived":
+ return s.handleRepositoryArchivedAction(ctx, event.Sender, event.Repo)
default:
log.WithFields(f).Warnf("no handler for action : %s", *event.Action)
}
@@ -155,6 +190,262 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
},
})
+ if s.sendEmail {
+ subject := fmt.Sprintf("EasyCLA: Github Repository Was Removed")
+ body, err := emails.RenderGithubRepositoryDisabledTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryDisabledTemplateParams{
+ GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
+ CommonEmailParams: emails.CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ RepositoryName: repoModel.RepositoryName,
+ },
+ GithubAction: "deleted",
+ })
+
+ if err != nil {
+ log.WithFields(f).Warnf("rendering email template failed : %v", err)
+ return nil
+ }
+
+ if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryProjectID, subject, body); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+
+ }
+
+ return nil
+}
+
+// handles the event when a repository is renamed so we rename the repo in our records as well
+func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context, sender *github.User, repo *github.Repository) error {
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.handleRepositoryRenamedAction",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if repo.ID == nil || *repo.ID == 0 {
+ return fmt.Errorf("missing repo id")
+ }
+ repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
+ repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ if err != nil {
+ if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
+ log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
+ return nil
+ }
+ return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
+ }
+
+ if _, err := s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
+ RepositoryName: repo.Name,
+ Note: "repository was renamed externally",
+ }); err != nil {
+ log.WithFields(f).Warnf("renaming repo : %s failed : %v", *repo.FullName, err)
+ return err
+ }
+
+ if sender == nil || sender.Login == nil {
+ return fmt.Errorf("missing sender can not log the event")
+ }
+
+ // sending event for the action
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryRenamed,
+ ProjectID: repoModel.RepositoryProjectID,
+ UserID: *sender.Login,
+ EventData: &events.RepositoryRenamedEventData{
+ NewRepositoryName: *repo.Name,
+ OldRepositoryName: repoModel.RepositoryName,
+ },
+ })
+
+ if s.sendEmail {
+ subject := fmt.Sprintf("EasyCLA: Github Repository Was Renamed")
+ body, err := emails.RenderGithubRepositoryRenamedTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryRenamedTemplateParams{
+ GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
+ CommonEmailParams: emails.CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ RepositoryName: repoModel.RepositoryName,
+ },
+ NewRepositoryName: *repo.Name,
+ OldRepositoryName: repoModel.RepositoryName,
+ })
+
+ if err != nil {
+ log.WithFields(f).Warnf("rendering email template failed : %v", err)
+ return nil
+ }
+
+ if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryProjectID, subject, body); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+
+ }
+
+ return nil
+}
+
+func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Context, sender *github.User, repo *github.Repository, org *github.Organization) error {
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.handleRepositoryTransferredAction",
+ "repositoryName": *repo.Name,
+ "newGithubOrganization": *org.Name,
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if repo.ID == nil || *repo.ID == 0 {
+ return fmt.Errorf("missing repo id")
+ }
+ repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
+ repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ if err != nil {
+ if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
+ log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
+ return nil
+ }
+ return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
+ }
+
+ newOrganizationName := *org.Name
+ oldOrganizationName := repoModel.RepositoryOrganizationName
+ // first check if it's a different organization name (could be a duplicate event)
+ if oldOrganizationName == *org.Name {
+ msg := fmt.Sprintf("nothing to change for github repo : %s, probably duplicate event was sent", repoModel.RepositoryName)
+ log.WithFields(f).Warnf(msg)
+ return fmt.Errorf(msg)
+ }
+
+ // fetch the old and the new github orgs from the db
+ oldGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, oldOrganizationName)
+ if err != nil {
+ return fmt.Errorf("fetching the old organization name : %s failed : %v", oldOrganizationName, err)
+ }
+
+ newGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, *org.Name)
+ if err != nil {
+ return fmt.Errorf("fetching the new organization name : %s failed : %v", newOrganizationName, err)
+ }
+
+ // we need to check if the new org name has autoenabled and have a cla group set otherwise we can't proceed
+ if !newGithubOrg.AutoEnabled || newGithubOrg.AutoEnabledClaGroupID == "" {
+ log.WithFields(f).Warnf("can't proceed with repo transfer operation because the new org doesn't have autoenabled=true, disabling the repo : %s", repoModel.RepositoryName)
+ if err := s.githubRepo.DisableRepository(ctx, repoModel.RepositoryID); err != nil {
+ return fmt.Errorf("disabling the repo : %s failed : %v", repoModel.RepositoryID, err)
+ }
+
+ // send event for the disabled repository.
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryDisabled,
+ ProjectID: repoModel.RepositoryProjectID,
+ UserID: *sender.Login,
+ EventData: &events.RepositoryDisabledEventData{
+ RepositoryName: repoModel.RepositoryName,
+ },
+ })
+
+ if s.sendEmail{
+ if err := s.notifyForGithubRepositoryTransferred(ctx, repoModel, oldGithubOrg, newGithubOrg, false); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+ }
+
+ return fmt.Errorf("aborting the repository : %s transfer, new githubOrg : %s doesn't have claGroupID set", repoModel.RepositoryName, newGithubOrg.OrganizationName)
+ }
+
+ _, err = s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
+ Note: fmt.Sprintf("repository was transferred from org : %s to : %s", oldGithubOrg.OrganizationName, newGithubOrg.OrganizationName),
+ RepositoryOrganizationName: aws.String(newGithubOrg.OrganizationName),
+ RepositoryURL: repo.URL,
+ })
+
+ if err != nil {
+ return fmt.Errorf("repository : %s transfer failed for new github org : %s : %v", repoModel.RepositoryID, newGithubOrg.OrganizationName, err)
+ }
+
+ // sending event for the action
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryTransferred,
+ ProjectID: repoModel.RepositoryProjectID,
+ UserID: *sender.Login,
+ EventData: &events.RepositoryTransferredEventData{
+ RepositoryName: repoModel.RepositoryName,
+ OldGithubOrgName: oldGithubOrg.OrganizationName,
+ NewGithubOrgName: newGithubOrg.OrganizationName,
+ },
+ })
+
+ if s.sendEmail {
+ if err := s.notifyForGithubRepositoryTransferred(ctx, repoModel, oldGithubOrg, newGithubOrg, true); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+ }
+
+ return nil
+}
+
+func (s *eventHandlerService) notifyForGithubRepositoryTransferred(ctx context.Context, repoModel *models.GithubRepository, oldGithubOrg *models.GithubOrganization, newGithubOrg *models.GithubOrganization, success bool) error {
+ subject := fmt.Sprintf("EasyCLA: Github Repository Was Transferred")
+ body, err := emails.RenderGithubRepositoryTransferredTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryTransferredTemplateParams{
+ GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
+ CommonEmailParams: emails.CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ RepositoryName: repoModel.RepositoryName,
+ },
+ OldGithubOrgName: oldGithubOrg.OrganizationName,
+ NewGithubOrgName: newGithubOrg.OrganizationName,
+ }, success)
+
+ if err != nil {
+ return fmt.Errorf("rendering email template failed : %v", err)
+ }
+
+ err = s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryProjectID, subject, body)
+ return err
+}
+
+func (s *eventHandlerService) handleRepositoryArchivedAction(ctx context.Context, sender *github.User, repo *github.Repository) error {
+ f := logrus.Fields{
+ "functionName": "v2.github_activity.service.handleRepositoryArchivedAction",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ if repo.ID == nil || *repo.ID == 0 {
+ return fmt.Errorf("missing repo id")
+ }
+ repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
+ repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ if err != nil {
+ if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
+ log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
+ return nil
+ }
+ return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
+ }
+
+ if s.sendEmail {
+ subject := fmt.Sprintf("EasyCLA: Github Repository Was Archived")
+ body, err := emails.RenderGithubRepositoryArchivedTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryArchivedTemplateParams{
+ GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
+ CommonEmailParams: emails.CommonEmailParams{
+ RecipientName: "CLA Manager",
+ },
+ RepositoryName: repoModel.RepositoryName,
+ },
+ })
+
+ if err != nil {
+ log.WithFields(f).Warnf("rendering email template failed : %v", err)
+ return nil
+ }
+
+ if err := s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryProjectID, subject, body); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+
+ }
+
return nil
}
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
new file mode 100644
index 000000000..16f09806e
--- /dev/null
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -0,0 +1,187 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package github_activity
+
+import (
+ "fmt"
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
+ "github.com/communitybridge/easycla/cla-backend-go/repositories/mock"
+ "github.com/golang/mock/gomock"
+ "github.com/google/go-github/v33/github"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryRenamedAction(t *testing.T) {
+ repoID := "1f15f478-0659-43f3-bcf1-383052de7616"
+ repoName := "org1/repo-name"
+ newRepoName := "org1/repo-name-new"
+
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ githubOrganizationRepo := github_organizations.NewMockRepository(ctrl)
+ githubRepo := mock.NewMockRepository(ctrl)
+ githubRepo.EXPECT().
+ GetRepositoryByGithubID(gomock.Any(), "1", true).
+ Return(&models.GithubRepository{
+ Enabled: true,
+ RepositoryExternalID: "1",
+ RepositoryID: repoID,
+ RepositoryName: repoName,
+ RepositoryOrganizationName: "org1",
+ }, nil)
+
+ githubRepo.EXPECT().
+ UpdateGithubRepository(gomock.Any(), repoID, &models.GithubRepositoryInput{
+ RepositoryName: &newRepoName,
+ Note: "repository was renamed externally",
+ }).Return(nil, nil)
+
+ eventsService := events.NewMockService(ctrl)
+ eventsService.EXPECT().
+ LogEventWithContext(gomock.Any(), &events.LogEventArgs{
+ EventType: events.RepositoryRenamed,
+ UserID: "githubLoginValue",
+ ProjectID: "",
+ EventData: &events.RepositoryRenamedEventData{
+ NewRepositoryName: newRepoName,
+ OldRepositoryName: repoName,
+ },
+ }).Return()
+
+ activityService := newService(githubRepo, githubOrganizationRepo, eventsService, nil, nil, false)
+ err := activityService.ProcessRepositoryEvent(&github.RepositoryEvent{
+ Action: aws.String("renamed"),
+ Repo: &github.Repository{
+ ID: aws.Int64(1),
+ Name: &newRepoName,
+ },
+ Org: nil,
+ Sender: &github.User{
+ Login: aws.String("githubLoginValue"),
+ },
+ Installation: nil,
+ })
+
+ assert.NoError(t, err)
+}
+
+func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredAction(t *testing.T) {
+ repoID := "1f15f478-0659-43f3-bcf1-383052de7616"
+ repoName := "org1/repo-name"
+ oldOrgName := "org1"
+ newOrgName := "org2"
+ newRepoUrl := "org2/repo-name"
+
+ testCases := []struct {
+ name string
+ newGithubOrg *models.GithubOrganization
+ }{
+ {
+ name: "success new org is enabled and and has cla group",
+ newGithubOrg: &models.GithubOrganization{
+ OrganizationName: newOrgName,
+ AutoEnabled: true,
+ AutoEnabledClaGroupID: "c057ed9a-4235-4acf-80bd-c7b4c235eff9",
+ },
+ },
+ {
+ name: "failure new org is disabled and no cla group",
+ newGithubOrg: &models.GithubOrganization{
+ OrganizationName: newOrgName,
+ AutoEnabled: false,
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ githubOrganizationRepo := github_organizations.NewMockRepository(ctrl)
+ githubRepo := mock.NewMockRepository(ctrl)
+ githubRepo.EXPECT().
+ GetRepositoryByGithubID(gomock.Any(), "1", true).
+ Return(&models.GithubRepository{
+ Enabled: true,
+ RepositoryExternalID: "1",
+ RepositoryID: repoID,
+ RepositoryName: repoName,
+ RepositoryOrganizationName: oldOrgName,
+ }, nil)
+
+ // return the old one
+ githubOrganizationRepo.EXPECT().
+ GetGithubOrganization(gomock.Any(), oldOrgName).
+ Return(&models.GithubOrganization{
+ OrganizationName: oldOrgName,
+ }, nil)
+
+ // return the new one
+ githubOrganizationRepo.EXPECT().
+ GetGithubOrganization(gomock.Any(), newOrgName).
+ Return(tc.newGithubOrg, nil)
+
+ eventsService := events.NewMockService(ctrl)
+ if tc.newGithubOrg.AutoEnabled {
+ githubRepo.EXPECT().
+ UpdateGithubRepository(gomock.Any(), repoID, &models.GithubRepositoryInput{
+ RepositoryOrganizationName: &newOrgName,
+ RepositoryURL: &newRepoUrl,
+ Note: fmt.Sprintf("repository was transferred from org : %s to : %s", oldOrgName, newOrgName),
+ }).Return(nil, nil)
+
+ eventsService.EXPECT().
+ LogEventWithContext(gomock.Any(), &events.LogEventArgs{
+ EventType: events.RepositoryTransferred,
+ UserID: "githubLoginValue",
+ ProjectID: "",
+ EventData: &events.RepositoryTransferredEventData{
+ RepositoryName: repoName,
+ OldGithubOrgName: oldOrgName,
+ NewGithubOrgName: newOrgName,
+ },
+ }).Return()
+ }else{
+ githubRepo.EXPECT().
+ DisableRepository(gomock.Any(), repoID).Return(nil)
+ eventsService.EXPECT().
+ LogEventWithContext(gomock.Any(), &events.LogEventArgs{
+ EventType: events.RepositoryDisabled,
+ UserID: "githubLoginValue",
+ ProjectID: "",
+ EventData: &events.RepositoryDisabledEventData{
+ RepositoryName: repoName,
+ },
+ }).Return()
+ }
+
+ activityService := newService(githubRepo, githubOrganizationRepo, eventsService, nil, nil, false)
+ err := activityService.ProcessRepositoryEvent(&github.RepositoryEvent{
+ Action: aws.String("transferred"),
+ Repo: &github.Repository{
+ ID: aws.Int64(1),
+ Name: &repoName,
+ URL: &newRepoUrl,
+ },
+ Org: &github.Organization{
+ Name: &newOrgName,
+ },
+ Sender: &github.User{
+ Login: aws.String("githubLoginValue"),
+ },
+ Installation: nil,
+ })
+
+ if tc.newGithubOrg.AutoEnabled{
+ assert.NoError(tt, err)
+ }else{
+ assert.Error(tt, err)
+ }
+ })
+ }
+}
From 9955f36dd94e626156f40b7b009d4ba7200f6609 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 22 Apr 2021 10:07:06 -0700
Subject: [PATCH 0238/1276] Added Permission Checks Logging (#2893)
- Added additional permission check logging
- Added permission check for child projects (if any have access, we allow)
- Added missing ctx parameter to permission check functions - to be consistent and have proper logging
Signed-off-by: David Deal
---
.../utils/utils_user_auth_lambda.go | 103 +++++++++++++---
.../utils/utils_user_auth_standalone.go | 112 +++++++++++++++---
cla-backend-go/v2/cla_manager/handlers.go | 6 +-
cla-backend-go/v2/company/handlers.go | 24 ++--
cla-backend-go/v2/events/handlers.go | 2 +-
cla-backend-go/v2/metrics/handlers.go | 2 +-
cla-backend-go/v2/sign/handlers.go | 2 +-
cla-backend-go/v2/signatures/handlers.go | 53 +++++----
8 files changed, 231 insertions(+), 73 deletions(-)
diff --git a/cla-backend-go/utils/utils_user_auth_lambda.go b/cla-backend-go/utils/utils_user_auth_lambda.go
index 9b0ccee84..0e9bfbd88 100644
--- a/cla-backend-go/utils/utils_user_auth_lambda.go
+++ b/cla-backend-go/utils/utils_user_auth_lambda.go
@@ -27,13 +27,28 @@ func IsUserAdmin(user *auth.User) bool {
}
// IsUserAuthorizedForOrganization helper function for determining if the user is authorized for this company
-func IsUserAuthorizedForOrganization(user *auth.User, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForOrganization(ctx context.Context, user *auth.User, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorizedForOrganizationScope(companySFID)
+ val := user.IsUserAuthorizedForOrganizationScope(companySFID)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for companySFID: %s", companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for companySFID: %s", companySFID)
+ }
+ return val
}
// IsUserAuthorizedForProjectTree helper function for determining if the user is authorized for this project hierarchy/tree
@@ -48,13 +63,17 @@ func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projec
}
if adminScopeAllowed && user.Admin {
- log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- log.WithFields(f).Debug("checking scope...")
+ log.WithFields(f).Debugf("checking project scope for projectSFID: %s...", projectSFID)
val := user.IsUserAuthorized(auth.Project, projectSFID, true)
- log.WithFields(f).Debugf("user allowed: %t", val)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s", projectSFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s", projectSFID)
+ }
return val
}
@@ -70,13 +89,17 @@ func IsUserAuthorizedForProject(ctx context.Context, user *auth.User, projectSFI
}
if adminScopeAllowed && user.Admin {
- log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- log.WithFields(f).Debug("checking scope...")
+ log.WithFields(f).Debugf("checking project scope for projectSFID: %s...", projectSFID)
val := user.IsUserAuthorizedForProjectScope(projectSFID)
- log.WithFields(f).Debugf("user allowed: %t", val)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s", projectSFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s", projectSFID)
+ }
return val
}
@@ -109,35 +132,85 @@ func IsUserAuthorizedForAnyProjects(ctx context.Context, user *auth.User, projec
}
// IsUserAuthorizedForProjectOrganization helper function for determining if the user is authorized for this project organization scope
-func IsUserAuthorizedForProjectOrganization(user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectOrganization(ctx context.Context, user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorizedByProject(projectSFID, companySFID)
+ val := user.IsUserAuthorizedByProject(projectSFID, companySFID)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
+ }
+ return val
}
// IsUserAuthorizedForAnyProjectOrganization helper function for determining if the user is authorized for any of the specified projects with scope of project + organization
-func IsUserAuthorizedForAnyProjectOrganization(user *auth.User, projectSFIDs []string, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForAnyProjectOrganization(ctx context.Context, user *auth.User, projectSFIDs []string, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForAnyProjectOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFIDs": strings.Join(projectSFIDs, ","),
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
+ if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
+ return true
+ }
+
for _, projectSFID := range projectSFIDs {
- if IsUserAuthorizedForProjectOrganizationTree(user, projectSFID, companySFID, adminScopeAllowed) {
+ if IsUserAuthorizedForProjectOrganizationTree(ctx, user, projectSFID, companySFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
return true
}
- if IsUserAuthorizedForProjectOrganization(user, projectSFID, companySFID, adminScopeAllowed) {
+ if IsUserAuthorizedForProjectOrganization(ctx, user, projectSFID, companySFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
return true
}
}
+ log.WithFields(f).Debugf("user is not authorized for any projectSFID: %s + companySFID: %s", strings.Join(projectSFIDs, ","), companySFID)
return false
}
// IsUserAuthorizedForProjectOrganizationTree helper function for determining if the user is authorized for this project organization scope and nested projects/orgs
-func IsUserAuthorizedForProjectOrganizationTree(user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectOrganizationTree(ctx context.Context, user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectOrganizationTree",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorized(auth.ProjectOrganization, projectSFID+"|"+companySFID, true)
+ val := user.IsUserAuthorized(auth.ProjectOrganization, projectSFID+"|"+companySFID, true)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
+ }
+ return val
}
diff --git a/cla-backend-go/utils/utils_user_auth_standalone.go b/cla-backend-go/utils/utils_user_auth_standalone.go
index b29e6a413..8654d9d7e 100644
--- a/cla-backend-go/utils/utils_user_auth_standalone.go
+++ b/cla-backend-go/utils/utils_user_auth_standalone.go
@@ -46,17 +46,34 @@ func skipPermissionChecks() bool {
}
// IsUserAuthorizedForOrganization helper function for determining if the user is authorized for this company
-func IsUserAuthorizedForOrganization(user *auth.User, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForOrganization(ctx context.Context, user *auth.User, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- return user.IsUserAuthorizedForOrganizationScope(companySFID)
+ val := user.IsUserAuthorizedForOrganizationScope(companySFID)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for companySFID: %s", companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for companySFID: %s", companySFID)
+ }
+ return val
}
// IsUserAuthorizedForProjectTree helper function for determining if the user is authorized for this project hierarchy/tree
@@ -77,13 +94,17 @@ func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projec
}
if adminScopeAllowed && user.Admin {
- log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- log.WithFields(f).Debug("checking scope...")
+ log.WithFields(f).Debugf("checking project scope for projectSFID: %s...", projectSFID)
val := user.IsUserAuthorized(auth.Project, projectSFID, true)
- log.WithFields(f).Debugf("user allowed: %t", val)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s", projectSFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s", projectSFID)
+ }
return val
}
@@ -105,13 +126,17 @@ func IsUserAuthorizedForProject(ctx context.Context, user *auth.User, projectSFI
}
if adminScopeAllowed && user.Admin {
- log.WithFields(f).Debug("admin scope is allowed and admin scope set for user")
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- log.WithFields(f).Debug("checking scope...")
+ log.WithFields(f).Debugf("checking project scope for projectSFID: %s...", projectSFID)
val := user.IsUserAuthorizedForProjectScope(projectSFID)
- log.WithFields(f).Debugf("user allowed: %t", val)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s", projectSFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s", projectSFID)
+ }
return val
}
@@ -149,51 +174,102 @@ func IsUserAuthorizedForAnyProjects(ctx context.Context, user *auth.User, projec
}
// IsUserAuthorizedForProjectOrganization helper function for determining if the user is authorized for this project organization scope
-func IsUserAuthorizedForProjectOrganization(user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectOrganization(ctx context.Context, user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- // Previously, we checked for user.Allowed, which is currently not used (future flag that is currently not implemented)
- return user.IsUserAuthorizedByProject(projectSFID, companySFID)
+ val := user.IsUserAuthorizedByProject(projectSFID, companySFID)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
+ }
+ return val
}
// IsUserAuthorizedForAnyProjectOrganization helper function for determining if the user is authorized for any of the specified projects with scope of project + organization
-func IsUserAuthorizedForAnyProjectOrganization(user *auth.User, projectSFIDs []string, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForAnyProjectOrganization(ctx context.Context, user *auth.User, projectSFIDs []string, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForAnyProjectOrganization",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFIDs": strings.Join(projectSFIDs, ","),
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
+ return true
+ }
+
+ if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
for _, projectSFID := range projectSFIDs {
- if IsUserAuthorizedForProjectOrganizationTree(user, projectSFID, companySFID, adminScopeAllowed) {
+ if IsUserAuthorizedForProjectOrganizationTree(ctx, user, projectSFID, companySFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
return true
}
- if IsUserAuthorizedForProjectOrganization(user, projectSFID, companySFID, adminScopeAllowed) {
+ if IsUserAuthorizedForProjectOrganization(ctx, user, projectSFID, companySFID, adminScopeAllowed) {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s", projectSFID, companySFID)
return true
}
}
+ log.WithFields(f).Debugf("user is not authorized for any projectSFID: %s + companySFID: %s", strings.Join(projectSFIDs, ","), companySFID)
return false
}
// IsUserAuthorizedForProjectOrganizationTree helper function for determining if the user is authorized for this project organization scope and nested projects/orgs
-func IsUserAuthorizedForProjectOrganizationTree(user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+func IsUserAuthorizedForProjectOrganizationTree(ctx context.Context, user *auth.User, projectSFID, companySFID string, adminScopeAllowed bool) bool {
+ f := logrus.Fields{
+ "functionName": "utils.IsUserAuthorizedForProjectOrganizationTree",
+ XREQUESTID: ctx.Value(XREQUESTID),
+ "userName": user.UserName,
+ "userEmail": user.Email,
+ "projectSFID": projectSFID,
+ "companySFID": companySFID,
+ "adminScopeAllowed": adminScopeAllowed,
+ }
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
+ log.WithFields(f).Debug("skipping permissions check")
return true
}
if adminScopeAllowed && user.Admin {
+ log.WithFields(f).Debug("user is authorized - admin scope is allowed and admin scope set for user")
return true
}
- // Previously, we checked for user.Admin - admins should be in a separate role
- // Previously, we checked for user.Allowed, which is currently not used (future flag that is currently not implemented)
- return user.IsUserAuthorized(auth.ProjectOrganization, projectSFID+"|"+companySFID, true)
+ val := user.IsUserAuthorized(auth.ProjectOrganization, projectSFID+"|"+companySFID, true)
+ if val {
+ log.WithFields(f).Debugf("user is authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
+ } else {
+ log.WithFields(f).Debugf("user is not authorized for projectSFID: %s + companySFID: %s tree", projectSFID, companySFID)
+ }
+ return val
}
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 87db4a8f6..4f134468b 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -61,7 +61,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
}
log.WithFields(f).Debug("checking permissions...")
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to DeleteCLAManager with Project|Organization scope of %s | %s", authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return cla_manager.NewCreateCLAManagerForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -113,7 +113,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
}
log.WithFields(f).Debug("checking permissions...")
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, params.ProjectSFID, v1CompanyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to DeleteCLAManager with Project|Organization scope of %s | %s", authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -320,7 +320,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
}
// Check perms...
- if !utils.IsUserAuthorizedForOrganization(authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v1CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to CreateCLAManagerRequest with Project|Organization scope of %s | %s",
authUser.UserName, params.ProjectSFID, v1CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index fc1704d87..73c0790c5 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -61,7 +61,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to CompanyGetCompanyByInternalIDHandler with Organization scope of %s",
authUser.UserName, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -107,7 +107,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to CompanyGetCompanyByExternalIDHandler with Organization scope of %s",
authUser.UserName, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -141,7 +141,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectClaManagers with Project|Organization scope of %s | %s",
authUser.UserName, params.ProjectSFID, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -210,7 +210,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to GetCompanyProjectActiveCla with Project|Organization scope of %s | %s",
authUser.UserName, params.ProjectSFID, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -508,7 +508,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
// finally, we can check permissions for the delete operation
- if !utils.IsUserAuthorizedForOrganization(authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf(" user %s does not have access to company %s with Organization scope of %s",
authUser.UserName, companyModel.CompanyName, companyModel.CompanyExternalID)
log.Warn(msg)
@@ -565,7 +565,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
// finally, we can check permissions for the delete operation
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf(" user %s does not have access to company %s with Organization scope of %s",
authUser.UserName, companyModel.CompanyName, companyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -740,19 +740,19 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
}
log.WithFields(f).Debug("testing if user has access to project SFID and organization SFID...")
- if utils.IsUserAuthorizedForProjectOrganization(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganization(ctx, authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID and organization SFID...")
return true
}
log.WithFields(f).Debug("testing if user has access to project SFID and organization SFID tree...")
- if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to project SFID and organization SFID tree...")
return true
}
log.WithFields(f).Debug("testing if user has access to organization SFID...")
- if utils.IsUserAuthorizedForOrganization(authUser, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForOrganization(ctx, authUser, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to organization SFID...")
return true
}
@@ -785,13 +785,13 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
}
log.WithFields(f).Debug("testing if user has access to foundation SFID and organization SFID...")
- if utils.IsUserAuthorizedForProjectOrganization(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganization(ctx, authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to foundation SFID and organization SFID...")
return true
}
log.WithFields(f).Debug("testing if user has access to foundation SFID and organization SFID tree...")
- if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to foundation SFID and organization SFID tree...")
return true
}
@@ -807,7 +807,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
f["projectIDs"] = strings.Join(projectSFIDs, ",")
log.WithFields(f).Debug("testing if user has access to any cla group project + organization")
- if utils.IsUserAuthorizedForAnyProjectOrganization(authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForAnyProjectOrganization(ctx, authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debug("user has access to at least of of the projects...")
return true
}
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index 224b5c3f3..4feb29cf6 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -291,7 +291,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
return events.NewGetCompanyProjectEventsBadRequest().WithPayload(errorResponse(reqID, compErr))
}
- if !utils.IsUserAuthorizedForOrganization(authUser, v1Company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v1Company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
return events.NewGetCompanyProjectEventsForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to GetCompanyProject Events with Organization scope of %s",
diff --git a/cla-backend-go/v2/metrics/handlers.go b/cla-backend-go/v2/metrics/handlers.go
index 60106acc4..b69c9369a 100644
--- a/cla-backend-go/v2/metrics/handlers.go
+++ b/cla-backend-go/v2/metrics/handlers.go
@@ -111,7 +111,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
return metrics.NewListCompanyProjectMetricsBadRequest().WithPayload(errorResponse(reqID, compErr))
}
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForOrganization(authUser, company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, company.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
return metrics.NewListCompanyProjectMetricsForbidden().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to List Company Project Metrics with Organization scope of %s",
diff --git a/cla-backend-go/v2/sign/handlers.go b/cla-backend-go/v2/sign/handlers.go
index cbcb846e1..822a549d4 100644
--- a/cla-backend-go/v2/sign/handlers.go
+++ b/cla-backend-go/v2/sign/handlers.go
@@ -28,7 +28,7 @@ func Configure(api *operations.EasyclaAPI, service Service) {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
- if !utils.IsUserAuthorizedForProjectOrganizationTree(user, utils.StringValue(params.Input.ProjectSfid), utils.StringValue(params.Input.CompanySfid), utils.DISALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(ctx, user, utils.StringValue(params.Input.ProjectSfid), utils.StringValue(params.Input.CompanySfid), utils.DISALLOW_ADMIN_SCOPE) {
return sign.NewRequestCorporateSignatureForbidden().WithPayload(&models.ErrorResponse{
Code: "403",
Message: fmt.Sprintf("EasyCLA - 403 Forbidden - user %s does not have access to Request Corporate Signature with Project|Organization scope of %s | %s",
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 3364d6c09..6f6cce439 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -120,7 +120,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
}
// Must be in the Project|Organization Scope to see this - signature ACL is double-checked in the service level when the signature is loaded
- if !utils.IsUserAuthorizedForProjectOrganizationTree(authUser, params.ProjectSFID, companyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, params.ProjectSFID, companyModel.CompanyExternalID, utils.DISALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user '%s' does not have access to update Project Company Approval List with Project|Organization scope of %s | %s",
authUser.UserName, params.ProjectSFID, params.CompanyID)
log.WithFields(f).Warn(msg)
@@ -444,7 +444,6 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
"functionName": "v2.signatures.handlers.SignaturesGetProjectCompanySignaturesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -606,7 +605,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
})
}
- if !utils.IsUserAuthorizedForOrganization(authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, companyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("%s - user %s is not authorized to view company signatures with Organization scope: %s",
utils.EasyCLA403Forbidden, authUser.UserName, companyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
@@ -1346,13 +1345,13 @@ func isUserHaveAccessOfSignedSignaturePDF(ctx context.Context, authUser *auth.Us
}
// Check the project|org tree starting with the foundation
- if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, foundationID, comp.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, foundationID, comp.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
return true, nil
}
// In case the project organization tree didn't pass, let's check the project list individually - if any has access, we return true
for _, proj := range projects {
- if utils.IsUserAuthorizedForProjectOrganization(authUser, proj.ProjectSFID, comp.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganization(ctx, authUser, proj.ProjectSFID, comp.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user is authorized for %s scope for project ID: %s, org iD: %s", utils.ProjectOrgScope, proj.ProjectSFID, comp.CompanyExternalID)
return true, nil
}
@@ -1531,19 +1530,19 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
}
log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID and organization SFID...", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForProjectOrganization(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganization(ctx, authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user %s/%s has access to project SFID and organization SFID...", authUser.UserName, authUser.Email)
return true
}
log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID and organization SFID tree...", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, projectSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user %s/%s has access to project SFID and organization SFID tree...", authUser.UserName, authUser.Email)
return true
}
log.WithFields(f).Debugf("testing if user %s/%s has access to organization SFID...", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForOrganization(authUser, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForOrganization(ctx, authUser, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user %s/%s has access to organization SFID...", authUser.UserName, authUser.Email)
return true
}
@@ -1564,26 +1563,27 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Check the foundation permissions
f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
- log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation...", authUser.UserName, authUser.Email)
+ log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation SFID: %s...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID)
if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to parent foundation...", authUser.UserName, authUser.Email)
+ log.WithFields(f).Debugf("user %s/%s has access to parent foundation SFID: %s...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID)
return true
}
- log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation tree...", authUser.UserName, authUser.Email)
+
+ log.WithFields(f).Debugf("testing if user %s/%s has access to parent foundation SFID: %s tree...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID)
if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to parent foundation tree...", authUser.UserName, authUser.Email)
+ log.WithFields(f).Debugf("user %s/%s has access to parent foundation SFID: %s tree...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID)
return true
}
- log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID and organization SFID...", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForProjectOrganization(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to foundation SFID and organization SFID...", authUser.UserName, authUser.Email)
+ log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID %s and organization SFID %s ...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID, organizationSFID)
+ if utils.IsUserAuthorizedForProjectOrganization(ctx, authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user %s/%s has access to foundation SFID %s and organization SFID %s...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID, organizationSFID)
return true
}
- log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID and organization SFID tree...", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForProjectOrganizationTree(authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to foundation SFID and organization SFID tree...", authUser.UserName, authUser.Email)
+ log.WithFields(f).Debugf("testing if user %s/%s has access to foundation SFID %s and organization SFID %s tree...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID, organizationSFID)
+ if utils.IsUserAuthorizedForProjectOrganizationTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user %s/%s has access to foundation SFID %s and organization SFID %s tree...", authUser.UserName, authUser.Email, projectCLAGroupModel.FoundationSFID, organizationSFID)
return true
}
@@ -1595,11 +1595,20 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
return false
}
+ // Get the list of the project group and projects associated with this CLA Group
projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
- f["projectIDs"] = strings.Join(projectSFIDs, ",")
- log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group project + organization", authUser.UserName, authUser.Email)
- if utils.IsUserAuthorizedForAnyProjectOrganization(authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects...", authUser.UserName, authUser.Email)
+ projectSFIDsCSV := strings.Join(projectSFIDs, ",") // Create a project SFID CSV for printout
+ f["projectIDs"] = projectSFIDsCSV
+
+ log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group projects: %s", authUser.UserName, authUser.Email, projectSFIDsCSV)
+ if utils.IsUserAuthorizedForAnyProjectOrganization(ctx, authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects: %s...", authUser.UserName, authUser.Email, projectSFIDsCSV)
+ return true
+ }
+
+ log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group projects: %s + organization SFID: %s", authUser.UserName, authUser.Email, projectSFIDsCSV, organizationSFID)
+ if utils.IsUserAuthorizedForAnyProjectOrganization(ctx, authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects: %s + organization SFID: %s...", authUser.UserName, authUser.Email, projectSFIDsCSV, organizationSFID)
return true
}
From eeba6a7304b1fc2c89944bd02a7ee56746f36051 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 22 Apr 2021 11:46:26 -0700
Subject: [PATCH 0239/1276] Resolved Issue with Permission Check for Project
List (#2894)
- Updated check for IsUserAuthorizedForAnyProjects when querying for signatures
Signed-off-by: David Deal
---
cla-backend-go/v2/signatures/handlers.go | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 6f6cce439..e07f27f39 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -1458,12 +1458,14 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
"userEmail": authUser.Email,
}
- log.WithFields(f).Debug("testing if user has access to project SFID")
+ log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ log.WithFields(f).Debugf("user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
return true
}
+ log.WithFields(f).Debugf("user %s/%s doesn't have direct access to the project SFID: %s - loading CLA Group from project id...", authUser.UserName, authUser.Email, projectSFID)
- log.WithFields(f).Debug("user doesn't have direct access to the projectSFID - loading CLA Group from project id...")
+ log.WithFields(f).Debug("loading CLA Group from project id...")
projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
@@ -1495,10 +1497,12 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
}
projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
- f["projectIDs"] = strings.Join(projectSFIDs, ",")
- log.WithFields(f).Debug("testing if user has access to any projects")
+ projectSFIDsCSV := strings.Join(projectSFIDs, ",") // Create a project SFID CSV for printout
+ f["projectIDs"] = projectSFIDsCSV
+
+ log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group projects: %s", authUser.UserName, authUser.Email, projectSFIDsCSV)
if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to at least of of the projects...")
+ log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects: %s...", authUser.UserName, authUser.Email, projectSFIDsCSV)
return true
}
@@ -1601,7 +1605,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
f["projectIDs"] = projectSFIDsCSV
log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group projects: %s", authUser.UserName, authUser.Email, projectSFIDsCSV)
- if utils.IsUserAuthorizedForAnyProjectOrganization(ctx, authUser, projectSFIDs, organizationSFID, utils.ALLOW_ADMIN_SCOPE) {
+ if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects: %s...", authUser.UserName, authUser.Email, projectSFIDsCSV)
return true
}
From 66d15f0807726eb5a254fd43b6ba70327b4d1892 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 22 Apr 2021 15:03:40 -0700
Subject: [PATCH 0240/1276] Updated Assign Role Scope Event Log Text (#2895)
- Added user name/email to struct, set in call, printout in event log
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 45 ++++++++++++++-----
cla-backend-go/events/mock.go | 3 +-
.../github/branch_protection/mock.go | 3 +-
cla-backend-go/github_organizations/mock.go | 3 +-
.../repositories/mock/mock_repository.go | 3 +-
.../repositories/mock/mock_service.go | 3 +-
cla-backend-go/v2/cla_manager/service.go | 8 ++--
cla-backend-go/v2/company/service.go | 9 ++--
cla-backend-go/v2/github_activity/service.go | 5 ++-
.../v2/github_activity/service_test.go | 11 ++---
10 files changed, 62 insertions(+), 31 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index e06b6ffca..d02bc56f0 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -371,12 +371,17 @@ type ContributorAssignCLADesignee struct {
}
// UserConvertToContactData . . .
-type UserConvertToContactData struct{}
+type UserConvertToContactData struct {
+ UserName string
+ UserEmail string
+}
// AssignRoleScopeData . . .
type AssignRoleScopeData struct {
- Role string
- Scope string
+ Role string
+ Scope string
+ UserName string
+ UserEmail string
}
// ClaManagerRoleCreatedData . . .
@@ -1024,9 +1029,23 @@ func (ed *UserConvertToContactData) GetEventDetailsString(args *LogEventArgs) (s
// GetEventDetailsString . . .
func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s was assigned Scope: %s with Role: %s for Project: %s with ID: %s.",
- args.UserName,
- ed.Scope, ed.Role, args.ProjectName, args.ProjectSFID)
+ data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -1995,7 +2014,7 @@ func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs
// GetEventSummaryString . . .
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was converted to a contact", args.UserName)
+ data := fmt.Sprintf("The user '%s' with email '%s' was converted to a contact", ed.UserName, ed.UserEmail)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -2005,13 +2024,16 @@ func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (s
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s", args.UserName, ed.Role)
+ data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -2021,13 +2043,16 @@ func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was added to the role %s", ed.UserName, ed.Role)
+ data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -2046,7 +2071,7 @@ func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (
// GetEventSummaryString . . .
func (ed *ClaManagerRoleDeletedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The user %s was removed from the role %s", ed.UserName, ed.Role)
+ data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
diff --git a/cla-backend-go/events/mock.go b/cla-backend-go/events/mock.go
index 93c0baa68..91494c65c 100644
--- a/cla-backend-go/events/mock.go
+++ b/cla-backend-go/events/mock.go
@@ -10,10 +10,11 @@ package events
import (
context "context"
+ reflect "reflect"
+
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
events0 "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
gomock "github.com/golang/mock/gomock"
- reflect "reflect"
)
// MockService is a mock of Service interface
diff --git a/cla-backend-go/github/branch_protection/mock.go b/cla-backend-go/github/branch_protection/mock.go
index ca127ac4e..456925c0e 100644
--- a/cla-backend-go/github/branch_protection/mock.go
+++ b/cla-backend-go/github/branch_protection/mock.go
@@ -10,10 +10,11 @@ package branch_protection
import (
context "context"
+ reflect "reflect"
+
gomock "github.com/golang/mock/gomock"
github "github.com/google/go-github/v33/github"
githubv4 "github.com/shurcooL/githubv4"
- reflect "reflect"
)
// MockCombinedRepository is a mock of CombinedRepository interface
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index 8eb3841ba..48f1c20bc 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -10,9 +10,10 @@ package github_organizations
import (
context "context"
+ reflect "reflect"
+
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
- reflect "reflect"
)
// MockRepository is a mock of Repository interface
diff --git a/cla-backend-go/repositories/mock/mock_repository.go b/cla-backend-go/repositories/mock/mock_repository.go
index 568756836..9791f117e 100644
--- a/cla-backend-go/repositories/mock/mock_repository.go
+++ b/cla-backend-go/repositories/mock/mock_repository.go
@@ -10,9 +10,10 @@ package mock
import (
context "context"
+ reflect "reflect"
+
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
- reflect "reflect"
)
// MockRepository is a mock of Repository interface
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index 339b28dff..e5c1a9ce0 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -10,9 +10,10 @@ package mock
import (
context "context"
+ reflect "reflect"
+
models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
gomock "github.com/golang/mock/gomock"
- reflect "reflect"
)
// MockService is a mock of Service interface
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index e9765a1ef..c154d1811 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -402,14 +402,14 @@ func (s *service) CreateCLAManagerDesignee(ctx context.Context, companyID string
s.eventService.LogEventWithContext(ctx,
&events.LogEventArgs{
EventType: events.AssignUserRoleScopeType,
- LfUsername: lfxUser.Username,
ProjectSFID: projectSFID,
CompanyModel: v1CompanyModel,
CompanyID: v1CompanyModel.CompanyID,
- UserModel: &v1Models.User{LfUsername: lfxUser.Username, UserID: lfxUser.ID},
EventData: &events.AssignRoleScopeData{
- Role: "cla-manager-designee",
- Scope: fmt.Sprintf("%s|%s", projectSFID, v1CompanyModel.CompanyExternalID),
+ Role: "cla-manager-designee",
+ Scope: fmt.Sprintf("%s|%s", projectSFID, v1CompanyModel.CompanyExternalID),
+ UserName: lfxUser.Username,
+ UserEmail: utils.StringValue(lfxUser.Email),
},
})
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 6872628fa..b889276ad 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -744,17 +744,16 @@ func (s *service) CreateContributor(ctx context.Context, companyID string, proje
s.eventService.LogEventWithContext(ctx,
&events.LogEventArgs{
EventType: events.AssignUserRoleScopeType,
- LfUsername: user.Username,
- UserID: user.ID,
ProjectSFID: projectID,
CompanyModel: v1CompanyModel,
ClaGroupModel: projectModel,
CLAGroupID: projectModel.ProjectID,
CLAGroupName: projectModel.ProjectName,
- UserModel: &v1Models.User{LfUsername: user.Username, UserID: user.ID},
EventData: &events.AssignRoleScopeData{
- Role: "contributor",
- Scope: fmt.Sprintf("%s|%s", projectID, companyID),
+ Role: "contributor",
+ Scope: fmt.Sprintf("%s|%s", projectID, companyID),
+ UserName: user.Username,
+ UserEmail: utils.StringValue(user.Email),
},
})
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index af29b3f98..b5b9b5afe 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -7,10 +7,11 @@ import (
"context"
"errors"
"fmt"
+ "strconv"
+
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/emails"
v1GithubOrg "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
- "strconv"
"github.com/sirupsen/logrus"
@@ -344,7 +345,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
},
})
- if s.sendEmail{
+ if s.sendEmail {
if err := s.notifyForGithubRepositoryTransferred(ctx, repoModel, oldGithubOrg, newGithubOrg, false); err != nil {
log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
}
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index 16f09806e..12514dd01 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -5,6 +5,8 @@ package github_activity
import (
"fmt"
+ "testing"
+
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
@@ -13,7 +15,6 @@ import (
"github.com/golang/mock/gomock"
"github.com/google/go-github/v33/github"
"github.com/stretchr/testify/assert"
- "testing"
)
func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryRenamedAction(t *testing.T) {
@@ -146,7 +147,7 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
NewGithubOrgName: newOrgName,
},
}).Return()
- }else{
+ } else {
githubRepo.EXPECT().
DisableRepository(gomock.Any(), repoID).Return(nil)
eventsService.EXPECT().
@@ -155,7 +156,7 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
UserID: "githubLoginValue",
ProjectID: "",
EventData: &events.RepositoryDisabledEventData{
- RepositoryName: repoName,
+ RepositoryName: repoName,
},
}).Return()
}
@@ -177,9 +178,9 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
Installation: nil,
})
- if tc.newGithubOrg.AutoEnabled{
+ if tc.newGithubOrg.AutoEnabled {
assert.NoError(tt, err)
- }else{
+ } else {
assert.Error(tt, err)
}
})
From 57b4c55916b2a9f61037eaec2c8115f4c9b41b23 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 23 Apr 2021 01:36:46 +0300
Subject: [PATCH 0241/1276] [#2882]Bug/Approval List Update (#2896)
- Resolved issue raised in ONAP use case for empty lists
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/repository.go | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index ba294813b..67c54ad5a 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2042,7 +2042,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
var gerritICLAECLAs []string
// Only load the gerrit user information, which is costly, if we have updates to remove email or email domains
- if params.RemoveEmailApprovalList != nil || params.RemoveDomainApprovalList != nil {
+ if (params.RemoveEmailApprovalList != nil && len(params.RemoveEmailApprovalList) > 0) || (params.RemoveDomainApprovalList != nil && len(params.RemoveDomainApprovalList) > 0) {
goRoutines := 2
gerritResultChannel := make(chan *GerritUserResponse, goRoutines)
@@ -2067,7 +2067,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
// If we have an add or remove email list...we need to run an update for this column
- if params.AddEmailApprovalList != nil || params.RemoveEmailApprovalList != nil {
+ if (params.AddEmailApprovalList != nil && len(params.AddEmailApprovalList) > 0) || (params.RemoveEmailApprovalList != nil && len(params.RemoveEmailApprovalList) > 0) {
columnName := "email_whitelist"
attrList := buildApprovalAttributeList(ctx, cclaSignature.EmailApprovalList, params.AddEmailApprovalList, params.RemoveEmailApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
@@ -2190,7 +2190,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
}
- if params.AddDomainApprovalList != nil || params.RemoveDomainApprovalList != nil {
+ if (params.AddDomainApprovalList != nil && len(params.AddDomainApprovalList) > 0) || (params.RemoveDomainApprovalList != nil && len(params.RemoveDomainApprovalList) > 0) {
columnName := "domain_whitelist"
attrList := buildApprovalAttributeList(ctx, cclaSignature.DomainApprovalList, params.AddDomainApprovalList, params.RemoveDomainApprovalList)
@@ -2249,7 +2249,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
}
- if params.AddGithubUsernameApprovalList != nil || params.RemoveGithubUsernameApprovalList != nil {
+ if (params.AddGithubUsernameApprovalList != nil && len(params.AddGithubUsernameApprovalList) > 0) || (params.RemoveGithubUsernameApprovalList != nil && len(params.RemoveGithubUsernameApprovalList) > 0) {
columnName := "github_whitelist"
attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubUsernameApprovalList, params.AddGithubUsernameApprovalList, params.RemoveGithubUsernameApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
@@ -2332,7 +2332,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
}
- if params.AddGithubOrgApprovalList != nil || params.RemoveGithubOrgApprovalList != nil {
+ if (params.AddGithubOrgApprovalList != nil && len(params.AddGithubOrgApprovalList) > 0) || (params.RemoveGithubOrgApprovalList != nil && len(params.RemoveGithubOrgApprovalList) > 0) {
columnName := "github_org_whitelist"
attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubOrgApprovalList, params.AddGithubOrgApprovalList, params.RemoveGithubOrgApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
From 5f2e27055b2185daa663b5167e7f815955c58d61 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Tue, 27 Apr 2021 02:21:51 +0300
Subject: [PATCH 0242/1276] [#2897] Feature/API Invalidation of ICLA (#2898)
- Added API that handles invalidation of ICLA and email notification
Signed-off-by: Harold Wanyama
---
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/signatures/email.go | 1 +
cla-backend-go/swagger/cla.v2.yaml | 29 +++++++++
cla-backend-go/utils/cla_user.go | 17 ++++++
cla-backend-go/v2/signatures/handlers.go | 28 +++++++++
cla-backend-go/v2/signatures/service.go | 75 +++++++++++++++++++++++-
6 files changed, 150 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 97ae9b630..cbd9d8f58 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -281,7 +281,7 @@ func server(localMode bool) http.Handler {
v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, v1CLAGroupRepo, usersRepo, v1CompanyRepo, projectClaGroupRepo, eventsService)
v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, projectClaGroupRepo, v1CompanyService)
v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
- v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, projectClaGroupRepo)
+ v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, projectClaGroupRepo, signaturesRepo, usersService)
v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, projectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
diff --git a/cla-backend-go/signatures/email.go b/cla-backend-go/signatures/email.go
index 9a5b62eeb..b7a4c54fb 100644
--- a/cla-backend-go/signatures/email.go
+++ b/cla-backend-go/signatures/email.go
@@ -16,6 +16,7 @@ type InvalidateSignatureTemplateParams struct {
ClaManager string
RemovalCriteria string
ProjectName string
+ ProjectManager string
CLAManagers []ClaManagerInfoParams
CLaManager string
CLAGroupName string
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 8b8e30fe4..30b40dc8f 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3103,6 +3103,35 @@ paths:
$ref: '#/responses/internal-server-error'
tags:
- gerrits
+ /cla-group/{claGroupID}/user/{userID}/icla:
+ put:
+ summary: Invalidate ICLA record
+ description: Invalidates a given ICLA record for a user
+ operationId: invalidateICLA
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-email"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/path-claGroupID"
+ - $ref: "#/parameters/path-userID"
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - signatures
/company/{companyID}:
get:
diff --git a/cla-backend-go/utils/cla_user.go b/cla-backend-go/utils/cla_user.go
index 8fc062b19..5470d1a90 100644
--- a/cla-backend-go/utils/cla_user.go
+++ b/cla-backend-go/utils/cla_user.go
@@ -4,6 +4,8 @@
package utils
import (
+ "strings"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
)
@@ -23,3 +25,18 @@ func GetBestUsername(user *models.User) string {
return "User Name Unknown"
}
+
+// GetBestEmail is a helper function to return the best email address for the user model
+func GetBestEmail(userModel *models.User) string {
+ if userModel.LfEmail != "" {
+ return userModel.LfEmail
+ }
+
+ for _, email := range userModel.Emails {
+ if email != "" && !strings.Contains(email, "noreply.github.com") {
+ return email
+ }
+ }
+
+ return ""
+}
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index e07f27f39..e9acbac6a 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -1255,6 +1255,34 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
}
})
})
+ api.SignaturesInvalidateICLAHandler = signatures.InvalidateICLAHandlerFunc(func(params signatures.InvalidateICLAParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ f := logrus.Fields{
+ "functionName": "v2.signatures.handlers.SignaturesInvalidateICLAHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": params.ClaGroupID,
+ "userID": params.UserID,
+ }
+ log.WithFields(f).Debug("Invalidating ICLA record...")
+ eventArgs := &events.LogEventArgs{
+ EventType: events.InvalidatedSignature,
+ EventData: &events.SignatureProjectInvalidatedEventData{
+ InvalidatedCount: 1,
+ },
+ }
+ err := v2service.InvalidateICLA(ctx, params.ClaGroupID, params.UserID, authUser, eventsService, eventArgs)
+ if err != nil {
+ msg := "unable to invalidate icla"
+ log.WithFields(f).Warn(msg)
+ // return signatures.NewInvalidateSignatureBadRequest().WithXRequestID(reqID).WithPayload(
+ // utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ return signatures.NewInvalidateICLABadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+ return signatures.NewInvalidateICLAOK().WithXRequestID(reqID)
+ })
}
// getProjectIDsFromModels is a helper function to extract the project SFIDs from the project CLA Group models
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index a60dd4039..252e9abd6 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -9,12 +9,14 @@ import (
"errors"
"fmt"
+ "github.com/LF-Engineering/lfx-kit/auth"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/aws"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/jinzhu/copier"
@@ -25,6 +27,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/project"
"github.com/communitybridge/easycla/cla-backend-go/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
)
@@ -46,6 +49,8 @@ type service struct {
v1ProjectService project.Service
v1CompanyService company.IService
v1SignatureService signatures.SignatureService
+ v1SignatureRepo signatures.SignatureRepository
+ usersService users.Service
projectsClaGroupsRepo projects_cla_groups.Repository
s3 *s3.S3
signaturesBucket string
@@ -62,17 +67,20 @@ type Service interface {
GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error)
GetSignedIclaZipPdf(claGroupID string) (*models.URLObject, error)
GetSignedCclaZipPdf(claGroupID string) (*models.URLObject, error)
+ InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error
}
// NewService creates instance of v2 signature service
func NewService(awsSession *session.Session, signaturesBucketName string, v1ProjectService project.Service,
v1CompanyService company.IService,
v1SignatureService signatures.SignatureService,
- pcgRepo projects_cla_groups.Repository) *service {
+ pcgRepo projects_cla_groups.Repository, v1SignatureRepo signatures.SignatureRepository, usersService users.Service) *service {
return &service{
v1ProjectService: v1ProjectService,
v1CompanyService: v1CompanyService,
v1SignatureService: v1SignatureService,
+ v1SignatureRepo: v1SignatureRepo,
+ usersService: usersService,
projectsClaGroupsRepo: pcgRepo,
s3: s3.New(awsSession),
signaturesBucket: signaturesBucketName,
@@ -292,3 +300,68 @@ func (s service) GetClaGroupCorporateContributors(ctx context.Context, claGroupI
return &resp, nil
}
+
+func (s service) InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error {
+ f := logrus.Fields{
+ "functionName": "v2.signatures.service.InvalidateICLA",
+ "claGroupID": claGroupID,
+ "userID": userID,
+ }
+ // Get signature record
+ log.WithFields(f).Debug("getting signature record ...")
+ icla, iclaErr := s.v1SignatureService.GetIndividualSignature(ctx, claGroupID, userID)
+ if iclaErr != nil {
+ log.WithFields(f).Debug("unable to get individual signature")
+ return iclaErr
+ }
+
+ // Get cla Group
+ log.WithFields(f).Debug("getting clGroup...")
+ claGroup, claGrpErr := s.v1ProjectService.GetCLAGroupByID(ctx, claGroupID)
+ if claGrpErr != nil {
+ log.WithFields(f).Debug("unable to fetch cla Group record")
+ return claGrpErr
+ }
+
+ //Get user record
+ user, userErr := s.usersService.GetUser(userID)
+ if userErr != nil {
+ log.WithFields(f).Debug("unable to get user record")
+ return userErr
+ }
+
+ log.WithFields(f).Debug("invalidating signature record ...")
+ note := fmt.Sprintf("Signature invalidated (approved set to false) by %s for %s ", authUser.UserName, utils.GetBestUsername(user))
+ err := s.v1SignatureRepo.InvalidateProjectRecord(ctx, icla.SignatureID, note)
+ if err != nil {
+ log.WithFields(f).Debug("unable to invalidate icla record")
+ return err
+ }
+ // send email
+ email := utils.GetBestEmail(user)
+ log.WithFields(f).Debugf("sending invalidation email to : %s ", email)
+ subject := fmt.Sprintf("EasyCLA: ICLA invalidated for %s ", claGroup.ProjectName)
+ params := signatures.InvalidateSignatureTemplateParams{
+ RecipientName: utils.GetBestUsername(user),
+ ProjectManager: authUser.UserName,
+ CLAGroupName: claGroup.ProjectName,
+ }
+ body, renderErr := utils.RenderTemplate(claGroup.Version, signatures.InvalidateICLASignatureTemplateName, signatures.InvalidateICLASignatureTemplate, params)
+ if renderErr != nil {
+ log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
+ } else {
+ err := utils.SendEmail(subject, body, []string{email})
+ if err != nil {
+ log.WithFields(f).Debugf("unable to send approval list update email to : %s ", email)
+ }
+ }
+
+ eventArgs.UserName = utils.GetBestUsername(user)
+ eventArgs.UserModel = user
+ eventArgs.ProjectName = claGroup.ProjectName
+
+ // Log event
+ eventsService.LogEventWithContext(ctx, eventArgs)
+
+ return nil
+}
From 417f789e0c38090f611a24d286b085051f9e865e Mon Sep 17 00:00:00 2001
From: makkalot
Date: Wed, 28 Apr 2021 13:53:06 +0300
Subject: [PATCH 0243/1276] github action rename fix, resolve the note conflict
in repository update
Signed-off-by: makkalot
---
cla-backend-go/repositories/repository.go | 6 +-
cla-backend-go/v2/github_activity/service.go | 63 +++++++++++++-------
2 files changed, 46 insertions(+), 23 deletions(-)
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index beaeeb1b6..a769d1c1c 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -234,9 +234,9 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
noteValue = fmt.Sprintf("%s. %s", repoModel.Note, noteValue)
}
}
- expressionAttributeNames["#N"] = aws.String("note")
- expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(noteValue)}
- updateExpression = updateExpression + " #N = :n, "
+ expressionAttributeNames["#NO"] = aws.String("note")
+ expressionAttributeValues[":no"] = &dynamodb.AttributeValue{S: aws.String(noteValue)}
+ updateExpression = updateExpression + " #NO = :no, "
}
if input.Enabled != nil && repoModel.Enabled != *input.Enabled {
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index b5b9b5afe..3841f005b 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -84,7 +84,7 @@ func (s *eventHandlerService) ProcessRepositoryEvent(event *github.RepositoryEve
return fmt.Errorf("missing repository object in event payload")
}
- log.Debugf("ProcessRepositoryEvent called for action : %s", *event.Action)
+ log.Debugf("ProcessRepositoryEvent called for action : %s for repository : %s", *event.Action, *event.Repo.Name)
switch *event.Action {
case "created":
return s.handleRepositoryAddedAction(ctx, event.Sender, event.Repo)
@@ -176,6 +176,8 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
}
+ log.WithFields(f).Infof("disabling repo : %s", repoModel.RepositoryID)
+
if err := s.githubRepo.DisableRepository(context.Background(), repoModel.RepositoryID); err != nil {
log.WithFields(f).Warnf("disabling repo : %s failed : %v", *repo.FullName, err)
return err
@@ -237,6 +239,8 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
}
+ log.WithFields(f).Infof("renaming Github Repository from : %s to : %s", repoModel.RepositoryName, *repo.Name)
+
if _, err := s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
RepositoryName: repo.Name,
Note: "repository was renamed externally",
@@ -298,6 +302,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
if repo.ID == nil || *repo.ID == 0 {
return fmt.Errorf("missing repo id")
}
+
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
@@ -310,6 +315,9 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
newOrganizationName := *org.Name
oldOrganizationName := repoModel.RepositoryOrganizationName
+
+ log.WithFields(f).Infof("running transfer for repository : %s from Github Org : %s to Github Org : %s", *repo.Name, oldOrganizationName, newOrganizationName)
+
// first check if it's a different organization name (could be a duplicate event)
if oldOrganizationName == *org.Name {
msg := fmt.Sprintf("nothing to change for github repo : %s, probably duplicate event was sent", repoModel.RepositoryName)
@@ -325,30 +333,19 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
newGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, *org.Name)
if err != nil {
+ disabledErr := s.disableFailedTransferRepo(ctx, sender, f, repoModel, oldGithubOrg, newGithubOrg)
+ if disabledErr != nil {
+ return disabledErr
+ }
+
return fmt.Errorf("fetching the new organization name : %s failed : %v", newOrganizationName, err)
}
// we need to check if the new org name has autoenabled and have a cla group set otherwise we can't proceed
if !newGithubOrg.AutoEnabled || newGithubOrg.AutoEnabledClaGroupID == "" {
- log.WithFields(f).Warnf("can't proceed with repo transfer operation because the new org doesn't have autoenabled=true, disabling the repo : %s", repoModel.RepositoryName)
- if err := s.githubRepo.DisableRepository(ctx, repoModel.RepositoryID); err != nil {
- return fmt.Errorf("disabling the repo : %s failed : %v", repoModel.RepositoryID, err)
- }
-
- // send event for the disabled repository.
- s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryDisabled,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
- EventData: &events.RepositoryDisabledEventData{
- RepositoryName: repoModel.RepositoryName,
- },
- })
-
- if s.sendEmail {
- if err := s.notifyForGithubRepositoryTransferred(ctx, repoModel, oldGithubOrg, newGithubOrg, false); err != nil {
- log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
- }
+ disabledErr := s.disableFailedTransferRepo(ctx, sender, f, repoModel, oldGithubOrg, newGithubOrg)
+ if disabledErr != nil {
+ return disabledErr
}
return fmt.Errorf("aborting the repository : %s transfer, new githubOrg : %s doesn't have claGroupID set", repoModel.RepositoryName, newGithubOrg.OrganizationName)
@@ -385,6 +382,30 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
return nil
}
+func (s *eventHandlerService) disableFailedTransferRepo(ctx context.Context, sender *github.User, f logrus.Fields, repoModel *models.GithubRepository, oldGithubOrg *models.GithubOrganization, newGithubOrg *models.GithubOrganization) error {
+ log.WithFields(f).Warnf("can't proceed with repo transfer operation because the new org doesn't have autoenabled=true, disabling the repo : %s", repoModel.RepositoryName)
+ if err := s.githubRepo.DisableRepository(ctx, repoModel.RepositoryID); err != nil {
+ return fmt.Errorf("disabling the repo : %s failed : %v", repoModel.RepositoryID, err)
+ }
+
+ // send event for the disabled repository.
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryDisabled,
+ ProjectID: repoModel.RepositoryProjectID,
+ UserID: *sender.Login,
+ EventData: &events.RepositoryDisabledEventData{
+ RepositoryName: repoModel.RepositoryName,
+ },
+ })
+
+ if s.sendEmail {
+ if err := s.notifyForGithubRepositoryTransferred(ctx, repoModel, oldGithubOrg, newGithubOrg, false); err != nil {
+ log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
+ }
+ }
+ return nil
+}
+
func (s *eventHandlerService) notifyForGithubRepositoryTransferred(ctx context.Context, repoModel *models.GithubRepository, oldGithubOrg *models.GithubOrganization, newGithubOrg *models.GithubOrganization, success bool) error {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Transferred")
body, err := emails.RenderGithubRepositoryTransferredTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryTransferredTemplateParams{
@@ -425,6 +446,8 @@ func (s *eventHandlerService) handleRepositoryArchivedAction(ctx context.Context
return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
}
+ log.WithFields(f).Infof("archiving repository : %s", repoModel.RepositoryName)
+
if s.sendEmail {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Archived")
body, err := emails.RenderGithubRepositoryArchivedTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryArchivedTemplateParams{
From 560ac38dca90b0ba50bd0d18d16e1db36df6738c Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 28 Apr 2021 19:11:56 +0300
Subject: [PATCH 0244/1276] some defensive code against the data coming from
github about transferred action (#2901)
transferred action
Signed-off-by: makkalot
---
cla-backend-go/v2/github_activity/service.go | 28 +++++++++++++------
.../v2/github_activity/service_test.go | 4 +--
2 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index 3841f005b..19b12d989 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -292,10 +292,20 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
}
func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Context, sender *github.User, repo *github.Repository, org *github.Organization) error {
+
+ if repo.Name == nil {
+ return fmt.Errorf("missing repo name can't proceed with transfer")
+ }
+ repoName := *repo.Name
+
+ if org.Login == nil {
+ return fmt.Errorf("missing organization login information can't proceed with transferring the rpo : %s", *org.Name)
+ }
+
f := logrus.Fields{
"functionName": "v2.github_activity.service.handleRepositoryTransferredAction",
- "repositoryName": *repo.Name,
- "newGithubOrganization": *org.Name,
+ "repositoryName": repoName,
+ "newGithubOrganization": *org.Login,
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
@@ -307,19 +317,19 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
- log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
+ log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", repoName)
return nil
}
- return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", *repo.FullName, repositoryExternalID, err)
+ return fmt.Errorf("fetching the repo : %s by external id : %s failed : %v", repoName, repositoryExternalID, err)
}
- newOrganizationName := *org.Name
+ newOrganizationName := *org.Login
oldOrganizationName := repoModel.RepositoryOrganizationName
- log.WithFields(f).Infof("running transfer for repository : %s from Github Org : %s to Github Org : %s", *repo.Name, oldOrganizationName, newOrganizationName)
+ log.WithFields(f).Infof("running transfer for repository : %s from Github Org : %s to Github Org : %s", repoName, oldOrganizationName, newOrganizationName)
// first check if it's a different organization name (could be a duplicate event)
- if oldOrganizationName == *org.Name {
+ if oldOrganizationName == newOrganizationName {
msg := fmt.Sprintf("nothing to change for github repo : %s, probably duplicate event was sent", repoModel.RepositoryName)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
@@ -331,7 +341,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
return fmt.Errorf("fetching the old organization name : %s failed : %v", oldOrganizationName, err)
}
- newGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, *org.Name)
+ newGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, newOrganizationName)
if err != nil {
disabledErr := s.disableFailedTransferRepo(ctx, sender, f, repoModel, oldGithubOrg, newGithubOrg)
if disabledErr != nil {
@@ -354,7 +364,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
_, err = s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
Note: fmt.Sprintf("repository was transferred from org : %s to : %s", oldGithubOrg.OrganizationName, newGithubOrg.OrganizationName),
RepositoryOrganizationName: aws.String(newGithubOrg.OrganizationName),
- RepositoryURL: repo.URL,
+ RepositoryURL: repo.HTMLURL,
})
if err != nil {
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index 12514dd01..8f7e5767c 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -167,10 +167,10 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
Repo: &github.Repository{
ID: aws.Int64(1),
Name: &repoName,
- URL: &newRepoUrl,
+ HTMLURL: &newRepoUrl,
},
Org: &github.Organization{
- Name: &newOrgName,
+ Login: &newOrgName,
},
Sender: &github.User{
Login: aws.String("githubLoginValue"),
From 83f2720a16b6c814117c6c807dc70e6b07250c65 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 28 Apr 2021 14:38:48 -0700
Subject: [PATCH 0245/1276] [#2886] Updated Event Log Details for Approval List
(#2902)
- updated event log, data structures, etc.
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 200 +++++++++++++-----
cla-backend-go/signatures/service.go | 24 ---
.../v2/github_activity/service_test.go | 6 +-
3 files changed, 147 insertions(+), 83 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index d02bc56f0..fced9444f 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -255,65 +255,41 @@ type CLAManagerRequestDeletedEventData struct {
// CLAApprovalListAddEmailData . . .
type CLAApprovalListAddEmailData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListEmail string
}
// CLAApprovalListRemoveEmailData . . .
type CLAApprovalListRemoveEmailData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListEmail string
}
// CLAApprovalListAddDomainData . . .
type CLAApprovalListAddDomainData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListDomain string
}
// CLAApprovalListRemoveDomainData . . .
type CLAApprovalListRemoveDomainData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListDomain string
}
// CLAApprovalListAddGitHubUsernameData . . .
type CLAApprovalListAddGitHubUsernameData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListGitHubUsername string
}
// CLAApprovalListRemoveGitHubUsernameData . . .
type CLAApprovalListRemoveGitHubUsernameData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListGitHubUsername string
}
// CLAApprovalListAddGitHubOrgData . . .
type CLAApprovalListAddGitHubOrgData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListGitHubOrg string
}
// CLAApprovalListRemoveGitHubOrgData . . .
type CLAApprovalListRemoveGitHubOrgData struct {
- UserName string
- UserEmail string
- UserLFID string
ApprovalListGitHubOrg string
}
@@ -724,10 +700,21 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventDetailsString(args *LogEven
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Email: %s to the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The email address %s was added to the approval list", ed.ApprovalListEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -735,10 +722,21 @@ func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs)
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Email: %s from the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListEmail, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The email address %s was removed from the approval list", ed.ApprovalListEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -746,10 +744,21 @@ func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventAr
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added Domain: %s to the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The email address domain %s was added to the approval list", ed.ApprovalListDomain)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -757,10 +766,21 @@ func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed Domain %s from the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListDomain, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The email address domain %s was removed from the approval list", ed.ApprovalListDomain)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -768,10 +788,21 @@ func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventA
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Username: %s to the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub username %s was added to the approval list", ed.ApprovalListGitHubUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -779,10 +810,21 @@ func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogE
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Username: %s from the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubUsername, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub username %s was removed from the approval list", ed.ApprovalListGitHubUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -790,10 +832,21 @@ func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *L
// GetEventDetailsString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s added GitHub Organization: %s to the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub organization %s was added to the approval list", ed.ApprovalListGitHubOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -801,10 +854,21 @@ func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventA
// GetEventDetailsString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Manager: %s, Email: %s, LFID: %s removed GitHub Organization: %s from the approval list for Company: %s, Project: %s",
- ed.UserName, ed.UserEmail, ed.UserLFID, ed.ApprovalListGitHubOrg, args.CompanyName, args.ProjectName)
+ data := fmt.Sprintf("The GitHub organization %s was removed from the approval list", ed.ApprovalListGitHubOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
}
data = data + "."
return data, true
@@ -1568,7 +1632,7 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEven
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the email %s to the approval list", args.UserName, ed.ApprovalListEmail)
+ data := fmt.Sprintf("The email address %s was added to the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1578,13 +1642,16 @@ func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs)
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the email %s from the approval list", args.UserName, ed.ApprovalListEmail)
+ data := fmt.Sprintf("The email address %s was removed from the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1594,13 +1661,16 @@ func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventAr
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the domain %s to the approval list", args.UserName, ed.ApprovalListDomain)
+ data := fmt.Sprintf("The email address domain %s was added to the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1610,13 +1680,16 @@ func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the domain %s from the approval list", args.UserName, ed.ApprovalListDomain)
+ data := fmt.Sprintf("The email address domain %s was removed from the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1626,13 +1699,16 @@ func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventA
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the GitHub username %s to the approval list", args.UserName, ed.ApprovalListGitHubUsername)
+ data := fmt.Sprintf("The GitHub username %s was added to the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1642,13 +1718,16 @@ func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogE
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the GitHub username %s from the approval list", args.UserName, ed.ApprovalListGitHubUsername)
+ data := fmt.Sprintf("The GitHub username %s was removed from the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1658,13 +1737,16 @@ func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *L
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", args.UserName, ed.ApprovalListGitHubOrg)
+ data := fmt.Sprintf("The GitHub organization %s was added to the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1674,13 +1756,16 @@ func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventA
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
// GetEventSummaryString . . .
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", args.UserName, ed.ApprovalListGitHubOrg)
+ data := fmt.Sprintf("The GitHub organization %s was removed from the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
@@ -1690,6 +1775,9 @@ func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEve
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
data = data + "."
return data, true
}
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 340d6d3da..3df3b867b 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -664,9 +664,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListAddEmailData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListEmail: value,
},
})
@@ -684,9 +681,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListRemoveEmailData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListEmail: value,
},
})
@@ -704,9 +698,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListAddDomainData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListDomain: value,
},
})
@@ -724,9 +715,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListRemoveDomainData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListDomain: value,
},
})
@@ -744,9 +732,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListAddGitHubUsernameData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListGitHubUsername: value,
},
})
@@ -764,9 +749,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListRemoveGitHubUsernameData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListGitHubUsername: value,
},
})
@@ -784,9 +766,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListAddGitHubOrgData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListGitHubOrg: value,
},
})
@@ -805,9 +784,6 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
UserModel: userModel,
ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAApprovalListRemoveGitHubOrgData{
- UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
- UserLFID: userModel.UserID,
ApprovalListGitHubOrg: value,
},
})
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index 8f7e5767c..1c0a623cb 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -165,9 +165,9 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
err := activityService.ProcessRepositoryEvent(&github.RepositoryEvent{
Action: aws.String("transferred"),
Repo: &github.Repository{
- ID: aws.Int64(1),
- Name: &repoName,
- HTMLURL: &newRepoUrl,
+ ID: aws.Int64(1),
+ Name: &repoName,
+ HTMLURL: &newRepoUrl,
},
Org: &github.Organization{
Login: &newOrgName,
From 785b259606d845a08d8597e19d71b459990788d4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 28 Apr 2021 15:39:31 -0700
Subject: [PATCH 0246/1276] Added Defensive Logic to Prevent Setting Enabled
Services on TLF Projects (#2903)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/helpers.go | 33 ++++++++++++---------
cla-backend-go/v2/project-service/client.go | 13 ++++++++
2 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 57edc2ded..2506e7897 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -520,21 +520,26 @@ func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, cla
if parentLookupErr != nil || parentProjectSFID == "" {
log.WithFields(f).WithError(parentLookupErr).Warnf("unable to lookup parent project SFID for project: %s", projectSFID)
} else {
- log.WithFields(f).Debugf("enabling parent project CLA service for project SFID: %s...", parentProjectSFID)
- enableProjectErr := psClient.EnableCLA(parentProjectSFID)
- if enableProjectErr != nil {
- log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", parentProjectSFID, enableProjectErr)
- errorList = append(errorList, enableProjectErr)
+ isTheLF, lookupErr := psClient.IsTheLinuxFoundation(parentProjectSFID)
+ if lookupErr != nil || isTheLF {
+ log.WithFields(f).Debugf("skipping setting the enabled services on The Linux Foundation parent project(s) for parent project SFID: %s", parentProjectSFID)
} else {
- log.WithFields(f).Debugf("enabled CLA service for parent project: %s", parentProjectSFID)
- // add event log entry
- s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.ProjectServiceCLAEnabled,
- ProjectID: parentProjectSFID,
- CLAGroupID: claGroupID,
- LfUsername: authUser.UserName,
- EventData: &events.ProjectServiceCLAEnabledData{},
- })
+ log.WithFields(f).Debugf("enabling parent project CLA service for project SFID: %s...", parentProjectSFID)
+ enableProjectErr := psClient.EnableCLA(parentProjectSFID)
+ if enableProjectErr != nil {
+ log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", parentProjectSFID, enableProjectErr)
+ errorList = append(errorList, enableProjectErr)
+ } else {
+ log.WithFields(f).Debugf("enabled CLA service for parent project: %s", parentProjectSFID)
+ // add event log entry
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ProjectServiceCLAEnabled,
+ ProjectID: parentProjectSFID,
+ CLAGroupID: claGroupID,
+ LfUsername: authUser.UserName,
+ EventData: &events.ProjectServiceCLAEnabledData{},
+ })
+ }
}
}
}
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index a8e438beb..a9072c2d9 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -4,6 +4,8 @@
package project_service
import (
+ "errors"
+ "fmt"
"strings"
"github.com/sirupsen/logrus"
@@ -232,6 +234,17 @@ func (pmm *Client) EnableCLA(projectSFID string) error {
"apiGWHost": apiGWHost,
}
+ theLF, lookupErr := pmm.IsTheLinuxFoundation(projectSFID)
+ if lookupErr != nil {
+ log.WithFields(f).WithError(lookupErr).Warnf("unable to test if project is The Linux Foundation using projectSFID: %s", projectSFID)
+ return lookupErr
+ }
+ if theLF {
+ msg := fmt.Sprintf("unable to set the enabled CLA services for The Linux Foundation with projectSFID: %s - not allowed", projectSFID)
+ log.WithFields(f).Debug(msg)
+ return errors.New(msg)
+ }
+
tok, err := token.GetToken()
if err != nil {
log.WithFields(f).WithError(err).Warning("problem retrieving token")
From c5eda16fa2a3a359499d27bbd9dbe85edff3bff1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 29 Apr 2021 15:46:54 -0700
Subject: [PATCH 0247/1276] Updated the CLA Manager Add/Remove Event Log Text
(#2904)
- Added authUser object as parameters to service calls
- Added additional debug for templates as well
Signed-off-by: David Deal
---
cla-backend-go/cla_manager/handlers.go | 15 ++++++++++--
cla-backend-go/cla_manager/service.go | 27 ++++++++++----------
cla-backend-go/events/event_data.go | 30 ++++++++++++++++++++---
cla-backend-go/template/service.go | 19 ++++++++++----
cla-backend-go/v2/cla_manager/handlers.go | 4 +--
cla-backend-go/v2/cla_manager/service.go | 16 ++++++------
6 files changed, 76 insertions(+), 35 deletions(-)
diff --git a/cla-backend-go/cla_manager/handlers.go b/cla-backend-go/cla_manager/handlers.go
index 99e15ce8d..4d16cd222 100644
--- a/cla-backend-go/cla_manager/handlers.go
+++ b/cla-backend-go/cla_manager/handlers.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ "github.com/LF-Engineering/lfx-kit/auth"
+
user_service "github.com/communitybridge/easycla/cla-backend-go/v2/user-service"
"github.com/sirupsen/logrus"
@@ -713,7 +715,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Audit Event sent from service upon success
- signature, addErr := service.AddClaManager(ctx, params.CompanyID, params.ProjectID, params.Body.UserLFID, "")
+ signature, addErr := service.AddClaManager(ctx, ToAuthUser(claUser), params.CompanyID, params.ProjectID, params.Body.UserLFID, "")
if addErr != nil {
msg := buildErrorMessageAddManager("Add CLA Manager - Service Error", params, addErr)
log.Warn(msg)
@@ -825,7 +827,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
}
// Audit Event sent from service upon success
- signature, deleteErr := service.RemoveClaManager(ctx, params.CompanyID, params.ProjectID, params.UserLFID)
+ signature, deleteErr := service.RemoveClaManager(ctx, ToAuthUser(claUser), params.CompanyID, params.ProjectID, params.UserLFID)
if deleteErr != nil {
msg := buildErrorMessageDeleteManager("EasyCLA - 400 Bad Request - Delete CLA Manager - Service Error", params, deleteErr)
@@ -1014,3 +1016,12 @@ func sendRequestDeniedEmailToRequester(emailSvc emails.EmailTemplateService, ema
log.Debugf("sent email with subject: %s to recipients: %+v", subject, recipients)
}
}
+
+// ToAuthUser converts a legacy v1 CLA user to a v2 platform auth user
+func ToAuthUser(claUser *user.CLAUser) *auth.User {
+ return &auth.User{
+ UserName: claUser.LFUsername,
+ Email: claUser.LFEmail,
+ ACL: auth.ACL{},
+ }
+}
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index f8e177443..39bad8916 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ "github.com/LF-Engineering/lfx-kit/auth"
+
"github.com/communitybridge/easycla/cla-backend-go/emails"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -35,8 +37,8 @@ type IService interface {
PendingRequest(companyID, claGroupID, requestID string) (*models.ClaManagerRequest, error)
DeleteRequest(requestID string) error
- AddClaManager(ctx context.Context, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error)
- RemoveClaManager(ctx context.Context, companyID string, claGroupID string, LFID string) (*models.Signature, error)
+ AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error)
+ RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string) (*models.Signature, error)
}
type service struct {
@@ -189,7 +191,7 @@ func (s service) DeleteRequest(requestID string) error {
}
// AddClaManager Adds LFID to Signature Access Control List list
-func (s service) AddClaManager(ctx context.Context, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) {
+func (s service) AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) {
userModel, userErr := s.usersService.GetUserByLFUserName(LFID)
if userErr != nil || userModel == nil {
@@ -254,16 +256,14 @@ func (s service) AddClaManager(ctx context.Context, companyID string, claGroupID
// Send an event
s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerCreated,
- ProjectID: claGroupModel.ProjectExternalID,
+ UserName: authUser.UserName,
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
ClaGroupModel: claGroupModel,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ProjectSFID: claGroupModel.ProjectExternalID,
CompanyID: companyID,
CompanyModel: companyModel,
- LfUsername: LFID,
- UserID: LFID,
- UserModel: userModel,
- ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAManagerCreatedEventData{
CompanyName: companyModel.CompanyName,
ProjectName: claGroupModel.ProjectName,
@@ -300,7 +300,7 @@ func (s service) getCompanySignature(ctx context.Context, companyID string, claG
}
// RemoveClaManager removes lfid from signature acl with given company and project
-func (s service) RemoveClaManager(ctx context.Context, companyID string, claGroupID string, LFID string) (*models.Signature, error) {
+func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string) (*models.Signature, error) {
userModel, userErr := s.usersService.GetUserByLFUserName(LFID)
if userErr != nil || userModel == nil {
@@ -367,16 +367,15 @@ func (s service) RemoveClaManager(ctx context.Context, companyID string, claGrou
// Send an event
s.eventsService.LogEvent(&events.LogEventArgs{
EventType: events.ClaManagerDeleted,
- ProjectID: claGroupModel.ProjectExternalID,
+ LfUsername: userModel.LfUsername,
+ UserName: authUser.UserName,
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
ClaGroupModel: claGroupModel,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ProjectSFID: claGroupModel.ProjectExternalID,
CompanyID: companyID,
CompanyModel: companyModel,
- LfUsername: userModel.LfUsername,
- UserID: LFID,
- UserModel: userModel,
- ProjectSFID: claGroupModel.ProjectExternalID,
EventData: &events.CLAManagerDeletedEventData{
CompanyName: companyModel.CompanyName,
ProjectName: claGroupModel.ProjectName,
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index fced9444f..ec1be67d1 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -645,8 +645,19 @@ func (ed *CLAManagerRequestCreatedEventData) GetEventDetailsString(args *LogEven
// GetEventDetailsString . . .
func (ed *CLAManagerCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s, LFID: %s, Email: %s was added as CLA Manager for Company: %s, Project: %s.",
- ed.UserName, ed.UserLFID, ed.UserEmail, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The user: %s LFID: %s, email: %s was added as CLA Manager", ed.UserName, ed.UserLFID, ed.UserEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
@@ -656,8 +667,19 @@ func (ed *CLAManagerCreatedEventData) GetEventDetailsString(args *LogEventArgs)
// GetEventDetailsString . . .
func (ed *CLAManagerDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("User: %s LFID: %s, Email: %s was removed as CLA Manager for Company: %s, Project: %s ",
- ed.UserName, ed.UserLFID, ed.UserEmail, ed.CompanyName, ed.ProjectName)
+ data := fmt.Sprintf("The user: %s LFID: %s, email: %s was removed as CLA Manager", ed.UserName, ed.UserLFID, ed.UserEmail)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index 50e25cbaf..d6ab26a50 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -59,7 +59,7 @@ func NewService(stage string, templateRepo Repository, docraptorClient docraptor
// GetTemplates API call
func (s service) GetTemplates(ctx context.Context) ([]models.Template, error) {
f := logrus.Fields{
- "functionName": "GetTemplates",
+ "functionName": "v1.template.service.GetTemplates",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
log.WithFields(f).Debug("Loading templates...")
@@ -81,7 +81,7 @@ func (s service) GetTemplates(ctx context.Context) ([]models.Template, error) {
func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error) {
f := logrus.Fields{
- "functionName": "CreateTemplatePreview",
+ "functionName": "v1.template.service.CreateTemplatePreview",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"templateID": claGroupFields.TemplateID,
"templateFor": templateFor,
@@ -93,6 +93,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
if claGroupFields.TemplateID != "" {
templateID = claGroupFields.TemplateID
}
+ log.WithFields(f).Debugf("using template ID: %s", templateID)
// Get Template
template, err = s.templateRepo.GetTemplate(templateID)
@@ -101,6 +102,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
claGroupFields.TemplateID)
return nil, err
}
+ log.WithFields(f).Debugf("loaded template ID: %s with ID: %s", template.Name, template.ID)
// Apply template fields
iclaTemplateHTML, cclaTemplateHTML, err := s.InjectProjectInformationIntoTemplate(template, claGroupFields.MetaFields)
@@ -134,7 +136,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
// CreateCLAGroupTemplate
func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error) {
f := logrus.Fields{
- "functionName": "CreateCLAGroupTemplate",
+ "functionName": "v1.template.service.CreateCLAGroupTemplate",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"claGroupFields": claGroupFields,
@@ -265,7 +267,7 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
func (s service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType string, watermark bool) ([]byte, error) {
f := logrus.Fields{
- "functionName": "GetCLATemplatePreview",
+ "functionName": "v1.template.service.GetCLATemplatePreview",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"claType": claType,
@@ -357,6 +359,11 @@ func (s service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType
// InjectProjectInformationIntoTemplate
func (s service) InjectProjectInformationIntoTemplate(template models.Template, metaFields []*models.MetaField) (string, string, error) {
+ f := logrus.Fields{
+ "functionName": "v1.template.service.InjectProjectInformationIntoTemplate",
+ "templateName": template.Name,
+ "templateID": template.ID,
+ }
lookupMap := map[string]models.MetaField{}
for _, field := range template.MetaFields {
lookupMap[field.Name] = *field
@@ -381,11 +388,13 @@ func (s service) InjectProjectInformationIntoTemplate(template models.Template,
return "", "", errors.New("bad request: required fields for template were not found")
}
+ log.WithFields(f).Debugf("Rendering ICLA body for template: %s with id: %s", template.Name, template.ID)
iclaTemplateHTML, err := raymond.Render(template.IclaHTMLBody, metaFieldsMap)
if err != nil {
return "", "", err
}
+ log.WithFields(f).Debugf("Rendering CCLA body for template: %s with id: %s", template.Name, template.ID)
cclaTemplateHTML, err := raymond.Render(template.CclaHTMLBody, metaFieldsMap)
if err != nil {
return "", "", err
@@ -414,7 +423,7 @@ func (s service) generateTemplateS3FilePath(claGroupID, claType string) string {
// SaveTemplateToS3
func (s service) SaveTemplateToS3(bucket, filepath string, template io.ReadCloser) (string, error) {
f := logrus.Fields{
- "functionName": "SaveTemplateToS3",
+ "functionName": "v1.template.service.SaveTemplateToS3",
"bucket": bucket,
"filepath": filepath,
}
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index 4f134468b..a86b48f58 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -78,7 +78,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
return cla_manager.NewCreateCLAManagerInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, err.Error(), err))
}
- compCLAManager, errorResponse := service.CreateCLAManager(ctx, cginfo.ClaGroupID, params, authUser.UserName)
+ compCLAManager, errorResponse := service.CreateCLAManager(ctx, authUser, cginfo.ClaGroupID, params, authUser.UserName)
if errorResponse != nil {
if errorResponse.Code == BadRequest {
return cla_manager.NewCreateCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(errorResponse)
@@ -126,7 +126,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- errResponse := service.DeleteCLAManager(ctx, cginfo.ClaGroupID, params)
+ errResponse := service.DeleteCLAManager(ctx, authUser, cginfo.ClaGroupID, params)
if errResponse != nil {
return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(errResponse)
}
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index c154d1811..aa755b1d2 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -82,8 +82,8 @@ type service struct {
// Service interface
type Service interface {
- CreateCLAManager(ctx context.Context, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse)
- DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse
+ CreateCLAManager(ctx context.Context, authUser *auth.User, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse)
+ DeleteCLAManager(ctx context.Context, authUser *auth.User, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse
InviteCompanyAdmin(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, name string, contributor *v1User.User) ([]*models.ClaManagerDesignee, error)
CreateCLAManagerDesignee(ctx context.Context, companyID string, projectID string, userEmail string) (*models.ClaManagerDesignee, error)
CreateCLAManagerRequest(ctx context.Context, contactAdmin bool, companyID string, projectID string, userEmail string, fullName string, authUser *auth.User) (*models.ClaManagerDesignee, error)
@@ -120,7 +120,7 @@ func NewService(emailTemplateService emails.EmailTemplateService, compService co
}
// CreateCLAManager creates Cla Manager
-func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse) {
+func (s *service) CreateCLAManager(ctx context.Context, authUser *auth.User, claGroupID string, params cla_manager.CreateCLAManagerParams, authUsername string) (*models.CompanyClaManager, *models.ErrorResponse) {
f := logrus.Fields{
"functionName": "cla_manager.service.CreateCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -237,7 +237,7 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
}
// Add CLA Manager to Database
- signature, addErr := s.managerService.AddClaManager(ctx, v1CompanyModel.CompanyID, claGroupID, user.Username, projectSF.Name)
+ signature, addErr := s.managerService.AddClaManager(ctx, authUser, v1CompanyModel.CompanyID, claGroupID, user.Username, projectSF.Name)
if addErr != nil {
msg := buildErrorMessageCreate(params, addErr)
log.WithFields(f).Warn(msg)
@@ -273,17 +273,17 @@ func (s *service) CreateCLAManager(ctx context.Context, claGroupID string, param
return claCompanyManager, nil
}
-func (s *service) DeleteCLAManager(ctx context.Context, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse {
+func (s *service) DeleteCLAManager(ctx context.Context, authUser *auth.User, claGroupID string, params cla_manager.DeleteCLAManagerParams) *models.ErrorResponse {
f := logrus.Fields{
"functionName": "cla_manager.service.DeleteCLAManager",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"companyID": params.CompanyID,
- "xUserName": params.XUSERNAME,
- "xEmail": params.XEMAIL,
+ "authUserName": authUser.UserName,
+ "authUserEmail": authUser.Email,
}
- signature, deleteErr := s.managerService.RemoveClaManager(ctx, params.CompanyID, claGroupID, params.UserLFID)
+ signature, deleteErr := s.managerService.RemoveClaManager(ctx, authUser, params.CompanyID, claGroupID, params.UserLFID)
if deleteErr != nil {
msg := buildErrorMessageDelete(params, deleteErr)
From c43fc6fc8927327f817f4e4a55c6561b07075c8b Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 30 Apr 2021 02:40:26 +0300
Subject: [PATCH 0248/1276] Bug/Invalidate Gerrit (#2905)
- Resolved email and email domain approval list removal invalidation for gerrit use cases
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/repository.go | 89 +++++++++----------------
1 file changed, 31 insertions(+), 58 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 67c54ad5a..432b71ece 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2138,10 +2138,24 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
Error: fmt.Errorf("unable to get icla for user: %s ", user.UserID),
}
} else {
+
+ // Update gerrit user
+ if utils.StringInSlice(user.LfUsername, gerritICLAECLAs) {
+ gerritIclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeICLA)
+ if gerritIclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).WithError(gerritIclaErr).Warn(msg)
+ }
+ eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
+ if eclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).WithError(eclaErr).Warn(msg)
+ }
+ }
results <- &ICLAUserResponse{
ICLASignature: &models.IclaSignature{
GithubUsername: icla.UserGHUsername,
- LfUsername: icla.UserLFID,
+ LfUsername: user.LfUsername,
SignatureID: icla.SignatureID,
},
}
@@ -2165,25 +2179,6 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Send email
repo.sendEmail(ctx, email, &approvalList, iclas, eclas)
- //update gerrit permissions
- gerritUser, getGerritUserErr := repo.getGerritUserByEmail(ctx, email, gerritICLAECLAs)
- if getGerritUserErr != nil || gerritUser == nil {
- msg := fmt.Sprintf("unable to get gerrit user by email : %s ", email)
- log.WithFields(f).WithError(getGerritUserErr).Warn(msg)
- return
- }
- iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, gerritUser.LfUsername, utils.ClaTypeICLA)
- if iclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", gerritUser.LfUsername, approvalList.ClaGroupID)
- log.WithFields(f).Warn(msg)
- return
- }
- eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, gerritUser.LfUsername, utils.ClaTypeECLA)
- if eclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", gerritUser.LfUsername, approvalList.ClaGroupID)
- log.WithFields(f).Warn(msg)
- return
- }
}(email)
}
wg.Wait()
@@ -2613,30 +2608,6 @@ func (repo repository) invalidateSignatures(ctx context.Context, approvalList *A
}
}
-// getGerritUsersByEmail searches gerrit instances for users with given email
-func (repo repository) getGerritUserByEmail(ctx context.Context, email string, gerritICLAECLAs []string) (*models.User, error) {
- f := logrus.Fields{
- "functionName": "v1.signatures.repository.getGerritUserByEmail",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "email": email,
- }
-
- log.WithFields(f).Debugf("checking gerrit user for email: %s ", email)
- if email != "" {
- claUser, err := repo.usersRepo.GetUserByEmail(email)
- if err != nil {
- msg := fmt.Sprintf("unable to get easyclauser by email: %s ", email)
- log.WithFields(f).Warn(msg)
- return nil, err
- }
- if utils.StringInSlice(claUser.LfUsername, gerritICLAECLAs) {
- return claUser, nil
- }
- }
-
- return nil, nil
-}
-
// verify UserApprovals checks user
func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatureID string, claManager *models.User, approvalList *ApprovalList) (*models.User, error) {
f := logrus.Fields{
@@ -2659,9 +2630,10 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
if approvalList.Criteria == utils.EmailDomainCriteria {
// Handle Domains
+ log.WithFields(f).Debugf("Handling domain for user email: %s with approval list: %+v ", email, approvalList.ApprovalList)
domain := strings.Split(email, "@")[1]
- if utils.StringInSlice(domain, approvalList.DomainApprovals) {
- if !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) && !utils.StringInSlice(email, approvalList.EmailApprovals) {
+ if utils.StringInSlice(domain, approvalList.ApprovalList) {
+ if (!utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) || utils.StringInSlice(user.LfUsername, approvalList.GerritICLAECLAs)) && !utils.StringInSlice(email, approvalList.EmailApprovals) {
//Invalidate record
note := fmt.Sprintf("Signature invalidated (approved set to false) by %s due to %s removal", utils.GetBestUsername(claManager), utils.EmailDomainCriteria)
err := repo.InvalidateProjectRecord(ctx, signatureID, note)
@@ -2670,18 +2642,19 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
return user, err
}
- log.WithFields(f).Debugf("removing gerrit user:%s from claGroup: %s ...", user.LfUsername, approvalList.ClaGroupID)
- iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeICLA)
- if iclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
- log.WithFields(f).Warn(msg)
- return user, iclaErr
- }
- eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
- if eclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
- log.WithFields(f).Warn(msg)
- return user, eclaErr
+ // Update Gerrit group users
+ if utils.StringInSlice(user.LfUsername, approvalList.GerritICLAECLAs) {
+ log.WithFields(f).Debugf("removing gerrit user:%s from claGroup: %s ...", user.LfUsername, approvalList.ClaGroupID)
+ iclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeICLA)
+ if iclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ }
+ eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
+ if eclaErr != nil {
+ msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ log.WithFields(f).Warn(msg)
+ }
}
}
}
From d70722ed1497b2603a1eeee867a3ebe2a3707b09 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 30 Apr 2021 09:19:40 +0300
Subject: [PATCH 0249/1276] [#2891] Bug/Search ICLA (#2899)
- Resolved failed search for iclas based on signature_reference_name or email
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/repository.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 432b71ece..8de69888f 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2877,6 +2877,13 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ if searchTerm != nil {
+ log.WithFields(f).Debugf("adding search term filter for : %s ", *searchTerm)
+ filterAdded := true
+ searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*searchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*searchTerm)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ }
+
// Use the builder to create the expression
expr, err := expression.NewBuilder().
WithKeyCondition(condition).
From ef49a25434615141e6c5b29944412c517b80132a Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 30 Apr 2021 16:53:46 +0300
Subject: [PATCH 0250/1276] [#2842] Feature/Email Subject (#2906)
- Updated email subject for signature invalidation
Signed-off-by: Harold Wanyama
---
cla-backend-go/signatures/repository.go | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 8de69888f..6d6c8fb17 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2478,8 +2478,8 @@ func (repo repository) sendEmail(ctx context.Context, email string, approvalList
// Send CCLA Email
if removalType == CCLA {
- subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
- log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ subject := fmt.Sprintf("EasyCLA: CCLA invalidated for :%s ", approvalList.ClaGroupName)
+ log.WithFields(f).Debugf("sending ccla invalidation email to :%s ", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLASignatureTemplateName, InvalidateCCLASignatureTemplate, params)
if renderErr != nil {
log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
@@ -2490,8 +2490,8 @@ func (repo repository) sendEmail(ctx context.Context, email string, approvalList
}
}
} else if removalType == ICLA {
- subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
- log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ subject := fmt.Sprintf("EasyCLA: ICLA invalidated for :%s ", approvalList.ClaGroupName)
+ log.WithFields(f).Debugf("sending icla invalidation email to :%s ", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateICLASignatureTemplateName, InvalidateICLASignatureTemplate, params)
if renderErr != nil {
log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
@@ -2502,8 +2502,8 @@ func (repo repository) sendEmail(ctx context.Context, email string, approvalList
}
}
} else if removalType == CCLAICLA {
- subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
- log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ subject := fmt.Sprintf("EasyCLA: ICLA invalidated for :%s ", approvalList.ClaGroupName)
+ log.WithFields(f).Debugf("sending icla invalidation email to :%s ", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLAICLASignatureTemplateName, InvalidateCCLASignatureTemplate, params)
if renderErr != nil {
log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
@@ -2514,8 +2514,8 @@ func (repo repository) sendEmail(ctx context.Context, email string, approvalList
}
}
} else if removalType == CCLAICLAECLA {
- subject := fmt.Sprintf("EasyCLA: Approval List Update for :%s ", email)
- log.WithFields(f).Debugf("sending approval list update removal for :%s ", email)
+ subject := fmt.Sprintf("EasyCLA: Employee Acknowledgement invalidated for :%s ", approvalList.ClaGroupName)
+ log.WithFields(f).Debugf("sending employee acknowledgement invalidation email to :%s ", email)
body, renderErr := utils.RenderTemplate(approvalList.Version, InvalidateCCLAICLAECLASignatureTemplateName, InvalidateCCLAICLAECLASignatureTemplate, params)
if renderErr != nil {
log.WithFields(f).Debugf("unable to render email approval template for user: %s ", email)
From 25fdfb5055e37625e426078a8796ca9adb88b714 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 3 May 2021 10:31:30 -0700
Subject: [PATCH 0251/1276] Bump urllib3 from 1.25.7 to 1.25.8 in /cla-backend
(#2908)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.25.7 to 1.25.8.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.25.7...1.25.8)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index 82e845d8c..0d872f646 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -53,7 +53,7 @@ six==1.13.0
soupsieve==1.9.5
termcolor==1.1.0
typed-ast==1.4.1
-urllib3==1.25.7
+urllib3==1.25.8
vintage==0.4.1
wcwidth==0.1.7
Werkzeug==0.15.5
From 1ea0ead7eefdd436af5792657b591e7473086ab5 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Tue, 4 May 2021 02:00:23 +0300
Subject: [PATCH 0252/1276] [#2886] Bug/Event Logging (#2909)
- Resolved foundation event logging with TLF as parent
- Added logging for cla manager add/removal
- Added lf username to eventargs params
Signed-off-by: Harold Wanyama
---
cla-backend-go/cla_manager/service.go | 27 +++++++++++++++++++++++----
cla-backend-go/events/service.go | 2 +-
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index 39bad8916..58ddbb212 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/emails"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -193,6 +194,15 @@ func (s service) DeleteRequest(requestID string) error {
// AddClaManager Adds LFID to Signature Access Control List list
func (s service) AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) {
+ f := logrus.Fields{
+ "functionName": "v1.cla_manager.AddClaManager",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "companyID": companyID,
+ "claGroupID": claGroupID,
+ "LFID": LFID,
+ "projectName": projectSFName,
+ }
+
userModel, userErr := s.usersService.GetUserByLFUserName(LFID)
if userErr != nil || userModel == nil {
return nil, userErr
@@ -218,7 +228,7 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company
claManagers := sigModel.SignatureACL
- log.Debugf("Got Company signatures - Company: %s , Project: %s , signatureID: %s ",
+ log.WithFields(f).Debugf("Got Company signatures - Company: %s , Project: %s , signatureID: %s ",
companyID, claGroupID, sigModel.SignatureID)
// Update the signature ACL
@@ -230,7 +240,7 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company
// Update the company ACL record in EasyCLA
companyACLError := s.companyService.AddUserToCompanyAccessList(ctx, companyID, LFID)
if companyACLError != nil {
- log.Warnf("AddCLAManager- Unable to add user to company ACL, companyID: %s, user: %s, error: %+v", companyID, LFID, companyACLError)
+ log.WithFields(f).Warnf("AddCLAManager- Unable to add user to company ACL, companyID: %s, user: %s, error: %+v", companyID, LFID, companyACLError)
return nil, companyACLError
}
@@ -257,6 +267,7 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company
s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.ClaManagerCreated,
UserName: authUser.UserName,
+ LfUsername: authUser.UserName,
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
ClaGroupModel: claGroupModel,
@@ -302,6 +313,14 @@ func (s service) getCompanySignature(ctx context.Context, companyID string, claG
// RemoveClaManager removes lfid from signature acl with given company and project
func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string) (*models.Signature, error) {
+ f := logrus.Fields{
+ "functionName": "v1.cla_manager.RemoveClaManager",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "LFID": LFID,
+ "companyID": companyID,
+ }
+
userModel, userErr := s.usersService.GetUserByLFUserName(LFID)
if userErr != nil || userModel == nil {
return nil, userErr
@@ -333,7 +352,7 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp
// Update the signature ACL
updatedSignature, aclErr := s.sigService.RemoveCLAManager(ctx, sigModel.SignatureID, LFID)
if aclErr != nil || updatedSignature == nil {
- log.Warnf("remove CLA Manager returned an error or empty signature model using Signature ID: %s, error: %+v",
+ log.WithFields(f).Warnf("remove CLA Manager returned an error or empty signature model using Signature ID: %s, error: %+v",
sigModel.SignatureID, sigErr)
return nil, aclErr
}
@@ -367,7 +386,7 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp
// Send an event
s.eventsService.LogEvent(&events.LogEventArgs{
EventType: events.ClaManagerDeleted,
- LfUsername: userModel.LfUsername,
+ LfUsername: authUser.UserName,
UserName: authUser.UserName,
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index e2670565a..4cbb22885 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -263,7 +263,7 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
return nil
}
var parentProjectName, parentProjectID string
- if utils.IsProjectCategory(project, parentProject) {
+ if !utils.IsProjectHasRootParent(project) {
parentProjectName = parentProject.Name
parentProjectID = parentProject.ID
} else {
From 3c9b4e2128c444e1d304de973efa1d90d3bb687b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 May 2021 07:48:42 -0700
Subject: [PATCH 0253/1276] Bump rsa from 4.1 to 4.7 in /cla-backend (#2907)
Bumps [rsa](https://github.com/sybrenstuvel/python-rsa) from 4.1 to 4.7.
- [Release notes](https://github.com/sybrenstuvel/python-rsa/releases)
- [Changelog](https://github.com/sybrenstuvel/python-rsa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sybrenstuvel/python-rsa/compare/version-4.1...version-4.7)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index 0d872f646..b6037b1df 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -46,7 +46,7 @@ python-dateutil==2.8.0
python-jose==3.0.1
requests==2.22.0
requests-oauthlib==1.2.0
-rsa==4.1
+rsa==4.7
s3transfer==0.2.1
sentinels==1.0.0
six==1.13.0
From 0da8fd06c29c00f454a548518b59536f8ca8191a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 5 May 2021 11:32:05 -0700
Subject: [PATCH 0254/1276] Added SignatureSigned and SignatureApproved to ICLA
Signature Query Response (#2916)
Signed-off-by: David Deal
---
cla-backend-go/Makefile | 1 +
cla-backend-go/signatures/repository.go | 10 +++++---
.../swagger/common/icla-signature.yaml | 25 ++++++++++++++++++-
3 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index ea7d1476c..2c3577f48 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -151,6 +151,7 @@ swagger-build-acs-service:
-t v2/acs-service \
-f swagger/acs-service.yaml
+build-swagger: swagger-build
swagger-build: clean-swagger swagger-prep swagger-build-v1-services swagger-build-v2-services swagger-build-project-service swagger-build-organization-service swagger-build-user-service swagger-build-acs-service
swagger-validate: swagger-v1-validate swagger-v2-validate
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 6d6c8fb17..deb3e1ddb 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -3028,13 +3028,15 @@ func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures
IclaSignature: &models.IclaSignature{
GithubUsername: sig.UserGithubUsername,
LfUsername: sig.UserLFUsername,
+ SignatureApproved: sig.SignatureApproved,
+ SignatureSigned: sig.SignatureSigned,
+ SignatureModified: sig.DateModified,
SignatureID: sig.SignatureID,
- UserEmail: sig.UserEmail,
- UserName: sig.UserName,
SignedOn: sigSignedTime,
- UserDocusignName: sig.UserDocusignName,
UserDocusignDateSigned: sigSignedTime,
- SignatureModified: sig.DateModified,
+ UserDocusignName: sig.UserDocusignName,
+ UserEmail: sig.UserEmail,
+ UserName: sig.UserName,
},
SignatureReferenceID: sig.SignatureReferenceID,
})
diff --git a/cla-backend-go/swagger/common/icla-signature.yaml b/cla-backend-go/swagger/common/icla-signature.yaml
index 41e085b4f..00ff988cf 100644
--- a/cla-backend-go/swagger/common/icla-signature.yaml
+++ b/cla-backend-go/swagger/common/icla-signature.yaml
@@ -5,21 +5,44 @@ type: object
title: ICLASignature
properties:
signature_id:
- type: string
+ description: the signature ID
+ $ref: './common/properties/internal-id.yaml'
github_username:
type: string
+ description: the user's github username
+ example: 'tomcruise'
lf_username:
type: string
+ description: the user's LF username
+ example: 'tomcruise'
user_name:
type: string
+ description: the user's user name/real name
+ example: 'Tom Cruise'
user_email:
type: string
+ description: the user's email address
+ example: 'tomcruise@hollywood.com'
signed_on:
type: string
+ description: the date/time the ICLA was signed
+ example: '2020-03-16T17:57:58Z'
userDocusignName:
type: string
+ description: the name provided on the DocuSign document
+ example: 'Jack Daniels'
userDocusignDateSigned:
type: string
+ description: the signature date signed value from DocuSign
+ example: '2020-03-16T17:57:58Z'
+ signatureApproved:
+ type: boolean
+ description: the signature approved flag - true or false value
+ example: true
+ signatureSigned:
+ type: boolean
+ description: the signature signed flag - true or false value
+ example: true
signatureModified:
type: string
description: the signature modified created time
From 22ecfdef77f76d8c8071cc4deaaaf3561a2349af Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 6 May 2021 19:10:16 +0300
Subject: [PATCH 0255/1276] [Snyk] Upgrade rxjs from 6.5.5 to 6.6.7 (#2914)
Snyk has created this PR to upgrade rxjs from 6.5.5 to 6.6.7.
See this package in npm:
See this project in Snyk:
https://app.snyk.io/org/pbajpai/project/e0799cf1-c42b-4a32-8810-b716ecd0b2b9?utm_source=github&utm_medium=upgrade-pr
---
cla-landing-page/package.json | 2 +-
cla-landing-page/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-landing-page/package.json b/cla-landing-page/package.json
index 5d429eee7..6946820be 100644
--- a/cla-landing-page/package.json
+++ b/cla-landing-page/package.json
@@ -55,7 +55,7 @@
"aws-sdk": "^2.733.0",
"bootstrap": "^4.4.0",
"query-string": "^6.13.6",
- "rxjs": "~6.5.4",
+ "rxjs": "~6.6.7",
"serverless": "^2.15.0",
"serverless-aws-alias": "^1.8.0",
"serverless-cloudfront-invalidate": "^1.5.0",
diff --git a/cla-landing-page/yarn.lock b/cla-landing-page/yarn.lock
index 8e79810cb..377c1658b 100644
--- a/cla-landing-page/yarn.lock
+++ b/cla-landing-page/yarn.lock
@@ -10138,10 +10138,10 @@ rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.2:
dependencies:
tslib "^1.9.0"
-rxjs@~6.5.4:
- version "6.5.5"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
- integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
+rxjs@~6.6.7:
+ version "6.6.7"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
+ integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
dependencies:
tslib "^1.9.0"
From 9716a37d17a85bfa059944a9783cf54ddc9ec634 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 6 May 2021 09:23:19 -0700
Subject: [PATCH 0256/1276] Added UserID to ICLA Signature Response (#2917)
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 1 +
cla-backend-go/swagger/common/icla-signature.yaml | 3 +++
2 files changed, 4 insertions(+)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index deb3e1ddb..17cb750a3 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -3027,6 +3027,7 @@ func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures
intermediateResponse = append(intermediateResponse, &iclaSignatureWithDetails{
IclaSignature: &models.IclaSignature{
GithubUsername: sig.UserGithubUsername,
+ UserID: sig.SignatureReferenceID,
LfUsername: sig.UserLFUsername,
SignatureApproved: sig.SignatureApproved,
SignatureSigned: sig.SignatureSigned,
diff --git a/cla-backend-go/swagger/common/icla-signature.yaml b/cla-backend-go/swagger/common/icla-signature.yaml
index 00ff988cf..4b4c627d9 100644
--- a/cla-backend-go/swagger/common/icla-signature.yaml
+++ b/cla-backend-go/swagger/common/icla-signature.yaml
@@ -7,6 +7,9 @@ properties:
signature_id:
description: the signature ID
$ref: './common/properties/internal-id.yaml'
+ user_id:
+ description: the user ID
+ $ref: './common/properties/internal-id.yaml'
github_username:
type: string
description: the user's github username
From 6860ed3b071be2325e7ed31dbcbe61d2abb8d0e6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 6 May 2021 15:51:45 -0700
Subject: [PATCH 0257/1276] Updated Signature Queries (#2919)
- Signature queries will now return Active, Incomplete, and Disabled signatures in most views
- Updated swagger to allow clients to filter based on approved and signed signatures
- Added USER_AUTH_TRACING to allow printout of user scopes for authenticated users (when needed)
Signed-off-by: David Deal
---
cla-backend-go/serverless.yml | 2 +
cla-backend-go/signatures/handlers.go | 24 ++--
cla-backend-go/signatures/repository.go | 141 +++++++++++++--------
cla-backend-go/signatures/service.go | 30 +++--
cla-backend-go/swagger/cla.v1.yaml | 16 +++
cla-backend-go/swagger/cla.v2.yaml | 24 +++-
cla-backend-go/utils/auth_user.go | 7 +
cla-backend-go/v2/company/handlers.go | 2 +-
cla-backend-go/v2/signatures/converters.go | 10 +-
cla-backend-go/v2/signatures/handlers.go | 8 +-
cla-backend-go/v2/signatures/service.go | 17 ++-
11 files changed, 186 insertions(+), 95 deletions(-)
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index aac6235f3..a92ccab80 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -249,6 +249,8 @@ provider:
# https://github.com/pypa/setuptools/issues/2350 and
# https://github.com/pypa/setuptools/issues/2232
SETUPTOOLS_USE_DISTUTILS: stdlib
+ # Turn on USER_AUTH_TRACING to see additional debug of user scopes for the authenticated users - output is verbose
+ USER_AUTH_TRACING: true
stackTags:
Name: ${self:service}
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index 6cfc1b0d2..c1589bce2 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -38,7 +38,8 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
}
log.WithFields(f).Debug("querying for individual signature...")
- signatureModel, sigErr := service.GetIndividualSignature(ctx, params.ClaGroupID, params.UserID)
+ approved, signed := true, true
+ signatureModel, sigErr := service.GetIndividualSignature(ctx, params.ClaGroupID, params.UserID, &approved, &signed)
if sigErr != nil {
msg := fmt.Sprintf("error retrieving signature using ClaGroupID: %s, userID: %s, error: %+v",
params.ClaGroupID, params.UserID, sigErr)
@@ -85,22 +86,29 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
api.SignaturesGetSignedCCLADocumentHandler = signatures.GetSignedCCLADocumentHandlerFunc(func(params signatures.GetSignedCCLADocumentParams) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- signatureModel, sigErr := service.GetCorporateSignature(ctx, params.ClaGroupID, params.CompanyID)
+ f := logrus.Fields{
+ "functionName": "v1.signatures.handler.SignaturesGetSignedCCLADocumentHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": params.ClaGroupID,
+ "companyID": params.CompanyID,
+ }
+
+ approved, signed := true, true
+ signatureModel, sigErr := service.GetCorporateSignature(ctx, params.ClaGroupID, params.CompanyID, &approved, &signed)
if sigErr != nil {
msg := fmt.Sprintf("EasyCLA - 500 Internal Server Error - error retrieving signature using ClaGroupID: %s, CompanyID: %s, error: %+v",
params.ClaGroupID, params.CompanyID, sigErr)
- log.Warn(msg)
+ log.WithFields(f).WithError(sigErr).Warn(msg)
return signatures.NewGetSignedCCLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "500",
Message: msg,
})
-
}
if signatureModel == nil {
msg := fmt.Sprintf("EasyCLA - 404 Not Found - - error retrieving signature using ClaGroupID: %s, CompanyID: %s",
params.ClaGroupID, params.CompanyID)
- log.Warn(msg)
+ log.WithFields(f).Warn(msg)
return signatures.NewGetSignedCCLADocumentNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "404",
Message: msg,
@@ -114,7 +122,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
if s3Err != nil {
msg := fmt.Sprintf("EasyCLA - 500 Internal Server Error - unable to locate PDF from source using ClaGroupID: %s, CompanyID: %s, s3 error: %+v",
params.ClaGroupID, params.CompanyID, s3Err)
- log.Warn(msg)
+ log.WithFields(f).WithError(s3Err).Warn(msg)
return signatures.NewGetSignedCCLADocumentInternalServerError().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "500",
Message: msg,
@@ -130,9 +138,9 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
if writeErr != nil {
msg := fmt.Sprintf("EasyCLA - 500 Internal Server Error - generating s3 redirect for the client client using source using ClaGroupID: %s, CompanyID: %s, error: %+v",
params.ClaGroupID, params.CompanyID, s3Err)
- log.Warn(msg)
+ log.WithFields(f).WithError(writeErr).Warn(msg)
}
- log.Debugf("SignaturesGetSignedICLADocumentHandler - wrote %d bytes", bytesWritten)
+ log.WithFields(f).Debugf("wrote %d bytes", bytesWritten)
})
})
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 17cb750a3..6b32a7932 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -64,13 +64,13 @@ type SignatureRepository interface {
InvalidateProjectRecord(ctx context.Context, signatureID, note string) error
GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
- GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error)
- GetCorporateSignature(ctx context.Context, claGroupID, companyID string) (*models.Signature, error)
+ GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error)
+ GetCorporateSignature(ctx context.Context, claGroupID, companyID string, approved, signed *bool) (*models.Signature, error)
GetSignatureACL(ctx context.Context, signatureID string) ([]string, error)
GetProjectSignatures(ctx context.Context, params signatures.GetProjectSignaturesParams) (*models.Signatures, error)
CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error)
- GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
- GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error)
+ GetProjectCompanySignature(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
+ GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error)
GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria, pageSize int64) (*models.Signatures, error)
GetCompanySignatures(ctx context.Context, params signatures.GetCompanySignaturesParams, pageSize int64, loadACL bool) (*models.Signatures, error)
GetCompanyIDsWithSignedCorporateSignatures(ctx context.Context, claGroupID string) ([]SignatureCompanyID, error)
@@ -87,7 +87,7 @@ type SignatureRepository interface {
AddUsersDetails(ctx context.Context, signatureID string, userID string) error
AddSignedOn(ctx context.Context, signatureID string) error
- GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
+ GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error)
}
@@ -437,7 +437,7 @@ func (repo repository) GetSignature(ctx context.Context, signatureID string) (*m
}
// GetIndividualSignature returns the signature record for the specified CLA Group and User
-func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error) {
+func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetIndividualSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -457,10 +457,20 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
And(expression.Key("signature_reference_id").Equal(expression.Value(userID)))
filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ // If the caller provided a signature signed value...add the appropriate filter
+ if signed != nil {
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
+ filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
+ }
+
+ // If the caller provided a signature approved value...add the appropriate filter
+ if approved != nil {
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
+ filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
+ }
+
builder := expression.NewBuilder().
WithKeyCondition(condition).
WithFilter(filter).
@@ -532,7 +542,7 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
}
// GetCorporateSignature returns the signature record for the specified CLA Group and Company ID
-func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, companyID string) (*models.Signature, error) {
+func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, companyID string, approved, signed *bool) (*models.Signature, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetCorporateSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -541,8 +551,8 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
"companyID": companyID,
"signatureType": "ccla",
"signatureReferenceType": "company",
- "signatureApproved": "true",
- "signatureSigned": "true",
+ "signatureApproved": utils.BoolValue(approved),
+ "signatureSigned": utils.BoolValue(signed),
}
// These are the keys we want to match for an CCLA Signature with a given CLA Group and Company ID
@@ -550,10 +560,20 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
And(expression.Name("signature_reference_type").Equal(expression.Value("company"))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ // If the caller provided a signature signed value...add the appropriate filter
+ if signed != nil {
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
+ filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
+ }
+
+ // If the caller provided a signature approved value...add the appropriate filter
+ if approved != nil {
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
+ filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
+ }
+
builder := expression.NewBuilder().
WithKeyCondition(condition).
WithFilter(filter).
@@ -685,7 +705,7 @@ func addConditionToFilter(filter expression.ConditionBuilder, cond expression.Co
}
// GetProjectSignatures returns a list of signatures for the specified project
-func (repo repository) GetProjectSignatures(ctx context.Context, params signatures.GetProjectSignaturesParams) (*models.Signatures, error) {
+func (repo repository) GetProjectSignatures(ctx context.Context, params signatures.GetProjectSignaturesParams) (*models.Signatures, error) { // nolint
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetProjectSignatures",
"tableName": repo.signatureTableName,
@@ -698,6 +718,8 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
"pageSize": aws.Int64Value(params.PageSize),
"nextKey": aws.StringValue(params.NextKey),
"sortOrder": aws.StringValue(params.SortOrder),
+ "approved": utils.BoolValue(params.Approved),
+ "signed": utils.BoolValue(params.Signed),
}
indexName := SignatureProjectIDIndex
@@ -722,21 +744,15 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
}
} else {
@@ -770,13 +786,18 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
}
+ }
- // Filter condition to cater for approved and signed signatures
- signatureApprovedExpression := expression.Name("signature_approved").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureApprovedExpression, &filterAdded)
+ // If the caller provided a signature approved value...add the appropriate filter
+ if params.Approved != nil {
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", *params.Approved)
+ filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved))))
+ }
- signatureSignedExpression := expression.Name("signature_signed").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureSignedExpression, &filterAdded)
+ // If the caller provided a signature signed value...add the appropriate filter
+ if params.Signed != nil {
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *params.Signed)
+ filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed))))
}
if filterAdded {
@@ -902,6 +923,8 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
"pageSize": aws.Int64Value(params.PageSize),
"nextKey": aws.StringValue(params.NextKey),
"sortOrder": aws.StringValue(params.SortOrder),
+ "approved": utils.BoolValue(params.Approved),
+ "signed": utils.BoolValue(params.Signed),
"companyIDList": params.Body,
}
@@ -927,21 +950,15 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
}
} else {
@@ -975,13 +992,18 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
}
+ }
- // Filter condition to cater for approved and signed signatures
- signatureApprovedExpression := expression.Name("signature_approved").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureApprovedExpression, &filterAdded)
+ // If the caller provided a signature approved value...add the appropriate filter
+ if params.Approved != nil {
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", *params.Approved)
+ filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved))))
+ }
- signatureSignedExpression := expression.Name("signature_signed").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureSignedExpression, &filterAdded)
+ // If the caller provided a signature signed value...add the appropriate filter
+ if params.Signed != nil {
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *params.Signed)
+ filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed))))
}
if len(params.Body) > 0 {
@@ -1107,7 +1129,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
}
// GetProjectCompanySignature returns a the signature for the specified project and specified company with the other query flags
-func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
+func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetProjectCompanySignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -1141,17 +1163,17 @@ func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID
}
// GetProjectCompanySignatures returns a list of signatures for the specified project and specified company
-func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error) {
+func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, sortOrder *string, pageSize *int64) (*models.Signatures, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetProjectCompanySignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"companyID": companyID,
"projectID": projectID,
- "signed": aws.BoolValue(signed),
- "approved": aws.BoolValue(approved),
"nextKey": aws.StringValue(nextKey),
"sortOrder": aws.StringValue(sortOrder),
"pageSize": aws.Int64Value(pageSize),
+ "approved": utils.BoolValue(approved),
+ "signed": utils.BoolValue(signed),
}
// These are the keys we want to match
@@ -1161,16 +1183,16 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
And(expression.Name("signature_reference_type").Equal(expression.Value("company")))
- // If the caller provided a signature signed value...add the appropriate filter
- if signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
- }
-
// If the caller provided a signature approved value...add the appropriate filter
if approved != nil {
log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
+ filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved))))
+ }
+
+ // If the caller provided a signature signed value...add the appropriate filter
+ if signed != nil {
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
+ filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed))))
}
limit := int64(10)
@@ -1989,11 +2011,11 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
log.WithFields(f).Debug("querying database for approval list details")
- signed, approved := true, true
+ approved, signed := true, true
pageSize := int64(10)
// Get CCLA signature - For Approval List info
- cclaSignature, err := repo.GetCorporateSignature(ctx, projectID, companyID)
+ cclaSignature, err := repo.GetCorporateSignature(ctx, projectID, companyID, &approved, &signed)
if err != nil || cclaSignature == nil {
msg := fmt.Sprintf("unable to get corporate signature for CLA Group: %s and company: %s", projectID, companyID)
log.WithFields(f).Warn(msg)
@@ -2132,7 +2154,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
go func() {
defer close(results)
for _, user := range userSearch.Users {
- icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, user.UserID)
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, user.UserID, &approved, &signed)
if iclaErr != nil || icla == nil {
results <- &ICLAUserResponse{
Error: fmt.Errorf("unable to get icla for user: %s ", user.UserID),
@@ -2208,7 +2230,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if params.RemoveDomainApprovalList != nil {
// Get ICLAs
log.WithFields(f).Debug("getting icla records... ")
- iclas, iclaErr := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil, 0, "")
+ iclas, iclaErr := repo.GetClaGroupICLASignatures(ctx, approvalList.ClaGroupID, nil, &approved, &signed, 0, "")
if iclaErr != nil {
log.WithFields(f).Warn("unable to get iclas")
}
@@ -2301,7 +2323,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
return
}
if claUser != nil {
- icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID)
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID, &approved, &signed)
if iclaErr != nil || icla == nil {
log.WithFields(f).Debugf("unable to get icla signature for user with ghUsername: %s ", ghUsername)
}
@@ -2428,7 +2450,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
// Query the CCLA signature once again to load the most recent updates which include approval list updates from above
- updatedSig, err := repo.GetCorporateSignature(ctx, projectID, companyID)
+ updatedSig, err := repo.GetCorporateSignature(ctx, projectID, companyID, &approved, &signed)
if err != nil || cclaSignature == nil {
msg := fmt.Sprintf("unable to get corporate signature for CLA Group: %s and company: %s", projectID, companyID)
log.WithFields(f).Warn(msg)
@@ -2862,21 +2884,28 @@ func (repo repository) AddSignedOn(ctx context.Context, signatureID string) erro
return nil
}
-func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
+func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetClaGroupICLASignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"searchTerm": utils.StringValue(searchTerm),
+ "approved": utils.BoolValue(approved),
+ "signed": utils.BoolValue(signed),
}
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ if approved != nil {
+ filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved))))
+ }
+ if signed != nil {
+ filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed))))
+ }
+
if searchTerm != nil {
log.WithFields(f).Debugf("adding search term filter for : %s ", *searchTerm)
filterAdded := true
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 3df3b867b..bfed7e6b6 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -33,11 +33,11 @@ import (
// SignatureService interface
type SignatureService interface {
GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
- GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error)
- GetCorporateSignature(ctx context.Context, claGroupID, companyID string) (*models.Signature, error)
+ GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error)
+ GetCorporateSignature(ctx context.Context, claGroupID, companyID string, approved, signed *bool) (*models.Signature, error)
GetProjectSignatures(ctx context.Context, params signatures.GetProjectSignaturesParams) (*models.Signatures, error)
CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error)
- GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
+ GetProjectCompanySignature(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, pageSize *int64) (*models.Signature, error)
GetProjectCompanySignatures(ctx context.Context, params signatures.GetProjectCompanySignaturesParams) (*models.Signatures, error)
GetProjectCompanyEmployeeSignatures(ctx context.Context, params signatures.GetProjectCompanyEmployeeSignaturesParams, criteria *ApprovalCriteria) (*models.Signatures, error)
GetCompanySignatures(ctx context.Context, params signatures.GetCompanySignaturesParams) (*models.Signatures, error)
@@ -53,8 +53,8 @@ type SignatureService interface {
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
RemoveCLAManager(ctx context.Context, ignatureID, claManagerID string) (*models.Signature, error)
- GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
- GetClaGroupCCLASignatures(ctx context.Context, claGroupID string) (*models.Signatures, error)
+ GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error)
+ GetClaGroupCCLASignatures(ctx context.Context, claGroupID string, approved, signed *bool) (*models.Signatures, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, searchTerm *string) (*models.CorporateContributorList, error)
}
@@ -83,13 +83,13 @@ func (s service) GetSignature(ctx context.Context, signatureID string) (*models.
}
// GetIndividualSignature returns the signature associated with the specified CLA Group and User ID
-func (s service) GetIndividualSignature(ctx context.Context, claGroupID, userID string) (*models.Signature, error) {
- return s.repo.GetIndividualSignature(ctx, claGroupID, userID)
+func (s service) GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error) {
+ return s.repo.GetIndividualSignature(ctx, claGroupID, userID, approved, signed)
}
// GetCorporateSignature returns the signature associated with the specified CLA Group and Company ID
-func (s service) GetCorporateSignature(ctx context.Context, claGroupID, companyID string) (*models.Signature, error) {
- return s.repo.GetCorporateSignature(ctx, claGroupID, companyID)
+func (s service) GetCorporateSignature(ctx context.Context, claGroupID, companyID string, approved, signed *bool) (*models.Signature, error) {
+ return s.repo.GetCorporateSignature(ctx, claGroupID, companyID, approved, signed)
}
// GetProjectSignatures returns the list of signatures associated with the specified project
@@ -115,8 +115,8 @@ func (s service) CreateProjectSummaryReport(ctx context.Context, params signatur
}
// GetProjectCompanySignature returns the signature associated with the specified project and company
-func (s service) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, signed, approved *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
- return s.repo.GetProjectCompanySignature(ctx, companyID, projectID, signed, approved, nextKey, pageSize)
+func (s service) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
+ return s.repo.GetProjectCompanySignature(ctx, companyID, projectID, approved, signed, nextKey, pageSize)
}
// GetProjectCompanySignatures returns the list of signatures associated with the specified project
@@ -790,16 +790,18 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
}
}
-func (s service) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
- return s.repo.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, pageSize, nextKey)
+func (s service) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
+ return s.repo.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, approved, signed, pageSize, nextKey)
}
-func (s service) GetClaGroupCCLASignatures(ctx context.Context, claGroupID string) (*models.Signatures, error) {
+func (s service) GetClaGroupCCLASignatures(ctx context.Context, claGroupID string, approved, signed *bool) (*models.Signatures, error) {
pageSize := utils.Int64(1000)
return s.repo.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{
ClaType: aws.String(utils.ClaTypeCCLA),
ProjectID: claGroupID,
PageSize: pageSize,
+ Approved: approved,
+ Signed: signed,
})
}
diff --git a/cla-backend-go/swagger/cla.v1.yaml b/cla-backend-go/swagger/cla.v1.yaml
index b291bb278..e9c8836e1 100644
--- a/cla-backend-go/swagger/cla.v1.yaml
+++ b/cla-backend-go/swagger/cla.v1.yaml
@@ -354,6 +354,8 @@ paths:
- $ref: '#/parameters/signatureType'
- $ref: '#/parameters/claType'
- $ref: '#/parameters/sortOrder'
+ - $ref: '#/parameters/approved'
+ - $ref: '#/parameters/signed'
responses:
'200':
description: 'Success'
@@ -392,6 +394,8 @@ paths:
- $ref: '#/parameters/signatureType'
- $ref: '#/parameters/claType'
- $ref: '#/parameters/sortOrder'
+ - $ref: '#/parameters/approved'
+ - $ref: '#/parameters/signed'
- name: body
in: body
schema:
@@ -2513,6 +2517,18 @@ parameters:
required: false
# UUID v4 regex
# pattern: '[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}'
+ approved:
+ name: approved
+ description: The signature approved query parameter. If set with a value of true, the query would return approved signatures. If set with a value of false, the query would return invalidated/disabled signatures.
+ in: query
+ type: boolean
+ required: false
+ signed:
+ name: signed
+ description: The signature signed query parameter. If set with a value of true, the query would return signed signatures. If set with a value of false, the query would return incomplete/unsigned signatures.
+ in: query
+ type: boolean
+ required: false
sortOrder:
name: sortOrder
description: The sort order - either asc or desc
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 30b40dc8f..002a18015 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1619,6 +1619,8 @@ paths:
- $ref: '#/parameters/sortOrder'
- $ref: '#/parameters/pageSize'
- $ref: '#/parameters/nextKey'
+ - $ref: '#/parameters/approved'
+ - $ref: '#/parameters/signed'
responses:
'200':
description: 'Success'
@@ -1641,7 +1643,7 @@ paths:
/cla-group/{claGroupID}/corporate-contributors:
get:
- summary: List corporate contributors for cla group
+ summary: List corporate contributors
description: Returns a list of corporate contributor for the CLA Group
operationId: listClaGroupCorporateContributors
parameters:
@@ -1763,6 +1765,8 @@ paths:
- $ref: '#/parameters/signatureType'
- $ref: '#/parameters/claType'
- $ref: '#/parameters/sortOrder'
+ - $ref: '#/parameters/signed'
+ - $ref: '#/parameters/approved'
responses:
'200':
description: 'Success'
@@ -1894,8 +1898,8 @@ paths:
# --------------------------------------------------------
/signatures/project/{claGroupID}/ccla/pdfs:
get:
- summary: Downloads all corporate CLAs for this project
- description: Downloads the corporate CLAs for this project
+ summary: Download corporate CLAs
+ description: Downloads all the corporate CLAs for this project
operationId: downloadProjectSignatureCCLAs
parameters:
- $ref: "#/parameters/x-request-id"
@@ -3105,7 +3109,7 @@ paths:
- gerrits
/cla-group/{claGroupID}/user/{userID}/icla:
put:
- summary: Invalidate ICLA record
+ summary: Invalidate ICLA record
description: Invalidates a given ICLA record for a user
operationId: invalidateICLA
parameters:
@@ -3770,6 +3774,18 @@ parameters:
required: false
# UUID v4 regex
# pattern: '[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}'
+ approved:
+ name: approved
+ description: The signature approved query parameter. If set with a value of true, the query would return approved signatures. If set with a value of false, the query would return invalidated/disabled signatures.
+ in: query
+ type: boolean
+ required: false
+ signed:
+ name: signed
+ description: The signature signed query parameter. If set with a value of true, the query would return signed signatures. If set with a value of false, the query would return incomplete/unsigned signatures.
+ in: query
+ type: boolean
+ required: false
sortOrder:
name: sortOrder
description: The sort order - either asc or desc
diff --git a/cla-backend-go/utils/auth_user.go b/cla-backend-go/utils/auth_user.go
index 471ffe32b..33af5fcaf 100644
--- a/cla-backend-go/utils/auth_user.go
+++ b/cla-backend-go/utils/auth_user.go
@@ -4,6 +4,9 @@
package utils
import (
+ "os"
+ "strconv"
+
"github.com/LF-Engineering/lfx-kit/auth"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/sirupsen/logrus"
@@ -24,5 +27,9 @@ func SetAuthUserProperties(authUser *auth.User, xUserName *string, xEmail *strin
authUser.Email = *xEmail
}
+ tracingEnabled, conversionErr := strconv.ParseBool(os.Getenv("USER_AUTH_TRACING"))
+ if conversionErr == nil && tracingEnabled {
+ log.WithFields(f).Debugf("Auth User: %+v", authUser)
+ }
log.WithFields(f).Debugf("set authuser x-username:%s and x-email:%s", authUser.UserName, authUser.Email)
}
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 73c0790c5..9a6b1bf8f 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -107,7 +107,7 @@ func Configure(api *operations.EasyclaAPI, service Service, projectClaGroupRepo
}
log.WithFields(f).Debug("checking permissions")
- if !utils.IsUserAuthorizedForOrganization(ctx, authUser, v2CompanyModel.CompanyExternalID, utils.ALLOW_ADMIN_SCOPE) {
+ if !utils.IsUserAuthorizedForOrganization(ctx, authUser, params.CompanySFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to CompanyGetCompanyByExternalIDHandler with Organization scope of %s",
authUser.UserName, v2CompanyModel.CompanyExternalID)
log.WithFields(f).Warn(msg)
diff --git a/cla-backend-go/v2/signatures/converters.go b/cla-backend-go/v2/signatures/converters.go
index fe87fc318..904735a2e 100644
--- a/cla-backend-go/v2/signatures/converters.go
+++ b/cla-backend-go/v2/signatures/converters.go
@@ -59,11 +59,11 @@ func iclaSigCsvLine(sig *v1Models.IclaSignature) string {
} else {
dateTime = t.Format("Jan 2,2006")
}
- return fmt.Sprintf("\n%s,%s,%s,%s,\"%s\"", sig.GithubUsername, sig.LfUsername, sig.UserName, sig.UserEmail, dateTime)
+ return fmt.Sprintf("\n%s,%s,%s,%s,\"%s\",%t,%t", sig.GithubUsername, sig.LfUsername, sig.UserName, sig.UserEmail, dateTime, sig.SignatureApproved, sig.SignatureSigned)
}
func cclaSigCsvHeader() string {
- return `Company Name,Signed,Approved,DomainApprovalList,EmailApprovalList,GitHubOrgApprovalList,GitHubUsernameApprovalList,Date Signed`
+ return `Company Name,Signed,Approved,DomainApprovalList,EmailApprovalList,GitHubOrgApprovalList,GitHubUsernameApprovalList,Date Signed,Approved,Signed`
}
func cclaSigCsvLine(sig *v1Models.Signature) string {
@@ -75,7 +75,7 @@ func cclaSigCsvLine(sig *v1Models.Signature) string {
} else {
dateTime = t.Format("Jan 2,2006")
}
- return fmt.Sprintf("\n\"%s\",%t,%t,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
+ return fmt.Sprintf("\n\"%s\",%t,%t,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",%t,%t",
sig.CompanyName,
sig.SignatureSigned,
sig.SignatureApproved,
@@ -83,5 +83,7 @@ func cclaSigCsvLine(sig *v1Models.Signature) string {
strings.Join(sig.EmailApprovalList, ","),
strings.Join(sig.GithubOrgApprovalList, ","),
strings.Join(sig.GithubUsernameApprovalList, ","),
- dateTime)
+ dateTime,
+ sig.SignatureApproved,
+ sig.SignatureApproved)
}
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index e9acbac6a..a09d1a252 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -369,6 +369,8 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"signatureType": params.SignatureType,
+ "approved": utils.BoolValue(params.Approved),
+ "signed": utils.BoolValue(params.Signed),
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
@@ -420,6 +422,8 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
SignatureType: params.SignatureType,
ClaType: params.ClaType,
SortOrder: params.SortOrder,
+ Approved: params.Approved,
+ Signed: params.Signed,
})
if err != nil {
msg := fmt.Sprintf("error retrieving project signatures for projectID: %s, error: %+v",
@@ -812,6 +816,8 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
"claGroupID": params.ClaGroupID,
"searchTerm": utils.StringValue(params.SearchTerm),
"sortOrder": utils.StringValue(params.SortOrder),
+ "approved": utils.BoolValue(params.Approved),
+ "signed": utils.BoolValue(params.Signed),
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
@@ -859,7 +865,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
nextKey = *params.NextKey
}
- results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm, pageSize, nextKey)
+ results, err := v2service.GetProjectIclaSignatures(ctx, params.ClaGroupID, params.SearchTerm, params.Approved, params.Signed, pageSize, nextKey)
if err != nil {
msg := fmt.Sprintf("problem loading ICLA signatures by CLA Group ID search term: %s", aws.StringValue(params.SearchTerm))
log.WithFields(f).WithError(err).Warn(msg)
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 252e9abd6..2cd4e629a 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -61,7 +61,7 @@ type Service interface {
GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error)
GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
- GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error)
+ GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error)
GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companySFID string, searchTerm *string) (*models.CorporateContributorList, error)
GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error)
@@ -140,11 +140,11 @@ func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGro
func (s service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
var b bytes.Buffer
- result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, nil, 0, "")
+ result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, nil, nil, nil, 0, "")
if err != nil {
return nil, err
}
- b.WriteString(`GitHub ID,LF_ID,Name,Email,Date Signed`)
+ b.WriteString(`GitHub ID,LF_ID,Name,Email,Date Signed,Approved,Signed`)
for _, sig := range result.List {
b.WriteString(iclaSigCsvLine(sig))
}
@@ -158,7 +158,7 @@ func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID str
"claGroupID": claGroupID,
}
log.WithFields(f).Debug("querying for CCLA signatures...")
- result, err := s.v1SignatureService.GetClaGroupCCLASignatures(ctx, claGroupID)
+ result, err := s.v1SignatureService.GetClaGroupCCLASignatures(ctx, claGroupID, nil, nil)
if err != nil {
log.WithFields(f).Warnf("error loading CCLA signatures for CLA group, error: %+v", err)
return nil, err
@@ -175,16 +175,18 @@ func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID str
return b.Bytes(), nil
}
-func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
+func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
"functionName": "v2.signatures.service.GetProjectIclaSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"searchTerm": utils.StringValue(searchTerm),
+ "approved": utils.BoolValue(approved),
+ "signed": utils.BoolValue(signed),
}
var out models.IclaSignatures
- result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, pageSize, nextKey)
+ result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, searchTerm, approved, signed, pageSize, nextKey)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load ICLA signatures using the specified search parameters")
return nil, err
@@ -309,7 +311,8 @@ func (s service) InvalidateICLA(ctx context.Context, claGroupID string, userID s
}
// Get signature record
log.WithFields(f).Debug("getting signature record ...")
- icla, iclaErr := s.v1SignatureService.GetIndividualSignature(ctx, claGroupID, userID)
+ approved, signed := true, true
+ icla, iclaErr := s.v1SignatureService.GetIndividualSignature(ctx, claGroupID, userID, &approved, &signed)
if iclaErr != nil {
log.WithFields(f).Debug("unable to get individual signature")
return iclaErr
From 46e127aa008a904333676ba1e5b9846162ee2bde Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 7 May 2021 13:10:57 -0700
Subject: [PATCH 0258/1276] Resolved Signature Query Issue (#2920)
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 98 +++++++++++--------
.../swagger/common/icla-signature.yaml | 2 +
.../swagger/common/signature-summary.yaml | 2 +
cla-backend-go/swagger/common/signature.yaml | 2 +
4 files changed, 62 insertions(+), 42 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 6b32a7932..4305033ea 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -452,6 +452,7 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
log.WithFields(f).Debug("querying signature for icla records ...")
+ var filterAdded bool
// These are the keys we want to match for an ICLA Signature with a given CLA Group and User ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(userID)))
@@ -459,16 +460,17 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
- // If the caller provided a signature signed value...add the appropriate filter
- if signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
- }
-
- // If the caller provided a signature approved value...add the appropriate filter
if approved != nil {
- log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ }
+ if signed != nil {
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
builder := expression.NewBuilder().
@@ -555,6 +557,7 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
"signatureSigned": utils.BoolValue(signed),
}
+ var filterAdded bool
// These are the keys we want to match for an CCLA Signature with a given CLA Group and Company ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
@@ -562,16 +565,17 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
And(expression.Name("signature_reference_type").Equal(expression.Value("company"))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
- // If the caller provided a signature signed value...add the appropriate filter
- if signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(*signed))))
- }
-
- // If the caller provided a signature approved value...add the appropriate filter
if approved != nil {
- log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(*approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ }
+ if signed != nil {
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
builder := expression.NewBuilder().
@@ -788,16 +792,17 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
}
- // If the caller provided a signature approved value...add the appropriate filter
if params.Approved != nil {
- log.WithFields(f).Debugf("adding signature_approved: %t filter", *params.Approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(params.Approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
-
- // If the caller provided a signature signed value...add the appropriate filter
if params.Signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *params.Signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(params.Signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if filterAdded {
@@ -994,16 +999,17 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
}
}
- // If the caller provided a signature approved value...add the appropriate filter
if params.Approved != nil {
- log.WithFields(f).Debugf("adding signature_approved: %t filter", *params.Approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(params.Approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
-
- // If the caller provided a signature signed value...add the appropriate filter
if params.Signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *params.Signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(params.Signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if len(params.Body) > 0 {
@@ -1176,6 +1182,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
"signed": utils.BoolValue(signed),
}
+ var filterAdded bool
// These are the keys we want to match
//condition := expression.Key("signature_project_id").Equal(expression.Value(projectID))
condition := expression.Key("signature_project_id").Equal(expression.Value(projectID)).
@@ -1183,16 +1190,17 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
And(expression.Name("signature_reference_type").Equal(expression.Value("company")))
- // If the caller provided a signature approved value...add the appropriate filter
if approved != nil {
- log.WithFields(f).Debugf("adding signature_approved: %t filter", *approved)
- filter = filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
-
- // If the caller provided a signature signed value...add the appropriate filter
if signed != nil {
- log.WithFields(f).Debugf("adding signature_signed: %t filter", *signed)
- filter = filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
limit := int64(10)
@@ -2894,21 +2902,27 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
"signed": utils.BoolValue(signed),
}
+ var filterAdded bool
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
if approved != nil {
- filter.And(expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
- filter.And(expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed))))
+ filterAdded = true
+ log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
+ filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if searchTerm != nil {
log.WithFields(f).Debugf("adding search term filter for : %s ", *searchTerm)
- filterAdded := true
searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*searchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*searchTerm)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
diff --git a/cla-backend-go/swagger/common/icla-signature.yaml b/cla-backend-go/swagger/common/icla-signature.yaml
index 4b4c627d9..58bde5232 100644
--- a/cla-backend-go/swagger/common/icla-signature.yaml
+++ b/cla-backend-go/swagger/common/icla-signature.yaml
@@ -42,10 +42,12 @@ properties:
type: boolean
description: the signature approved flag - true or false value
example: true
+ x-omitempty: false
signatureSigned:
type: boolean
description: the signature signed flag - true or false value
example: true
+ x-omitempty: false
signatureModified:
type: string
description: the signature modified created time
diff --git a/cla-backend-go/swagger/common/signature-summary.yaml b/cla-backend-go/swagger/common/signature-summary.yaml
index 9530bcfcd..69290d7bb 100644
--- a/cla-backend-go/swagger/common/signature-summary.yaml
+++ b/cla-backend-go/swagger/common/signature-summary.yaml
@@ -23,10 +23,12 @@ properties:
type: boolean
description: the signature signed flag - true or false value
example: true
+ x-omitempty: false
signatureApproved:
type: boolean
description: the signature approved flag - true or false value
example: true
+ x-omitempty: false
signatureReferenceType:
type: string
description: the signature reference type - either user or company
diff --git a/cla-backend-go/swagger/common/signature.yaml b/cla-backend-go/swagger/common/signature.yaml
index 5413f8a8b..0e61a9012 100644
--- a/cla-backend-go/swagger/common/signature.yaml
+++ b/cla-backend-go/swagger/common/signature.yaml
@@ -32,10 +32,12 @@ properties:
type: boolean
description: the signature signed flag - true or false value
example: true
+ x-omitempty: false
signatureApproved:
type: boolean
description: the signature approved flag - true or false value
example: true
+ x-omitempty: false
signatureReferenceType:
type: string
description: the signature reference type - either user or company
From 258c757925be5e9f68c80c5d8a1efba88a9477a9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 10 May 2021 15:31:38 -0700
Subject: [PATCH 0259/1276] Bump url-parse from 1.4.7 to 1.5.0 in
/cla-frontend-contributor-console/src (#2928)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.0.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.0)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/package.json | 2 +-
cla-frontend-contributor-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-contributor-console/src/package.json b/cla-frontend-contributor-console/src/package.json
index 505a2f79a..daf558756 100644
--- a/cla-frontend-contributor-console/src/package.json
+++ b/cla-frontend-contributor-console/src/package.json
@@ -51,7 +51,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.4.7",
+ "url-parse": "^1.5.0",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 377a3f2dc..4643accbf 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -4119,10 +4119,10 @@ url-join@^4.0.1:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
-url-parse@^1.4.7:
- version "1.4.7"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
- integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+url-parse@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
+ integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From ab0bf93ae5984a336986878a80f274ab209ded36 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 12 May 2021 15:43:43 -0700
Subject: [PATCH 0260/1276] Added cla-signature-query-default Feature Flag
(#2944)
- Added cla-signature-query-default parameter - A flag to indicate how
a default signature query should return data - show only 'active'
signatures or 'all' signatures when no other query signed/approved
params are provided
Signed-off-by: David Deal
---
cla-backend-go/config/config.go | 5 +++
cla-backend-go/config/ssm.go | 8 +++++
cla-backend-go/signatures/repository.go | 44 +++++++++++++++++++++++++
cla-backend-go/utils/constants.go | 10 ++++++
4 files changed, 67 insertions(+)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 704982d4e..2d3629674 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -29,6 +29,11 @@ type Config struct {
// EnableCLAServiceForParent is a configuration flag to indicate if we should set the enable_services=[CLA] attribute on the parent project object in the project service when a child project is associated with a CLA group. This determines the v2 project console experience/behavior."
EnableCLAServiceForParent bool `json:"enable_cla_service_for_parent"`
+ // SignatureQueryDefault is a flag to indicate how a default signature query should return data - show only 'active' signatures or 'all' signatures when no other query signed/approved params are provided
+ SignatureQueryDefault string `json:"signature_query_default"`
+ // SignatureQueryDefaultValue the default value for the SignatureQueryDefault configuration value
+ SignatureQueryDefaultValue string `json:"signature_query_default_value"`
+
// SFDC
// GitHub
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 19e5c358a..06d83a970 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -45,6 +45,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
"stage": stage,
}
config := Config{}
+ config.SignatureQueryDefaultValue = "all"
ssmClient := ssm.New(awsSession)
@@ -94,6 +95,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-lfx-metrics-report-sqs-url-%s", stage),
fmt.Sprintf("cla-lfx-metrics-report-enabled-%s", stage),
fmt.Sprintf("cla-enable-services-for-parent-%s", stage),
+ fmt.Sprintf("cla-signature-query-default-%s", stage),
fmt.Sprintf("cla-platform-api-gw-%s", stage),
}
@@ -221,6 +223,12 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
} else {
config.EnableCLAServiceForParent = boolVal
}
+ case fmt.Sprintf("cla-signature-query-default-%s", stage):
+ if resp.value == "" {
+ config.SignatureQueryDefault = config.SignatureQueryDefaultValue
+ } else {
+ config.SignatureQueryDefault = resp.value
+ }
}
}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 4305033ea..dbfea2614 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -12,6 +12,8 @@ import (
"sync"
"time"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/sirupsen/logrus"
@@ -473,6 +475,13 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
builder := expression.NewBuilder().
WithKeyCondition(condition).
WithFilter(filter).
@@ -578,6 +587,13 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
builder := expression.NewBuilder().
WithKeyCondition(condition).
WithFilter(filter).
@@ -805,6 +821,13 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
if filterAdded {
builder = builder.WithFilter(filter)
}
@@ -1012,6 +1035,13 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
if len(params.Body) > 0 {
// expression.Name("Color").In(expression.Value("red"), expression.Value("green"), expression.Value("blue"))
var referenceIDExpressions []expression.OperandBuilder
@@ -1203,6 +1233,13 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
limit := int64(10)
if pageSize != nil {
limit = *pageSize
@@ -2921,6 +2958,13 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
+ // If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
+ if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
+ filterAdded = true
+ filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ }
+
if searchTerm != nil {
log.WithFields(f).Debugf("adding search term filter for : %s ", *searchTerm)
searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*searchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*searchTerm)))
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index c91af9f7a..03c9b233f 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -167,3 +167,13 @@ const RemoveApprovals = "RemoveApprovals"
//GitHubUsernameCriteria represents criteria based on GH username
const GitHubUsernameCriteria = "GitHubUsername"
+
+// SignatureQueryDefaultAll the signature query default active value - A flag to indicate how a default signature
+// query should return data - show only 'active' signatures or 'all' signatures when no other query signed/approved
+// params are provided
+const SignatureQueryDefaultAll = "all"
+
+// SignatureQueryDefaultActive the signature query default active value - A flag to indicate how a default signature
+// query should return data - show only 'active' signatures or 'all' signatures when no other query signed/approved
+// params are provided
+const SignatureQueryDefaultActive = "active"
From f5ca3464c631a001b4e5ccef9367ce8bfbd4bc68 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 12 May 2021 21:28:07 -0700
Subject: [PATCH 0261/1276] Added Sort to Project Signature Query (#2945)
- Added default sort order, updated next key logic
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 173 ++++++++++++++++++------
1 file changed, 133 insertions(+), 40 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index dbfea2614..6f04abc07 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -5,6 +5,8 @@ package signatures
import (
"context"
+ "encoding/base64"
+ "encoding/json"
"errors"
"fmt"
"sort"
@@ -464,13 +466,13 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
if approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -478,6 +480,7 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -576,13 +579,13 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
if approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -590,6 +593,7 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -742,10 +746,8 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
"signed": utils.BoolValue(params.Signed),
}
- indexName := SignatureProjectIDIndex
- if params.SortOrder != nil && *params.SortOrder != "" {
- indexName = SignatureProjectDateIDIndex
- }
+ // Always sort by date
+ indexName := SignatureProjectDateIDIndex
realPageSize := int64(100)
if params.PageSize != nil && *params.PageSize > 0 {
@@ -782,7 +784,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
if params.SignatureType != nil {
- if params.SearchTerm != nil && (params.FullMatch != nil && !*params.FullMatch) {
+ if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" && (params.FullMatch != nil && !*params.FullMatch) {
indexName = SignatureProjectIDTypeIndex
condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(*params.SignatureType))))
} else {
@@ -797,12 +799,15 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
}
- if params.SearchTerm != nil {
+ if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
if *params.FullMatch {
indexName = SignatureReferenceSearchIndex
- condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(*params.SearchTerm))))
+ log.WithFields(f).Debugf("adding filter signature_reference_name_lower: %s", strings.ToLower(utils.StringValue(params.SearchTerm)))
+ condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SearchTerm)))))
} else {
- searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*params.SearchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*params.SearchTerm)))
+ log.WithFields(f).Debugf("adding filters signature_reference_name_lower: %s or user_email: %s", strings.ToLower(utils.StringValue(params.SearchTerm)), strings.ToLower(utils.StringValue(params.SearchTerm)))
+ searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
+ Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
}
@@ -810,13 +815,13 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
if params.Approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(params.Approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(params.Signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -824,6 +829,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -854,25 +860,35 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
f["indexName"] = indexName
- // If we have the next key, set the exclusive start key value
if params.NextKey != nil {
- log.WithFields(f).Debugf("received a nextKey, value: %s", *params.NextKey)
- // The primary key of the first item that this operation will evaluate.
- // and the query key (if not the same)
- queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
- "signature_id": {
- S: params.NextKey,
- },
- "signature_project_id": {
- S: ¶ms.ProjectID,
- },
+ queryInput.ExclusiveStartKey, err = decodeNextKey(*params.NextKey)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem decoding next key value")
+ return nil, err
}
- if params.FullMatch != nil && *params.FullMatch && params.SearchTerm != nil {
- queryInput.ExclusiveStartKey["signature_reference_name_lower"] = &dynamodb.AttributeValue{
- S: params.SearchTerm,
+ log.WithFields(f).Debugf("received a nextKey, value: %s - decoded: %+v", *params.NextKey, queryInput.ExclusiveStartKey)
+ }
+ /*
+ // If we have the next key, set the exclusive start key value
+ if params.NextKey != nil {
+ log.WithFields(f).Debugf("received a nextKey, value: %s", *params.NextKey)
+ // The primary key of the first item that this operation will evaluate.
+ // and the query key (if not the same)
+ queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
+ "signature_id": {
+ S: params.NextKey,
+ },
+ "signature_project_id": {
+ S: ¶ms.ProjectID,
+ },
+ }
+ if params.FullMatch != nil && utils.BoolValue(params.FullMatch) && params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
+ queryInput.ExclusiveStartKey["signature_reference_name_lower"] = &dynamodb.AttributeValue{
+ S: params.SearchTerm,
+ }
}
}
- }
+ */
sigs := make([]*models.Signature, 0)
var lastEvaluatedKey string
@@ -928,6 +944,16 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
lastEvaluatedKey = sigs[realPageSize-1].SignatureID
}
+ if len(lastEvaluatedKey) > 0 {
+ log.WithFields(f).Debug("building next key...")
+ encodedString, err := buildNextKey(indexName, sigs[len(sigs)-1])
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to build nextKey")
+ }
+ lastEvaluatedKey = encodedString
+ log.WithFields(f).Debugf("lastEvaluatedKey encoded is: %s", encodedString)
+ }
+
return &models.Signatures{
ProjectID: params.ProjectID,
ResultCount: int64(len(sigs)),
@@ -1011,12 +1037,13 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
}
}
- if params.SearchTerm != nil {
- if *params.FullMatch {
+ if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
+ if utils.BoolValue(params.FullMatch) {
indexName = SignatureReferenceSearchIndex
- condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(*params.SearchTerm))))
+ condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SearchTerm)))))
} else {
- searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*params.SearchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*params.SearchTerm)))
+ searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
+ Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
}
@@ -1024,13 +1051,13 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
if params.Approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(params.Approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(params.Signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -1038,6 +1065,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -1094,7 +1122,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
S: ¶ms.ProjectID,
},
}
- if params.FullMatch != nil && *params.FullMatch && params.SearchTerm != nil {
+ if params.FullMatch != nil && *params.FullMatch && params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
queryInput.ExclusiveStartKey["signature_reference_name_lower"] = &dynamodb.AttributeValue{
S: params.SearchTerm,
}
@@ -1222,13 +1250,13 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
if approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -1236,6 +1264,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -2947,13 +2976,13 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
if approved != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_approved filter: %t", aws.BoolValue(approved))
+ log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
- log.WithFields(f).Debugf("adding signature_signed filter: %t", aws.BoolValue(signed))
+ log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
}
@@ -2961,6 +2990,7 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
+ log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
@@ -3336,3 +3366,66 @@ func (repo repository) getGerritUsers(ctx context.Context, authUser *auth.User,
Error: nil,
}
}
+
+func buildNextKey(indexName string, signature *models.Signature) (string, error) {
+ nextKey := make(map[string]*dynamodb.AttributeValue)
+ nextKey["signature_id"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureID)}
+ switch indexName {
+ // TODO: review all these use-cases
+ case SignatureProjectIDIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ case SignatureProjectDateIDIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ nextKey["date_modified"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureModified)}
+ case SignatureProjectReferenceIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ nextKey["signature_reference_id"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureReferenceID)}
+ case SignatureProjectIDSigTypeSignedApprovedIDIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ nextKey["signature_signed_approved_id"] = &dynamodb.AttributeValue{S: aws.String(fmt.Sprintf("%t#%t", signature.SignatureSigned, signature.SignatureApproved))}
+ case SignatureProjectIDTypeIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ nextKey["signature_type"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureType)}
+ case SignatureReferenceIndex:
+ nextKey["signature_reference_id"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureReferenceID)}
+ case SignatureReferenceSearchIndex:
+ nextKey["signature_project_id"] = &dynamodb.AttributeValue{S: aws.String(signature.ProjectID)}
+ nextKey["signature_reference_name_lower"] = &dynamodb.AttributeValue{S: aws.String(signature.SignatureReferenceNameLower)}
+ }
+
+ return encodeNextKey(nextKey)
+}
+
+// encodeNextKey encodes the map as a string
+func encodeNextKey(in map[string]*dynamodb.AttributeValue) (string, error) {
+ if len(in) == 0 {
+ return "", nil
+ }
+ b, err := json.Marshal(in)
+ if err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(b), nil
+}
+
+// decodeNextKey decodes the next key value into a dynamodb attribute value
+func decodeNextKey(str string) (map[string]*dynamodb.AttributeValue, error) {
+ f := logrus.Fields{
+ "functionName": "v1.events.repository.decodeNextKey",
+ }
+
+ sDec, err := base64.StdEncoding.DecodeString(str)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error decoding string %s", str)
+ return nil, err
+ }
+
+ var m map[string]*dynamodb.AttributeValue
+ err = json.Unmarshal(sDec, &m)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling string after decoding: %s", sDec)
+ return nil, err
+ }
+
+ return m, nil
+}
From c6ad1a70a4f54ec28c69bcf5150b1231ae34c773 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 13 May 2021 08:35:57 -0700
Subject: [PATCH 0262/1276] Resolved #2938 Signatory Email Not Being Sent
(#2946)
- Removed logic that returns an error with CLA Signatory does not have
an LFID - it's not required. If they do then fine - we can assign the
cla-signatory role, but if it's missing then it's not a show-stopper.
Signed-off-by: David Deal
---
cla-backend-go/v2/sign/service.go | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 733166aa9..01d181491 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -415,7 +415,6 @@ func prepareUserForSigning(ctx context.Context, userEmail string, companySFID, p
"signedEntityName": signedEntityName,
}
- var ErrNotInOrg error
role := utils.CLASignatoryRole
log.WithFields(f).Debug("called")
usc := userService.GetClient()
@@ -438,20 +437,20 @@ func prepareUserForSigning(ctx context.Context, userEmail string, companySFID, p
// assign user role of cla signatory for this project
osc := organizationService.GetClient()
- // make user cla-signatory
+ // Attempt to assign the cla-signatory role
log.WithFields(f).Debugf("assigning user role of %s...", role)
err = osc.CreateOrgUserRoleOrgScopeProjectOrg(ctx, userEmail, projectSFID, companySFID, roleID)
if err != nil {
+ // Log the error - but assigning the cla-signatory role is not a requirement as most users do not have a LF Login - do not throw an error
if strings.Contains(err.Error(), "associated with some organization") {
msg := fmt.Sprintf("user: %s already associated with some organization", user.Username)
- ErrNotInOrg = errors.New(msg)
log.WithFields(f).WithError(err).Warn(msg)
- return ErrNotInOrg
- }
- // Ignore conflict - role has already been assigned, otherwise, return the error
- if _, ok := err.(*organizations.CreateOrgUsrRoleScopesConflict); !ok {
+ // return errors.New(msg)
+ } else if _, ok := err.(*organizations.CreateOrgUsrRoleScopesConflict); !ok {
+ log.WithFields(f).WithError(err).Warnf("assigning user role of %s failed - user already assigned the role: %v", role, err)
+ // return err
+ } else {
log.WithFields(f).WithError(err).Warnf("assigning user role of %s failed: %v", role, err)
- return err
}
}
From 3a5da0a0d518cff61d9d9c4c0a5840985a79ae42 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 May 2021 08:50:45 -0700
Subject: [PATCH 0263/1276] Bump url-parse from 1.4.7 to 1.5.0 in
/cla-frontend-project-console/src (#2927)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.0.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.0)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/package.json | 2 +-
cla-frontend-project-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-project-console/src/package.json b/cla-frontend-project-console/src/package.json
index 7fa182314..e5e27ee59 100644
--- a/cla-frontend-project-console/src/package.json
+++ b/cla-frontend-project-console/src/package.json
@@ -54,7 +54,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.4.7",
+ "url-parse": "^1.5.0",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 658895412..19f23f84b 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -4200,10 +4200,10 @@ url-join@^4.0.1:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
-url-parse@^1.4.7:
- version "1.4.7"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
- integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+url-parse@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
+ integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From a06f5761ffab9f1f8b08c5370b0a158987fdd991 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 May 2021 08:51:08 -0700
Subject: [PATCH 0264/1276] Bump hosted-git-info from 2.8.8 to 2.8.9 (#2935)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 246d24da5..0840af6c3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2451,9 +2451,9 @@ has@^1.0.3:
function-bind "^1.1.1"
hosted-git-info@^2.1.4:
- version "2.8.8"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
- integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-cache-semantics@3.8.1:
version "3.8.1"
From 5450a1d9883f68ff7ddfdd8dad62d9770a2bc497 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 May 2021 08:51:44 -0700
Subject: [PATCH 0265/1276] Bump lodash from 4.17.20 to 4.17.21 in
/cla-backend-go (#2937)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index e8b46aa70..1f3a8d7d4 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -3170,9 +3170,9 @@ lodash.union@^4.6.0:
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-ok@^0.1.1:
version "0.1.1"
From 1a75e4b534864492df362115e3bcc57a56817d54 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Tue, 18 May 2021 00:42:30 +0300
Subject: [PATCH 0266/1276] [Snyk] Security upgrade urllib3 from 1.25.8 to
1.25.9 (#2947)
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-URLLIB3-1014645
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index b6037b1df..308486186 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -53,7 +53,7 @@ six==1.13.0
soupsieve==1.9.5
termcolor==1.1.0
typed-ast==1.4.1
-urllib3==1.25.8
+urllib3==1.25.9
vintage==0.4.1
wcwidth==0.1.7
Werkzeug==0.15.5
From 1103c4a070c6289d71a1a14d9b71c4b30a17cf44 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 May 2021 16:07:40 -0700
Subject: [PATCH 0267/1276] Bump hosted-git-info from 2.8.8 to 2.8.9 in
/cla-frontend-contributor-console/edge (#2933)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/edge/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index 8c6b32458..7bf38acdc 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -1448,9 +1448,9 @@ home-or-tmp@^2.0.0:
os-tmpdir "^1.0.1"
hosted-git-info@^2.1.4:
- version "2.8.8"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
- integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-signature@~1.2.0:
version "1.2.0"
From 4f49ef714a0be7e2fb587585cb6ccb4d1a8e6b59 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 18 May 2021 14:27:08 -0700
Subject: [PATCH 0268/1276] Resolved [#2948] Signature Query for Project and
Company (#2950)
- Resolved issue with project and company query - issue occured when
more than 1 CCLA signature was on file, user specified limit 1, and the
next key was invalid. Ultimately, the issue was with the next key logic
- it was missing the signature_reference_id in the hash.
Signed-off-by: David Deal
---
cla-backend-go/signatures/repository.go | 61 ++++++++++---------------
1 file changed, 24 insertions(+), 37 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 6f04abc07..330b19186 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -868,27 +868,6 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
log.WithFields(f).Debugf("received a nextKey, value: %s - decoded: %+v", *params.NextKey, queryInput.ExclusiveStartKey)
}
- /*
- // If we have the next key, set the exclusive start key value
- if params.NextKey != nil {
- log.WithFields(f).Debugf("received a nextKey, value: %s", *params.NextKey)
- // The primary key of the first item that this operation will evaluate.
- // and the query key (if not the same)
- queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
- "signature_id": {
- S: params.NextKey,
- },
- "signature_project_id": {
- S: ¶ms.ProjectID,
- },
- }
- if params.FullMatch != nil && utils.BoolValue(params.FullMatch) && params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
- queryInput.ExclusiveStartKey["signature_reference_name_lower"] = &dynamodb.AttributeValue{
- S: params.SearchTerm,
- }
- }
- }
- */
sigs := make([]*models.Signature, 0)
var lastEvaluatedKey string
@@ -1283,6 +1262,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
return nil, err
}
+ indexName := SignatureProjectReferenceIndex
// Assemble the query input parameters
queryInput := &dynamodb.QueryInput{
ExpressionAttributeNames: expr.Names(),
@@ -1291,27 +1271,21 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
FilterExpression: expr.Filter(),
ProjectionExpression: expr.Projection(),
TableName: aws.String(repo.signatureTableName),
- IndexName: aws.String(SignatureProjectReferenceIndex), // Name of a secondary index to scan
+ IndexName: aws.String(indexName), // Name of a secondary index to scan
Limit: aws.Int64(limit),
- //IndexName: aws.String("project-signature-index"), // Name of a secondary index to scan
}
// If we have the next key, set the exclusive start key value
if nextKey != nil {
- log.WithFields(f).Debugf("Received a nextKey, value: %s", *nextKey)
- // The primary key of the first item that this operation will evaluate.
- // and the query key (if not the same)
- queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
- "signature_id": {
- S: nextKey,
- },
- "signature_project_id": {
- S: &projectID,
- },
+ queryInput.ExclusiveStartKey, err = decodeNextKey(*nextKey)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem decoding next key value")
+ return nil, err
}
+ log.WithFields(f).Debugf("received a nextKey, value: %s - decoded: %+v", *nextKey, queryInput.ExclusiveStartKey)
}
- var sigs []*models.Signature
+ sigs := make([]*models.Signature, 0)
var lastEvaluatedKey string
// Loop until we have all the records
@@ -1338,7 +1312,6 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
// Add to the signatures response model to the list
sigs = append(sigs, signatureList...)
- // log.WithFields(f).Debugf("LastEvaluatedKey: %+v", results.LastEvaluatedKey["signature_id"])
if results.LastEvaluatedKey["signature_id"] != nil {
lastEvaluatedKey = *results.LastEvaluatedKey["signature_id"].S
queryInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
@@ -1346,7 +1319,10 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
S: aws.String(lastEvaluatedKey),
},
"signature_project_id": {
- S: &projectID,
+ S: aws.String(projectID),
+ },
+ "signature_reference_id": {
+ S: aws.String(companyID),
},
}
} else {
@@ -1368,6 +1344,17 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
return nil, err
}
+ // Calculate the next key - this uses a compound key - need to encode it before sharing with the caller
+ if len(lastEvaluatedKey) > 0 {
+ log.WithFields(f).Debug("building next key...")
+ encodedString, err := buildNextKey(indexName, sigs[len(sigs)-1])
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to build nextKey")
+ }
+ lastEvaluatedKey = encodedString
+ log.WithFields(f).Debugf("lastEvaluatedKey encoded is: %s", encodedString)
+ }
+
// Meta-data for the response
totalCount := *describeTableResult.Table.ItemCount
@@ -1384,7 +1371,7 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
}, nil
}
-// Get project signatures with no pagination
+// ProjectSignatures - get project signatures with no pagination
func (repo repository) ProjectSignatures(ctx context.Context, projectID string) (*models.Signatures, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.ProjectSignatures",
From ca2b19872cdadb8926a1ecf8f9e6c888c889c783 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 19 May 2021 11:04:43 -0700
Subject: [PATCH 0269/1276] Resolved PCC-968 - Return 409 Conflict when
Duplicate CLA Name Occurs (#2951)
- Updated swagger spec to include 409 conflict response error
- Added logic to trap for conflict specific error and return 409
Signed-off-by: David Deal
---
cla-backend-go/swagger/cla.v2.yaml | 2 ++
cla-backend-go/v2/cla_groups/handlers.go | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 002a18015..7f2a2a7f3 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -411,6 +411,8 @@ paths:
$ref: '#/responses/forbidden'
'404':
$ref: '#/responses/not-found'
+ '409':
+ $ref: '#/responses/conflict'
'500':
$ref: '#/responses/internal-server-error'
tags:
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 9541a4a66..d07051c75 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -164,6 +164,11 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
claGroup, err := service.UpdateCLAGroup(ctx, authUser, claGroupModel, params.Body, utils.StringValue(params.XUSERNAME))
if err != nil {
+ // Return a 409 conflict if we have a duplicate name
+ if _, ok := err.(*utils.CLAGroupNameConflict); ok {
+ return cla_group.NewUpdateClaGroupConflict().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseConflictWithError(reqID, err.Error(), err))
+ }
log.WithFields(f).WithError(err).Warn("unable to update the CLA Group Name and/or Description - update failed")
return cla_group.NewUpdateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to update CLA Group by ID: %s", params.ClaGroupID), err))
From af7f1b7e01ceaa9d62157f3476bc74817a5970da Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 May 2021 11:36:49 -0700
Subject: [PATCH 0270/1276] Bump hosted-git-info from 2.7.1 to 2.8.9 in
/cla-frontend-corporate-console/src (#2930)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 00c9980c1..06bdb1339 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -1790,8 +1790,9 @@ hoek@6.x.x, hoek@^4.2.1:
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
hosted-git-info@^2.1.4:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-errors@1.7.2:
version "1.7.2"
From e360a862dfe990fe713172dcdc17b779ca3b6a58 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 May 2021 11:37:15 -0700
Subject: [PATCH 0271/1276] Bump hosted-git-info from 2.7.1 to 2.8.9 in
/cla-frontend-contributor-console/src (#2932)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 4643accbf..84c3ff46a 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -1820,8 +1820,9 @@ hoek@6.x.x, hoek@^4.2.1:
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
hosted-git-info@^2.1.4:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-errors@1.7.2:
version "1.7.2"
From 1da43c85c96f1c20a7d7f5932be85dacde7c5089 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 May 2021 11:37:35 -0700
Subject: [PATCH 0272/1276] Bump hosted-git-info from 2.7.1 to 2.8.9 in
/cla-frontend-project-console/src (#2931)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 19f23f84b..8ad50aaad 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -1843,8 +1843,9 @@ hoek@6.x.x, hoek@^4.2.1:
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
hosted-git-info@^2.1.4:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+ version "2.8.9"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-errors@1.7.2:
version "1.7.2"
From 1ebfb2769a3a50b28f2f681fca159370ab4f0db5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 May 2021 11:55:42 -0700
Subject: [PATCH 0273/1276] Bump url-parse from 1.4.7 to 1.5.0 in
/cla-frontend-corporate-console/src (#2926)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/package.json | 2 +-
cla-frontend-corporate-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-corporate-console/src/package.json b/cla-frontend-corporate-console/src/package.json
index ad099499c..184a0456b 100644
--- a/cla-frontend-corporate-console/src/package.json
+++ b/cla-frontend-corporate-console/src/package.json
@@ -50,7 +50,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.4.7",
+ "url-parse": "^1.5.0",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 06bdb1339..d387df25f 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -4051,10 +4051,10 @@ urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
-url-parse@^1.4.7:
- version "1.4.7"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
- integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+url-parse@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
+ integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From 176111ada3b4cd4dbaa17222db214024181dc380 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 May 2021 12:42:39 -0700
Subject: [PATCH 0274/1276] Bump py from 1.8.0 to 1.10.0 in /cla-backend
(#2889)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index 308486186..c0f65349f 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -31,7 +31,7 @@ nose2==0.9.1
oauthlib==3.1.0
packaging==19.2
pluggy==0.13.1
-py==1.8.0
+py==1.10.0
pyasn1==0.4.8
pydocusign==2.2
PyGithub==1.43.8
From ef0cce7c18e9b2781d690357cd01899f33520fd4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 19 May 2021 13:00:09 -0700
Subject: [PATCH 0275/1276] Resolved CCLA Query Issue (#2952)
---
cla-backend-go/signatures/repository.go | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 330b19186..d08661a7c 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -1192,7 +1192,7 @@ func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID
return nil, getErr
}
- if sigs == nil || sigs.Signatures == nil {
+ if sigs == nil || len(sigs.Signatures) == 0 {
return nil, nil
}
@@ -1300,17 +1300,20 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
}
log.WithFields(f).Debugf("query response received with %d results", len(results.Items))
- // Convert the list of DB models to a list of response models
- log.WithFields(f).Debugf("building response model for %d results", len(results.Items))
- signatureList, modelErr := repo.buildProjectSignatureModels(ctx, results, projectID, LoadACLDetails)
- if modelErr != nil {
- log.WithFields(f).Warnf("error converting DB model to response model for signatures with project %s with company: %s, error: %v",
- projectID, companyID, modelErr)
- return nil, modelErr
- }
+ // If we have any results - may not have any after filters are applied, but may have more records to page through...
+ if len(results.Items) > 0 {
+ // Convert the list of DB models to a list of response models
+ log.WithFields(f).Debugf("building response model for %d results", len(results.Items))
+ signatureList, modelErr := repo.buildProjectSignatureModels(ctx, results, projectID, LoadACLDetails)
+ if modelErr != nil {
+ log.WithFields(f).Warnf("error converting DB model to response model for signatures with project %s with company: %s, error: %v",
+ projectID, companyID, modelErr)
+ return nil, modelErr
+ }
- // Add to the signatures response model to the list
- sigs = append(sigs, signatureList...)
+ // Add to the signatures response model to the list
+ sigs = append(sigs, signatureList...)
+ }
if results.LastEvaluatedKey["signature_id"] != nil {
lastEvaluatedKey = *results.LastEvaluatedKey["signature_id"].S
From e84a6941005c3e5e9c7affcb712a1c1fd9671957 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 20 May 2021 19:43:38 +0300
Subject: [PATCH 0276/1276] [Snyk] Upgrade aws-sdk from 2.775.0 to 2.885.0
(#2911)
---
cla-landing-page/package.json | 2 +-
cla-landing-page/yarn.lock | 17 ++++++++++++++++-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/cla-landing-page/package.json b/cla-landing-page/package.json
index 6946820be..6966dabe5 100644
--- a/cla-landing-page/package.json
+++ b/cla-landing-page/package.json
@@ -52,7 +52,7 @@
"@types/node": "^12.11.1",
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
"auth0-js": "^9.13.2",
- "aws-sdk": "^2.733.0",
+ "aws-sdk": "^2.885.0",
"bootstrap": "^4.4.0",
"query-string": "^6.13.6",
"rxjs": "~6.6.7",
diff --git a/cla-landing-page/yarn.lock b/cla-landing-page/yarn.lock
index 377c1658b..06b3c8a96 100644
--- a/cla-landing-page/yarn.lock
+++ b/cla-landing-page/yarn.lock
@@ -2419,7 +2419,7 @@ autoprefixer@9.7.4:
postcss "^7.0.26"
postcss-value-parser "^4.0.2"
-aws-sdk@^2.224.1, aws-sdk@^2.733.0:
+aws-sdk@^2.224.1:
version "2.775.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.775.0.tgz#707c369e84b79cf3b136819c01f849b93fb44eab"
integrity sha512-rlej1sgHmfhl+PJqpQ2qOOsbHEEnLBIKBmanMTUNGiEAfuS0MpFjXECXTpJIOrbUzl3OZuAYrGuBkg2qrBwRbQ==
@@ -2449,6 +2449,21 @@ aws-sdk@^2.803.0:
uuid "3.3.2"
xml2js "0.4.19"
+aws-sdk@^2.885.0:
+ version "2.899.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.899.0.tgz#fac90b8926707b3e9dcd1e7d5937cfa71c0be5ca"
+ integrity sha512-k8jSANDQGvTyyj1f/7Hj4SWaV61/gjj/BopRmavAr6n1ayjXtUeVrV8G29+ABD3V82pHXDqLq47bqNmZ9m86xQ==
+ dependencies:
+ buffer "4.9.2"
+ events "1.1.1"
+ ieee754 "1.1.13"
+ jmespath "0.15.0"
+ querystring "0.2.0"
+ sax "1.2.1"
+ url "0.10.3"
+ uuid "3.3.2"
+ xml2js "0.4.19"
+
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
From 572d8678545faff9307eb18cfd2ccb1db9a0601b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 20 May 2021 14:09:41 -0700
Subject: [PATCH 0277/1276] Resolved 2955 - Fixed Contributor Console Typo
(#2956)
---
cla-backend-go/v2/company/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index b889276ad..1e44f218d 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1798,7 +1798,7 @@ func (s *service) RequestCompanyAdmin(ctx context.Context, userID string, claMan
return orgErr
}
if len(organizations.Data) > 0 {
- msg := fmt.Sprintf("Comapny already exist with the name: %s ", companyName)
+ msg := fmt.Sprintf("Company already exists with the name: %s ", companyName)
log.Warn(msg)
return errors.New(msg)
}
From 963090ca7f348ca92c25a6526fb189e9099698f3 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Fri, 21 May 2021 01:27:04 +0300
Subject: [PATCH 0278/1276] [Snyk] Upgrade graceful-fs from 4.2.4 to 4.2.6
(#2953)
---
cla-frontend-corporate-console/package.json | 2 +-
cla-frontend-corporate-console/yarn.lock | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-corporate-console/package.json b/cla-frontend-corporate-console/package.json
index 3316ac840..ff305e0af 100644
--- a/cla-frontend-corporate-console/package.json
+++ b/cla-frontend-corporate-console/package.json
@@ -13,7 +13,7 @@
"install-frontend": "../scripts/install-frontend.sh"
},
"dependencies": {
- "graceful-fs": "^4.2.2",
+ "graceful-fs": "^4.2.6",
"ionic": "^3.20.0",
"serverless": "^2.15.0",
"serverless-cloudfront-invalidate": "^1.2.1",
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 75f9ac8ad..208e1e46c 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -3055,11 +3055,16 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4:
+graceful-fs@^4.1.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.6:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
graphlib@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
From 73b1e47c798836ffa3a35480b5256e65b5e4a75b Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 21 May 2021 17:58:07 +0300
Subject: [PATCH 0279/1276] [#2954] Bug/Create CLA Group (#2958)
---
.../projects_cla_groups/repository.go | 53 ++++++++++++-------
1 file changed, 34 insertions(+), 19 deletions(-)
diff --git a/cla-backend-go/projects_cla_groups/repository.go b/cla-backend-go/projects_cla_groups/repository.go
index be33acecf..26467a4c6 100644
--- a/cla-backend-go/projects_cla_groups/repository.go
+++ b/cla-backend-go/projects_cla_groups/repository.go
@@ -248,22 +248,37 @@ func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID st
}
_, nowStr := utils.CurrentTime()
- input := &ProjectClaGroup{
- ProjectSFID: projectSFID,
- ProjectName: projectName,
- ClaGroupID: claGroupID,
- ClaGroupName: claGroupName,
- FoundationSFID: foundationSFID,
- FoundationName: foundationName,
- Note: fmt.Sprintf("Associate CLA Group with project API request on: %s", nowStr),
- Version: "v1",
- DateCreated: nowStr,
- DateModified: nowStr,
- }
-
- av, err := dynamodbattribute.MarshalMap(input)
- if err != nil {
- return err
+ item := map[string]*dynamodb.AttributeValue{
+ "project_sfid": {
+ S: aws.String(projectSFID),
+ },
+ "project_name": {
+ S: aws.String(projectName),
+ },
+ "cla_group_id": {
+ S: aws.String(claGroupID),
+ },
+ "cla_group_name": {
+ S: aws.String(claGroupName),
+ },
+ "foundation_sfid": {
+ S: aws.String(foundationSFID),
+ },
+ "foundation_name": {
+ S: aws.String(foundationName),
+ },
+ "note": {
+ S: aws.String(fmt.Sprintf("Associate CLA Group with project API request on: %s", nowStr)),
+ },
+ "version": {
+ S: aws.String("v1"),
+ },
+ "date_created": {
+ S: aws.String(nowStr),
+ },
+ "date_modified": {
+ S: aws.String(nowStr),
+ },
}
log.WithFields(f).Debug("Locating records with matching projectSFID...")
@@ -277,9 +292,9 @@ func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID st
log.WithFields(f).Debugf("record found with matching projectSFID: %+v", existingRecord)
}
- log.WithFields(f).Debugf("adding entry into the %s table with: %+v", repo.tableName, input)
- _, err = repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
- Item: av,
+ log.WithFields(f).Debugf("adding entry into the %s table with: %+v", repo.tableName, item)
+ _, err := repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
+ Item: item,
TableName: aws.String(repo.tableName),
ConditionExpression: aws.String("attribute_not_exists(project_sfid)"),
})
From 99602c53126c993d2620b9de03368c923260934d Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Fri, 21 May 2021 18:21:40 +0300
Subject: [PATCH 0280/1276] [Snyk] Upgrade graceful-fs from 4.2.4 to 4.2.6
(#2957)
---
cla-frontend-contributor-console/package.json | 2 +-
cla-frontend-contributor-console/yarn.lock | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-contributor-console/package.json b/cla-frontend-contributor-console/package.json
index 2a31baa5a..e55171d2f 100644
--- a/cla-frontend-contributor-console/package.json
+++ b/cla-frontend-contributor-console/package.json
@@ -13,7 +13,7 @@
"install-frontend": "../scripts/install-frontend.sh"
},
"dependencies": {
- "graceful-fs": "^4.2.2",
+ "graceful-fs": "^4.2.6",
"ionic": "^3.20.0",
"serverless": "^2.15.0",
"serverless-cloudfront-invalidate": "^1.2.1",
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index de6b5e402..014f2fb79 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -3075,11 +3075,16 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4:
+graceful-fs@^4.1.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.6:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
graphlib@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
From a1a2146372f514a8a745d22afedf10b95fd9e0ed Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Tue, 25 May 2021 20:02:18 +0300
Subject: [PATCH 0281/1276] [Snyk] Upgrade aws-sdk from 2.488.0 to 2.897.0
(#2959)
---
.../src/package.json | 2 +-
cla-frontend-corporate-console/src/yarn.lock | 28 +++++++++++--------
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/cla-frontend-corporate-console/src/package.json b/cla-frontend-corporate-console/src/package.json
index 184a0456b..f0996d309 100644
--- a/cla-frontend-corporate-console/src/package.json
+++ b/cla-frontend-corporate-console/src/package.json
@@ -38,7 +38,7 @@
"@swimlane/ngx-datatable": "^11.3.2",
"@types/lodash": "4.14.112",
"@types/node": "^8.0.17",
- "aws-sdk": "^2.304.0",
+ "aws-sdk": "^2.897.0",
"chart.js": "^2.5.0",
"google-libphonenumber": "^2.0.18",
"graceful-fs": "^4.2.2",
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index d387df25f..849d57044 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -394,13 +394,14 @@ autoprefixer@^7.2.6:
postcss "^6.0.17"
postcss-value-parser "^3.2.3"
-aws-sdk@^2.304.0:
- version "2.488.0"
- resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.488.0.tgz#1664e14743d93793d2ffc4b2e70b7ce19c008d84"
+aws-sdk@^2.897.0:
+ version "2.912.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.912.0.tgz#3f096b057ccc7b22ca234b44167be5c02a1daab3"
+ integrity sha512-ImeMEC014AB9DMlS3X25G+y1Q/jGDwh7kOY+5AE0PBOMjDrQOYjfiqVYbFU2nZx57Uk7UVFclOb3Lty3hEz2gg==
dependencies:
- buffer "4.9.1"
+ buffer "4.9.2"
events "1.1.1"
- ieee754 "1.1.8"
+ ieee754 "1.1.13"
jmespath "0.15.0"
querystring "0.2.0"
sax "1.2.1"
@@ -617,7 +618,16 @@ buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
-buffer@4.9.1, buffer@^4.3.0:
+buffer@4.9.2:
+ version "4.9.2"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+ integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+ isarray "^1.0.0"
+
+buffer@^4.3.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
dependencies:
@@ -1836,11 +1846,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-ieee754@1.1.8:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
-
-ieee754@^1.1.4:
+ieee754@1.1.13, ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
From b64b351c358ab6fb1d2ec63f51280c7b8a868e5e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:04:40 -0700
Subject: [PATCH 0282/1276] Bump lodash from 4.17.20 to 4.17.21 in /cla-backend
(#2929)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index eb2ab3c37..d672ee8df 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -3939,9 +3939,9 @@ lodash.values@^4.3.0:
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
lodash@4.17.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-ok@^0.1.1:
version "0.1.1"
From 4cbf45ae579081dd5d1d3a13f40de4285373be64 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:05:22 -0700
Subject: [PATCH 0283/1276] Bump lodash from 4.17.20 to 4.17.21 in
/cla-frontend-project-console (#2939)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-project-console/yarn.lock b/cla-frontend-project-console/yarn.lock
index 1f47c6f18..c334447b7 100644
--- a/cla-frontend-project-console/yarn.lock
+++ b/cla-frontend-project-console/yarn.lock
@@ -3979,9 +3979,9 @@ lodash.union@^4.6.0:
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-ok@^0.1.1:
version "0.1.1"
From 9691433b4bdaf6ea48f1992fa7179d9d568f62bc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:05:46 -0700
Subject: [PATCH 0284/1276] Bump lodash from 4.17.19 to 4.17.21 in
/cla-frontend-project-console/src (#2921)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/yarn.lock | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 8ad50aaad..dc655ea79 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -2381,15 +2381,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash@>=4.17.19, lodash@^4.17.15:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
-
-lodash@^4.0.0, lodash@^4.17.11, lodash@~4.17.10:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@>=4.17.19, lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.15, lodash@~4.17.10:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
longest@^1.0.1:
version "1.0.1"
From 9aa694f99c4b49d28bc3ff1534814e899d9b4486 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:06:10 -0700
Subject: [PATCH 0285/1276] Bump lodash from 4.17.19 to 4.17.21 in
/cla-frontend-contributor-console/edge (#2925)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/edge/yarn.lock | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index 7bf38acdc..d30e8eecf 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -1866,15 +1866,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash@^4.0.0, lodash@^4.17.15, lodash@~4.17.10:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
-
-lodash@^4.17.14, lodash@^4.17.4:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@~4.17.10:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
longest@^1.0.1:
version "1.0.1"
From 54e997bd69e799847b6af1caba04a34cf62d8afd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:06:34 -0700
Subject: [PATCH 0286/1276] Bump lodash from 4.17.20 to 4.17.21 in
/cla-frontend-contributor-console (#2924)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 014f2fb79..71ba0f0e7 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -4069,9 +4069,9 @@ lodash.union@^4.6.0:
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-ok@^0.1.1:
version "0.1.1"
From 8d8b1f7deee2fdc68d7c6d74066ea42cedf91eb5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:06:58 -0700
Subject: [PATCH 0287/1276] Bump lodash from 4.17.20 to 4.17.21 (#2934)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 0840af6c3..8e21dbb68 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3026,9 +3026,9 @@ lodash.union@^4.6.0:
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x, lodash@>=4.17.19, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log@^6.0.0:
version "6.0.0"
From e8793d7e5dda9d4f58041b3282105f6f0cd0588d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:07:24 -0700
Subject: [PATCH 0288/1276] Bump lodash from 4.17.20 to 4.17.21 in
/cla-frontend-corporate-console (#2936)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 208e1e46c..1fa89e32d 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -4030,9 +4030,9 @@ lodash.union@^4.6.0:
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-ok@^0.1.1:
version "0.1.1"
From b900980a506d815537b5e7a221265808b5670905 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:07:52 -0700
Subject: [PATCH 0289/1276] Bump lodash from 4.17.19 to 4.17.21 in
/cla-frontend-corporate-console/src (#2923)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/yarn.lock | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 849d57044..22ee0fefa 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -2281,15 +2281,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash@>=4.17.19, lodash@^4.17.15:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
-
-lodash@^4.0.0, lodash@^4.17.11, lodash@~4.17.10:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@>=4.17.19, lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.15, lodash@~4.17.10:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
longest@^1.0.1:
version "1.0.1"
From c1c8cc55a7ad442748e9aa4035998f1cc6dc5dc3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 10:08:17 -0700
Subject: [PATCH 0290/1276] Bump lodash from 4.17.19 to 4.17.21 in
/cla-frontend-contributor-console/src (#2922)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/yarn.lock | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 84c3ff46a..fb0203b33 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -2326,15 +2326,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash@>=4.17.19, lodash@^4.17.15:
- version "4.17.20"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
- integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
-
-lodash@^4.0.0, lodash@^4.17.11, lodash@~4.17.10:
- version "4.17.19"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
+lodash@>=4.17.19, lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.15, lodash@~4.17.10:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
longest@^1.0.1:
version "1.0.1"
From a13c428f79712fcbe928d2ecf81b479227d4444b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 12:29:24 -0700
Subject: [PATCH 0291/1276] Bump chart.js from 2.8.0 to 2.9.4 in
/cla-frontend-contributor-console/src (#2941)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/package.json | 2 +-
cla-frontend-contributor-console/src/yarn.lock | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/cla-frontend-contributor-console/src/package.json b/cla-frontend-contributor-console/src/package.json
index daf558756..a51c8c7d1 100644
--- a/cla-frontend-contributor-console/src/package.json
+++ b/cla-frontend-contributor-console/src/package.json
@@ -40,7 +40,7 @@
"@types/node": "^8.0.17",
"auth0-js": "^9.13.2",
"aws-sdk": "^2.304.0",
- "chart.js": "^2.5.0",
+ "chart.js": "^2.9.4",
"google-libphonenumber": "^2.0.18",
"graceful-fs": "^4.2.2",
"ionic-angular": "3.9.2",
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index fb0203b33..1d91d9e19 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -720,9 +720,10 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.0, chalk@^2.4.1:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chart.js@^2.5.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.8.0.tgz#b703b10d0f4ec5079eaefdcd6ca32dc8f826e0e9"
+chart.js@^2.9.4:
+ version "2.9.4"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
+ integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
From 77594ae51e3183a28eeb5be04a0c0ec509b64e50 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Wed, 26 May 2021 00:25:01 +0300
Subject: [PATCH 0292/1276] [Snyk] Upgrade graceful-fs from 4.2.4 to 4.2.6
(#2961)
---
cla-frontend-project-console/edge/package.json | 2 +-
cla-frontend-project-console/edge/yarn.lock | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-project-console/edge/package.json b/cla-frontend-project-console/edge/package.json
index fcf194d49..18de7a2b7 100644
--- a/cla-frontend-project-console/edge/package.json
+++ b/cla-frontend-project-console/edge/package.json
@@ -10,7 +10,7 @@
"test": "./node_modules/.bin/jasmine --config=jasmine.json"
},
"dependencies": {
- "graceful-fs": "^4.2.2"
+ "graceful-fs": "^4.2.6"
},
"devDependencies": {
"babel-core": "^6.26.0",
diff --git a/cla-frontend-project-console/edge/yarn.lock b/cla-frontend-project-console/edge/yarn.lock
index 76b68e1bf..3e897acfe 100644
--- a/cla-frontend-project-console/edge/yarn.lock
+++ b/cla-frontend-project-console/edge/yarn.lock
@@ -1094,11 +1094,16 @@ globals@^9.18.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.2:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.6:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
From cd9e2c8dff928328b5b02b0c993749f54c0e40f1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 25 May 2021 16:43:20 -0700
Subject: [PATCH 0293/1276] Added Sort/Most Recent to Preview (#2963)
---
cla-backend-go/approval_list/service.go | 2 +-
cla-backend-go/cmd/server.go | 55 ++++-----
cla-backend-go/emails/prefill.go | 6 +-
cla-backend-go/events/mockrepo.go | 2 +-
cla-backend-go/events/service.go | 4 +-
.../github_organizations/service.go | 4 +-
cla-backend-go/go.mod | 6 +-
cla-backend-go/go.sum | 39 +-----
cla-backend-go/project/repository.go | 2 +-
cla-backend-go/project/service.go | 28 ++---
.../projects_cla_groups/repository.go | 112 ++++++++++--------
cla-backend-go/projects_cla_groups/service.go | 95 +++++++++++++++
cla-backend-go/template/service.go | 97 +++++++++++++--
cla-backend-go/v2/cla_groups/handlers.go | 6 +-
cla-backend-go/v2/cla_groups/helpers.go | 12 +-
cla-backend-go/v2/cla_groups/service.go | 16 +--
cla-backend-go/v2/cla_manager/handlers.go | 6 +-
cla-backend-go/v2/cla_manager/service.go | 6 +-
cla-backend-go/v2/company/handlers.go | 4 +-
cla-backend-go/v2/company/service.go | 8 +-
cla-backend-go/v2/dynamo_events/autoenable.go | 6 +-
.../v2/dynamo_events/cla_groups_db_handler.go | 21 ++--
.../v2/dynamo_events/cla_manager.go | 4 +-
cla-backend-go/v2/dynamo_events/events.go | 2 +-
.../v2/dynamo_events/github_repository.go | 4 +-
.../v2/dynamo_events/projects_cla_groups.go | 2 +-
cla-backend-go/v2/dynamo_events/signatures.go | 6 +-
cla-backend-go/v2/events/handlers.go | 6 +-
cla-backend-go/v2/gerrits/handlers.go | 4 +-
.../v2/github_organizations/service.go | 2 +-
cla-backend-go/v2/metrics/handlers.go | 2 +-
cla-backend-go/v2/metrics/repository.go | 5 +-
cla-backend-go/v2/metrics/service.go | 9 +-
cla-backend-go/v2/project/service.go | 2 +-
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 4 +-
cla-backend-go/v2/signatures/handlers.go | 18 +--
cla-backend-go/v2/signatures/service.go | 2 +-
cla-backend-go/v2/template/handlers.go | 49 ++++++--
39 files changed, 423 insertions(+), 237 deletions(-)
create mode 100644 cla-backend-go/projects_cla_groups/service.go
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index 4bf7077c1..ae9ce32df 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -197,7 +197,7 @@ func (s service) ApproveCclaApprovalListRequest(ctx context.Context, claUser *us
// Get project cla Group records
log.WithFields(f).Debugf("Getting SalesForce Projects for claGroup: %s ", claGroupID)
- projectCLAGroups, getErr := s.projectsCLAGroupRepository.GetProjectsIdsForClaGroup(claGroupID)
+ projectCLAGroups, getErr := s.projectsCLAGroupRepository.GetProjectsIdsForClaGroup(ctx, claGroupID)
if getErr != nil {
msg := fmt.Sprintf("Error getting SF projects for claGroup: %s ", claGroupID)
log.Debug(msg)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index cbd9d8f58..5718fe743 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -237,9 +237,9 @@ func server(localMode bool) http.Handler {
approvalListRepo := approval_list.NewRepository(awsSession, stage)
v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
eventsRepo := events.NewRepository(awsSession, stage)
- projectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
- v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, projectClaGroupRepo)
- metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, projectClaGroupRepo)
+ v1ProjectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
+ v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, v1ProjectClaGroupRepo)
+ metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, v1ProjectClaGroupRepo)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
claManagerReqRepo := cla_manager.NewRepository(awsSession, stage)
@@ -248,7 +248,7 @@ func server(localMode bool) http.Handler {
usersRepo,
v1CompanyRepo,
v1CLAGroupRepo,
- projectClaGroupRepo,
+ v1ProjectClaGroupRepo,
})
gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{
@@ -270,31 +270,32 @@ func server(localMode bool) http.Handler {
organization_service.InitClient(configFile.PlatformAPIGatewayURL, eventsService)
acs_service.InitClient(configFile.PlatformAPIGatewayURL, configFile.AcsAPIKey)
+ v1ProjectClaGroupService := projects_cla_groups.NewService(v1ProjectClaGroupRepo)
usersService := users.NewService(usersRepo, eventsService)
healthService := health.New(Version, Commit, Branch, BuildDate)
templateService := template.NewService(stage, templateRepo, docraptorClient, awsSession)
- v1ProjectService := project.NewService(v1CLAGroupRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo)
- emailTemplateService := emails.NewEmailTemplateService(v1CLAGroupRepo, projectClaGroupRepo, v1ProjectService, configFile.CorporateConsoleV1URL, configFile.CorporateConsoleV2URL)
+ v1ProjectService := project.NewService(v1CLAGroupRepo, repositoriesRepo, gerritRepo, v1ProjectClaGroupRepo, usersRepo)
+ emailTemplateService := emails.NewEmailTemplateService(v1CLAGroupRepo, v1ProjectClaGroupRepo, v1ProjectService, configFile.CorporateConsoleV1URL, configFile.CorporateConsoleV2URL)
emailService := emails.NewService(emailTemplateService, v1ProjectService)
- v2ProjectService := v2Project.NewService(v1ProjectService, v1CLAGroupRepo, projectClaGroupRepo)
+ v2ProjectService := v2Project.NewService(v1ProjectService, v1CLAGroupRepo, v1ProjectClaGroupRepo)
v1CompanyService := v1Company.NewService(v1CompanyRepo, configFile.CorporateConsoleV1URL, userRepo, usersService)
- v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, v1CLAGroupRepo, usersRepo, v1CompanyRepo, projectClaGroupRepo, eventsService)
- v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, projectClaGroupRepo, v1CompanyService)
+ v2CompanyService := v2Company.NewService(v1CompanyService, signaturesRepo, v1CLAGroupRepo, usersRepo, v1CompanyRepo, v1ProjectClaGroupRepo, eventsService)
+ v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, v1ProjectClaGroupRepo, v1CompanyService)
v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
- v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, projectClaGroupRepo, signaturesRepo, usersService)
- v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, projectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
- v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo)
- v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, projectClaGroupRepo, githubOrganizationsRepo)
- v2ClaManagerService := v2ClaManager.NewService(emailTemplateService, v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, projectClaGroupRepo)
- v1ApprovalListService := approval_list.NewService(approvalListRepo, projectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, v1CLAGroupRepo, signaturesRepo, emailTemplateService, configFile.CorporateConsoleV2URL, http.DefaultClient)
+ v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, v1ProjectClaGroupRepo, signaturesRepo, usersService)
+ v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, v1ProjectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
+ v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, v1ProjectClaGroupRepo)
+ v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, v1ProjectClaGroupRepo, githubOrganizationsRepo)
+ v2ClaManagerService := v2ClaManager.NewService(emailTemplateService, v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, v1ProjectClaGroupRepo)
+ v1ApprovalListService := approval_list.NewService(approvalListRepo, v1ProjectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, v1CLAGroupRepo, signaturesRepo, emailTemplateService, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
- v2MetricsService := metrics.NewService(metricsRepo, projectClaGroupRepo)
- githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo)
- v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo, githubOrganizationsService)
- autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo, v1ProjectService)
+ v2MetricsService := metrics.NewService(metricsRepo, v1ProjectClaGroupRepo)
+ githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo)
+ v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo, githubOrganizationsService)
+ autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
- v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, projectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
+ v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, v1ProjectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
sessionStore, err := dynastore.New(dynastore.Path("/"), dynastore.HTTPOnly(), dynastore.TableName(configFile.SessionStoreTableName), dynastore.DynamoDB(dynamodb.New(awsSession)))
if err != nil {
@@ -314,10 +315,10 @@ func server(localMode bool) http.Handler {
health.Configure(api, healthService)
v2Health.Configure(v2API, healthService)
template.Configure(api, templateService, eventsService)
- v2Template.Configure(v2API, templateService, eventsService)
+ v2Template.Configure(v2API, templateService, v1ProjectClaGroupService, eventsService)
github.Configure(api, configFile.GitHub.ClientID, configFile.GitHub.ClientSecret, configFile.GitHub.AccessToken, sessionStore)
signatures.Configure(api, v1SignaturesService, sessionStore, eventsService)
- v2Signatures.Configure(v2API, v1ProjectService, v1CLAGroupRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, projectClaGroupRepo)
+ v2Signatures.Configure(v2API, v1ProjectService, v1CLAGroupRepo, v1CompanyService, v1SignaturesService, sessionStore, eventsService, v2SignatureService, v1ProjectClaGroupRepo)
approval_list.Configure(api, v1ApprovalListService, sessionStore, v1SignaturesService, eventsService)
v1Company.Configure(api, v1CompanyService, usersService, companyUserValidation, eventsService)
docs.Configure(api)
@@ -325,19 +326,19 @@ func server(localMode bool) http.Handler {
version.Configure(api, Version, Commit, Branch, BuildDate)
v2Version.Configure(v2API, Version, Commit, Branch, BuildDate)
events.Configure(api, eventsService)
- v2Events.Configure(v2API, eventsService, v1CompanyRepo, projectClaGroupRepo)
+ v2Events.Configure(v2API, eventsService, v1CompanyRepo, v1ProjectClaGroupRepo)
v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
- v2Gerrits.Configure(v2API, gerritService, v1ProjectService, eventsService, projectClaGroupRepo)
- v2Company.Configure(v2API, v2CompanyService, projectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleV1URL)
+ v2Gerrits.Configure(v2API, gerritService, v1ProjectService, eventsService, v1ProjectClaGroupRepo)
+ v2Company.Configure(v2API, v2CompanyService, v1ProjectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleV1URL)
cla_manager.Configure(api, v1ClaManagerService, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService)
- v2ClaManager.Configure(v2API, v2ClaManagerService, v1CompanyService, configFile.LFXPortalURL, configFile.CorporateConsoleV2URL, projectClaGroupRepo, userRepo)
+ v2ClaManager.Configure(v2API, v2ClaManagerService, v1CompanyService, configFile.LFXPortalURL, configFile.CorporateConsoleV2URL, v1ProjectClaGroupRepo, userRepo)
sign.Configure(v2API, v2SignService)
- cla_groups.Configure(v2API, v2ClaGroupService, v1ProjectService, projectClaGroupRepo, eventsService)
+ cla_groups.Configure(v2API, v2ClaGroupService, v1ProjectService, v1ProjectClaGroupRepo, eventsService)
v2GithubActivity.Configure(v2API, v2GithubActivityService)
userCreaterMiddleware := func(next http.Handler) http.Handler {
diff --git a/cla-backend-go/emails/prefill.go b/cla-backend-go/emails/prefill.go
index 507330f15..55c3dc494 100644
--- a/cla-backend-go/emails/prefill.go
+++ b/cla-backend-go/emails/prefill.go
@@ -53,7 +53,7 @@ func (s *emailTemplateServiceProvider) PrefillV2CLAProjectParams(projectSFIDs []
// keeping a cache so we can safe some of the remote svc calls
signedAtFoundationLevelCache := map[string]bool{}
for _, pSFID := range projectSFIDs {
- projectCLAGroup, err := s.repository.GetClaGroupIDForProject(pSFID)
+ projectCLAGroup, err := s.repository.GetClaGroupIDForProject(context.Background(), pSFID)
if err != nil {
return nil, fmt.Errorf("fetching project : %s failed: %v", pSFID, err)
}
@@ -104,7 +104,7 @@ func (s *emailTemplateServiceProvider) GetCLAGroupTemplateParamsFromCLAGroup(cla
}
func (s *emailTemplateServiceProvider) getV2CLAGroupTemplateParamsFromProjectSFID(projectSFID string) (CLAGroupTemplateParams, error) {
- projectCLAGroup, err := s.repository.GetClaGroupIDForProject(projectSFID)
+ projectCLAGroup, err := s.repository.GetClaGroupIDForProject(context.Background(), projectSFID)
if err != nil {
return CLAGroupTemplateParams{}, err
}
@@ -114,7 +114,7 @@ func (s *emailTemplateServiceProvider) getV2CLAGroupTemplateParamsFromProjectSFI
params.CorporateConsole = s.corporateConsoleV2
params.Version = projectCLAGroup.Version
- projects, err := s.repository.GetProjectsIdsForClaGroup(projectCLAGroup.ClaGroupID)
+ projects, err := s.repository.GetProjectsIdsForClaGroup(context.Background(), projectCLAGroup.ClaGroupID)
if err != nil {
return CLAGroupTemplateParams{}, fmt.Errorf("getProjectsIdsForClaGroup failed : %w", err)
}
diff --git a/cla-backend-go/events/mockrepo.go b/cla-backend-go/events/mockrepo.go
index e4a5def3f..61c647466 100644
--- a/cla-backend-go/events/mockrepo.go
+++ b/cla-backend-go/events/mockrepo.go
@@ -44,7 +44,7 @@ func (repo *mockRepository) GetClaGroupEvents(claGroupID string, nextKey *string
panic("implement me")
}
-func (repo *mockRepository) GetClaGroupIDForProject(projectSFID string) (*projects_cla_groups.ProjectClaGroup, error) {
+func (repo *mockRepository) GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*projects_cla_groups.ProjectClaGroup, error) {
return nil, nil
}
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 4cbb22885..2eb9fadf9 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -50,7 +50,7 @@ type CombinedRepo interface {
GetCompany(ctx context.Context, companyID string) (*models.Company, error)
GetUserByUserName(userName string, fullMatch bool) (*models.User, error)
GetUser(userID string) (*models.User, error)
- GetClaGroupIDForProject(projectSFID string) (*projects_cla_groups.ProjectClaGroup, error)
+ GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*projects_cla_groups.ProjectClaGroup, error)
}
type service struct {
@@ -207,7 +207,7 @@ func (s *service) loadCLAGroup(ctx context.Context, args *LogEventArgs) error {
args.CLAGroupName = claGroupModel.ProjectName
args.CLAGroupID = claGroupID
} else if args.ProjectSFID != "" {
- projectCLAGroupModel, projectCLAGroupErr := s.combinedRepo.GetClaGroupIDForProject(args.ProjectSFID)
+ projectCLAGroupModel, projectCLAGroupErr := s.combinedRepo.GetClaGroupIDForProject(ctx, args.ProjectSFID)
if projectCLAGroupErr != nil || projectCLAGroupModel == nil {
log.WithFields(f).WithError(projectCLAGroupErr).Warnf("failed to load project CLA Group mapping by SFID: %s", args.ProjectSFID)
return nil
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index 3fbdfb722..f434d64af 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -63,7 +63,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
// check if valid cla group id is passed
if input.AutoEnabledClaGroupID != "" {
- if _, err := s.claRepository.GetCLAGroupNameByID(input.AutoEnabledClaGroupID); err != nil {
+ if _, err := s.claRepository.GetCLAGroupNameByID(ctx, input.AutoEnabledClaGroupID); err != nil {
return nil, err
}
}
@@ -161,7 +161,7 @@ func (s service) GetGithubOrganizationByName(ctx context.Context, githubOrgName
func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
- if _, err := s.claRepository.GetCLAGroupNameByID(autoEnabledClaGroupID); err != nil {
+ if _, err := s.claRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
return err
}
}
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index a98bc3a35..106261197 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -8,8 +8,8 @@ replace github.com/awslabs/aws-lambda-go-api-proxy => github.com/LF-Engineering/
require (
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2
- github.com/LF-Engineering/lfx-kit v0.1.24
- github.com/LF-Engineering/lfx-models v0.6.42
+ github.com/LF-Engineering/lfx-kit v0.1.25
+ github.com/LF-Engineering/lfx-models v0.6.44
github.com/aws/aws-lambda-go v1.22.0
github.com/aws/aws-sdk-go v1.36.27
github.com/aymerick/raymond v2.0.2+incompatible
@@ -48,7 +48,7 @@ require (
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
- github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a
+ github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.3.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index d094865eb..7e1875a77 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -26,14 +26,10 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOC
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUamMO8SWS1m4ZKJGGeh9lT985U=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
-github.com/LF-Engineering/lfx-kit v0.1.22 h1:4tE1xTvu5CRWIokOo1waOfuB6vgaCpov5glhkdVzbAs=
-github.com/LF-Engineering/lfx-kit v0.1.22/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
-github.com/LF-Engineering/lfx-kit v0.1.24 h1:2lfmBMWWfOwU2XEOSP7keS/CqR5mj30YmJPv6fUiOvM=
-github.com/LF-Engineering/lfx-kit v0.1.24/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
-github.com/LF-Engineering/lfx-models v0.6.34 h1:K8al2aTq8nDm3qNmsTNAhZ1uDzfew/UymwbcW9gbDDs=
-github.com/LF-Engineering/lfx-models v0.6.34/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
-github.com/LF-Engineering/lfx-models v0.6.42 h1:PfvP/hmcV+OHftNrzclzvwGUv864xyNcp0Kz2HUA5C0=
-github.com/LF-Engineering/lfx-models v0.6.42/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
+github.com/LF-Engineering/lfx-kit v0.1.25 h1:Bb3Snc72ppBmbS5CMoLBGFg1Tt7ZhZktZLJpEYa80PY=
+github.com/LF-Engineering/lfx-kit v0.1.25/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
+github.com/LF-Engineering/lfx-models v0.6.44 h1:a4/6+Hc05caUCzd9eQnZIioZUhWxtgpfgVRuf/M2SRY=
+github.com/LF-Engineering/lfx-models v0.6.44/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
@@ -58,7 +54,6 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
@@ -91,19 +86,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/communitybridge/easycla v1.0.99 h1:PkmkMV7cLH2Q2YNSFiGGmlyrHBXVYdsWMwbXNuMAyqw=
-github.com/communitybridge/easycla v1.0.106 h1:NLYUZUZtp9DQ0dHEQkhz9h9EMzLRmuh9udsPZk8oLoQ=
-github.com/communitybridge/easycla v1.0.107 h1:dktHAji1yJ1nMEu54z4paPWOM4Q7A9rryc0OCADfAcY=
-github.com/communitybridge/easycla v1.0.117 h1:o+rdmcNgZeMQ/N8HV/d5apNIBrkYH7eyM9UUYnEzewo=
-github.com/communitybridge/easycla v1.0.118 h1:8yrsOQ+ENUFi4RFl1krRlIxc51lzZNutidR+yy2HwW0=
-github.com/communitybridge/easycla v1.0.123 h1:Lh5i/9aajrTYItxNpVCmi9T1yyIfnQIOk0tC2Wtslvk=
-github.com/communitybridge/easycla v1.0.133 h1:aJulQGLLRISCMsZcCP4aIE8xGtHoBNm/EmA00n3NYVA=
-github.com/communitybridge/easycla v1.0.135 h1:Dvn8jX+7BAnpmA+jvdK0n5ajWP8SoH5vvopt7whZDEU=
-github.com/communitybridge/easycla v1.0.145 h1:ikhBSsOeEL2u3/EoyDsufh/j3HkjfFTiXAk1d61GoS8=
-github.com/communitybridge/easycla v2.0.10+incompatible h1:6eRJ5fxrMxRZHBkg8piYo+zHTcSowMrP85nZXzp5mpA=
-github.com/communitybridge/easycla v2.0.16+incompatible h1:I0hEApDh4IvlwRPyHV1LOsSYlSPbqBsGszjSTHwkdak=
-github.com/communitybridge/easycla v2.0.19+incompatible h1:HLaNt3jGDXPh3Au+rW/HKbJNkQf3daboVIrP9G6WYQ4=
-github.com/communitybridge/easycla v2.0.29+incompatible h1:kEply0yEyhsflUFDo7DZ0XBpncNPnsdgy6zn6ujkReY=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
@@ -139,7 +121,6 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
github.com/fnproject/fdk-go v0.0.2/go.mod h1:9m+nEyku9SqJAVJQsfZOZBQzFkCs+jvmbZJhvgDX4ts=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -195,7 +176,6 @@ github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2e
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/runtime v0.19.15 h1:2GIefxs9Rx1vCDNghRtypRq+ig8KSLrjHbAYI/gCLCM=
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
github.com/go-openapi/runtime v0.19.19 h1:PCaQSqG0HiCgpekchPrHO9AEc5ZUaAclOUp9T3RSKoQ=
github.com/go-openapi/runtime v0.19.19/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
@@ -268,8 +248,6 @@ github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZC
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
-github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -283,7 +261,6 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
@@ -294,7 +271,6 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
@@ -326,7 +302,6 @@ github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmI
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -497,7 +472,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -610,7 +584,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
@@ -729,7 +702,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -775,12 +747,10 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -878,7 +848,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 24c9d31dd..9c69d9705 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -373,7 +373,7 @@ func (repo *repo) GetClaGroupByProjectSFID(ctx context.Context, projectSFID stri
"tableName": repo.claGroupTable}
log.WithFields(f).Debugf("loading project")
- claGroupProject, err := repo.projectClaGroupRepo.GetClaGroupIDForProject(projectSFID)
+ claGroupProject, err := repo.projectClaGroupRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).Warnf("error fetching CLA Group ID for project, error: %v", err)
return nil, err
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index 570e72639..3e98bca4d 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -42,25 +42,25 @@ type Service interface {
// service
type service struct {
- repo ProjectRepository
- repositoriesRepo repositories.Repository
- gerritRepo gerrits.Repository
- projectCGRepo projects_cla_groups.Repository
- usersRepo users.UserRepository
+ repo ProjectRepository
+ repositoriesRepo repositories.Repository
+ gerritRepo gerrits.Repository
+ projectCLAGroupRepo projects_cla_groups.Repository
+ usersRepo users.UserRepository
}
// NewService returns an instance of the project service
-func NewService(projectRepo ProjectRepository, repositoriesRepo repositories.Repository, gerritRepo gerrits.Repository, pcgRepo projects_cla_groups.Repository, usersRepo users.UserRepository) Service {
+func NewService(projectRepo ProjectRepository, repositoriesRepo repositories.Repository, gerritRepo gerrits.Repository, projectCLAGroupRepo projects_cla_groups.Repository, usersRepo users.UserRepository) Service {
return service{
- repo: projectRepo,
- repositoriesRepo: repositoriesRepo,
- gerritRepo: gerritRepo,
- projectCGRepo: pcgRepo,
- usersRepo: usersRepo,
+ repo: projectRepo,
+ repositoriesRepo: repositoriesRepo,
+ gerritRepo: gerritRepo,
+ projectCLAGroupRepo: projectCLAGroupRepo,
+ usersRepo: usersRepo,
}
}
-// CreateProject service method
+// CreateCLAGroup service method
func (s service) CreateCLAGroup(ctx context.Context, claGroupModel *models.ClaGroup) (*models.ClaGroup, error) {
return s.repo.CreateCLAGroup(ctx, claGroupModel)
}
@@ -70,7 +70,7 @@ func (s service) GetCLAGroups(ctx context.Context, params *project.GetProjectsPa
return s.repo.GetCLAGroups(ctx, params)
}
-// GetProjectByID service method
+// GetCLAGroupByID service method
func (s service) GetCLAGroupByID(ctx context.Context, claGroupID string) (*models.ClaGroup, error) {
f := logrus.Fields{
"functionName": "GetCLAGroupByID",
@@ -337,7 +337,7 @@ func (s service) SignedAtFoundationLevel(ctx context.Context, foundationSFID str
}
log.WithFields(f).Debug("querying foundation CLA Group entries...")
- entries, pcgErr := s.projectCGRepo.GetProjectsIdsForFoundation(foundationSFID)
+ entries, pcgErr := s.projectCLAGroupRepo.GetProjectsIdsForFoundation(ctx, foundationSFID)
if pcgErr != nil {
return false, pcgErr
}
diff --git a/cla-backend-go/projects_cla_groups/repository.go b/cla-backend-go/projects_cla_groups/repository.go
index 26467a4c6..55efa5b9d 100644
--- a/cla-backend-go/projects_cla_groups/repository.go
+++ b/cla-backend-go/projects_cla_groups/repository.go
@@ -4,6 +4,7 @@
package projects_cla_groups
import (
+ "context"
"errors"
"fmt"
"strconv"
@@ -42,19 +43,19 @@ var (
// Repository provides interface for interacting with project_cla_groups table
type Repository interface {
- GetClaGroupIDForProject(projectSFID string) (*ProjectClaGroup, error)
- GetProjectsIdsForClaGroup(claGroupID string) ([]*ProjectClaGroup, error)
- GetProjectsIdsForFoundation(foundationSFID string) ([]*ProjectClaGroup, error)
- GetProjectsIdsForAllFoundation() ([]*ProjectClaGroup, error)
- AssociateClaGroupWithProject(claGroupID string, projectSFID string, foundationSFID string) error
- RemoveProjectAssociatedWithClaGroup(claGroupID string, projectSFIDList []string, all bool) error
- GetCLAGroupNameByID(claGroupID string) (string, error)
- GetCLAGroup(claGroupID string) (*ProjectClaGroup, error)
-
- IsExistingFoundationLevelCLAGroup(foundationSFID string) (bool, error)
- IsAssociated(projectSFID string, claGroupID string) (bool, error)
- UpdateRepositoriesCount(projectSFID string, diff int64, reset bool) error
- UpdateClaGroupName(projectSFID string, claGroupName string) error
+ GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*ProjectClaGroup, error)
+ GetProjectsIdsForClaGroup(ctx context.Context, claGroupID string) ([]*ProjectClaGroup, error)
+ GetProjectsIdsForFoundation(ctx context.Context, foundationSFID string) ([]*ProjectClaGroup, error)
+ GetProjectsIdsForAllFoundation(ctx context.Context) ([]*ProjectClaGroup, error)
+ AssociateClaGroupWithProject(ctx context.Context, claGroupID string, projectSFID string, foundationSFID string) error
+ RemoveProjectAssociatedWithClaGroup(ctx context.Context, claGroupID string, projectSFIDList []string, all bool) error
+ GetCLAGroupNameByID(ctx context.Context, claGroupID string) (string, error)
+ GetCLAGroup(ctx context.Context, claGroupID string) (*ProjectClaGroup, error)
+
+ IsExistingFoundationLevelCLAGroup(ctx context.Context, foundationSFID string) (bool, error)
+ IsAssociated(ctx context.Context, projectSFID string, claGroupID string) (bool, error)
+ UpdateRepositoriesCount(ctx context.Context, projectSFID string, diff int64, reset bool) error
+ UpdateClaGroupName(ctx context.Context, projectSFID string, claGroupName string) error
}
type repo struct {
@@ -72,11 +73,12 @@ func NewRepository(awsSession *session.Session, stage string) Repository {
}
}
-func (repo *repo) queryClaGroupsProjects(keyCondition expression.KeyConditionBuilder, indexName *string) ([]*ProjectClaGroup, error) {
+func (repo *repo) queryClaGroupsProjects(ctx context.Context, keyCondition expression.KeyConditionBuilder, indexName *string) ([]*ProjectClaGroup, error) {
f := logrus.Fields{
- "functionName": "project_cla_groups.repository.queryClaGroupsProjects",
- "indexName": aws.StringValue(indexName),
- "keyCondition": fmt.Sprintf("%+v", keyCondition),
+ "functionName": "project_cla_groups.repository.queryClaGroupsProjects",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "indexName": aws.StringValue(indexName),
+ "keyCondition": fmt.Sprintf("%+v", keyCondition),
}
expr, err := expression.NewBuilder().WithKeyCondition(keyCondition).Build()
@@ -123,11 +125,12 @@ func (repo *repo) queryClaGroupsProjects(keyCondition expression.KeyConditionBui
}
// GetClaGroupIDForProject retrieves the CLA Group ID for the project
-func (repo *repo) GetClaGroupIDForProject(projectSFID string) (*ProjectClaGroup, error) {
+func (repo *repo) GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*ProjectClaGroup, error) {
f := logrus.Fields{
- "functionName": "project_cla_groups.repository.GetClaGroupIDForProject",
- "tableName": repo.tableName,
- "projectSFID": projectSFID,
+ "functionName": "project_cla_groups.repository.GetClaGroupIDForProject",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "tableName": repo.tableName,
+ "projectSFID": projectSFID,
}
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
@@ -147,7 +150,7 @@ func (repo *repo) GetClaGroupIDForProject(projectSFID string) (*ProjectClaGroup,
if len(result.Item) == 0 {
// Query by foundation sfid index returns multiple results
log.WithFields(f).Debug("no results querying by project SFID - checking if this is a foundation SFID")
- pcgs, foundationErr := repo.GetProjectsIdsForFoundation(projectSFID)
+ pcgs, foundationErr := repo.GetProjectsIdsForFoundation(ctx, projectSFID)
if foundationErr != nil {
log.WithFields(f).Warnf("unable to lookup CLA Group associated with project, error: %+v", foundationErr)
return nil, err
@@ -171,18 +174,23 @@ func (repo *repo) GetClaGroupIDForProject(projectSFID string) (*ProjectClaGroup,
return &out, nil
}
-func (repo *repo) GetProjectsIdsForClaGroup(claGroupID string) ([]*ProjectClaGroup, error) {
+func (repo *repo) GetProjectsIdsForClaGroup(ctx context.Context, claGroupID string) ([]*ProjectClaGroup, error) {
keyCondition := expression.Key("cla_group_id").Equal(expression.Value(claGroupID))
- return repo.queryClaGroupsProjects(keyCondition, aws.String(CLAGroupIDIndex))
+ return repo.queryClaGroupsProjects(ctx, keyCondition, aws.String(CLAGroupIDIndex))
}
-func (repo *repo) GetProjectsIdsForFoundation(foundationSFID string) ([]*ProjectClaGroup, error) {
+func (repo *repo) GetProjectsIdsForFoundation(ctx context.Context, foundationSFID string) ([]*ProjectClaGroup, error) {
keyCondition := expression.Key("foundation_sfid").Equal(expression.Value(foundationSFID))
- return repo.queryClaGroupsProjects(keyCondition, aws.String(FoundationSFIDIndex))
+ return repo.queryClaGroupsProjects(ctx, keyCondition, aws.String(FoundationSFIDIndex))
}
-func (repo *repo) GetProjectsIdsForAllFoundation() ([]*ProjectClaGroup, error) {
- f := logrus.Fields{"functionName": "project_cla_groups.repository.GetProjectsIdsForAllFoundation", "tableName": repo.tableName}
+func (repo *repo) GetProjectsIdsForAllFoundation(ctx context.Context) ([]*ProjectClaGroup, error) {
+ f := logrus.Fields{
+ "functionName": "project_cla_groups.repository.GetProjectsIdsForAllFoundation",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "tableName": repo.tableName,
+ }
+
scanInput := &dynamodb.ScanInput{
TableName: aws.String(repo.tableName),
}
@@ -210,9 +218,10 @@ func (repo *repo) GetProjectsIdsForAllFoundation() ([]*ProjectClaGroup, error) {
}
// AssociateClaGroupWithProject creates entry in db to track cla_group association with project/foundation
-func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID string, foundationSFID string) error {
+func (repo *repo) AssociateClaGroupWithProject(ctx context.Context, claGroupID string, projectSFID string, foundationSFID string) error {
f := logrus.Fields{
"functionName": "project_cla_groups.repository.AssociateClaGroupWithProject",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFID": projectSFID,
"foundationSFID": foundationSFID,
@@ -240,7 +249,7 @@ func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID st
}
// Lookup the CLA Group name/Project Name
- claGroupName, claGroupLookupErr := repo.GetCLAGroupNameByID(claGroupID)
+ claGroupName, claGroupLookupErr := repo.GetCLAGroupNameByID(ctx, claGroupID)
if claGroupLookupErr != nil {
claGroupName = NotDefined
log.Warnf("unable to lookup CLA Group/Project by ID, error: %+v - using '%s'",
@@ -282,7 +291,7 @@ func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID st
}
log.WithFields(f).Debug("Locating records with matching projectSFID...")
- existingRecord, lookupErr := repo.GetClaGroupIDForProject(projectSFID)
+ existingRecord, lookupErr := repo.GetClaGroupIDForProject(ctx, projectSFID)
if lookupErr != nil {
log.WithFields(f).Warnf("cannot lookup record by projectSFID, error: %+v", lookupErr)
}
@@ -313,14 +322,15 @@ func (repo *repo) AssociateClaGroupWithProject(claGroupID string, projectSFID st
}
// RemoveProjectAssociatedWithClaGroup removes all associated project with cla_group
-func (repo *repo) RemoveProjectAssociatedWithClaGroup(claGroupID string, projectSFIDList []string, all bool) error {
+func (repo *repo) RemoveProjectAssociatedWithClaGroup(ctx context.Context, claGroupID string, projectSFIDList []string, all bool) error {
f := logrus.Fields{
"functionName": "project_cla_groups.repository.RemoveProjectAssociatedWithClaGroup",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"projectSFIDList": projectSFIDList,
"all": all,
}
- list, err := repo.GetProjectsIdsForClaGroup(claGroupID)
+ list, err := repo.GetProjectsIdsForClaGroup(ctx, claGroupID)
if err != nil {
log.WithFields(f).Warnf("unable to fetch projects IDs for CLA Group, error: %+v", err)
return err
@@ -355,7 +365,7 @@ func (repo *repo) RemoveProjectAssociatedWithClaGroup(claGroupID string, project
}
// GetCLAGroupNameByID helper function to fetch the CLA Group name
-func (repo *repo) GetCLAGroupNameByID(claGroupID string) (string, error) {
+func (repo *repo) GetCLAGroupNameByID(ctx context.Context, claGroupID string) (string, error) {
tableName := fmt.Sprintf("cla-%s-projects", repo.stage)
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(tableName),
@@ -382,7 +392,7 @@ func (repo *repo) GetCLAGroupNameByID(claGroupID string) (string, error) {
}
// GetCLAGroup helper function to fetch the CLA Group
-func (repo *repo) GetCLAGroup(claGroupID string) (*ProjectClaGroup, error) {
+func (repo *repo) GetCLAGroup(ctx context.Context, claGroupID string) (*ProjectClaGroup, error) {
tableName := fmt.Sprintf("cla-%s-projects", repo.stage)
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(tableName),
@@ -409,16 +419,17 @@ func (repo *repo) GetCLAGroup(claGroupID string) (*ProjectClaGroup, error) {
}
// UpdateRepositoriesCount updates the repositories count
-func (repo *repo) UpdateRepositoriesCount(projectSFID string, diff int64, reset bool) error {
+func (repo *repo) UpdateRepositoriesCount(ctx context.Context, projectSFID string, diff int64, reset bool) error {
f := logrus.Fields{
- "functionName": "project_cla_groups.repository.UpdateRepositoriesCount",
- "projectSFID": projectSFID,
- "diff": diff,
- "reset": reset,
+ "functionName": "project_cla_groups.repository.UpdateRepositoriesCount",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "diff": diff,
+ "reset": reset,
}
// Check to see if we have an existing record
- existingProjectCLAGroupMapping, err := repo.GetClaGroupIDForProject(projectSFID)
+ existingProjectCLAGroupMapping, err := repo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to lookup existing project cla group mapping")
return err
@@ -472,15 +483,16 @@ func (repo *repo) UpdateRepositoriesCount(projectSFID string, diff int64, reset
}
// UpdateClaGroupName updates cla group name for given projectSFID
-func (repo *repo) UpdateClaGroupName(projectSFID string, claGroupName string) error {
+func (repo *repo) UpdateClaGroupName(ctx context.Context, projectSFID string, claGroupName string) error {
f := logrus.Fields{
- "functionName": "project_cla_groups.repository.UpdateClaGroupName",
- "projectSFID": projectSFID,
- "claGroupName": claGroupName,
+ "functionName": "project_cla_groups.repository.UpdateClaGroupName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "claGroupName": claGroupName,
}
// Check to see if we have an existing record
- existingProjectCLAGroupMapping, err := repo.GetClaGroupIDForProject(projectSFID)
+ existingProjectCLAGroupMapping, err := repo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to lookup existing project cla group mapping")
return err
@@ -528,8 +540,8 @@ func (repo *repo) UpdateClaGroupName(projectSFID string, claGroupName string) er
// IsExistingFoundationLevelCLAGroup is a query helper function to determine if the
// specified foundation SFID has an entry in the mapping table to signify that
// it's a foundation level CLA Group (foundationSFID == projectSFID)
-func (repo *repo) IsExistingFoundationLevelCLAGroup(foundationSFID string) (bool, error) {
- projectCLAGroupModels, err := repo.GetProjectsIdsForFoundation(foundationSFID)
+func (repo *repo) IsExistingFoundationLevelCLAGroup(ctx context.Context, foundationSFID string) (bool, error) {
+ projectCLAGroupModels, err := repo.GetProjectsIdsForFoundation(ctx, foundationSFID)
if err != nil {
return false, err
}
@@ -543,8 +555,8 @@ func (repo *repo) IsExistingFoundationLevelCLAGroup(foundationSFID string) (bool
return false, nil
}
-func (repo *repo) IsAssociated(projectSFID string, claGroupID string) (bool, error) {
- pmlist, err := repo.GetProjectsIdsForClaGroup(claGroupID)
+func (repo *repo) IsAssociated(ctx context.Context, projectSFID string, claGroupID string) (bool, error) {
+ pmlist, err := repo.GetProjectsIdsForClaGroup(ctx, claGroupID)
if err != nil {
return false, err
}
diff --git a/cla-backend-go/projects_cla_groups/service.go b/cla-backend-go/projects_cla_groups/service.go
new file mode 100644
index 000000000..d98f53b7f
--- /dev/null
+++ b/cla-backend-go/projects_cla_groups/service.go
@@ -0,0 +1,95 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package projects_cla_groups
+
+import "context"
+
+// ProjectCLAGroupsService interface
+type ProjectCLAGroupsService interface {
+ GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*ProjectClaGroup, error)
+ GetProjectsIdsForClaGroup(ctx context.Context, claGroupID string) ([]*ProjectClaGroup, error)
+ GetProjectsIdsForFoundation(ctx context.Context, foundationSFID string) ([]*ProjectClaGroup, error)
+ GetProjectsIdsForAllFoundation() ([]*ProjectClaGroup, error)
+ AssociateClaGroupWithProject(ctx context.Context, claGroupID string, projectSFID string, foundationSFID string) error
+ RemoveProjectAssociatedWithClaGroup(ctx context.Context, claGroupID string, projectSFIDList []string, all bool) error
+ GetCLAGroupNameByID(ctx context.Context, claGroupID string) (string, error)
+ GetCLAGroup(ctx context.Context, claGroupID string) (*ProjectClaGroup, error)
+
+ IsExistingFoundationLevelCLAGroup(ctx context.Context, foundationSFID string) (bool, error)
+ IsAssociated(ctx context.Context, projectSFID string, claGroupID string) (bool, error)
+ UpdateRepositoriesCount(ctx context.Context, projectSFID string, diff int64, reset bool) error
+ UpdateClaGroupName(ctx context.Context, projectSFID string, claGroupName string) error
+}
+
+// Service model
+type Service struct {
+ repo Repository
+}
+
+// NewService creates a new whitelist service
+func NewService(repo Repository) Service {
+ return Service{
+ repo,
+ }
+}
+
+// GetClaGroupIDForProject service method
+func (s Service) GetClaGroupIDForProject(ctx context.Context, projectSFID string) (*ProjectClaGroup, error) {
+ return s.repo.GetClaGroupIDForProject(ctx, projectSFID)
+}
+
+// GetProjectsIdsForClaGroup service method
+func (s Service) GetProjectsIdsForClaGroup(ctx context.Context, claGroupID string) ([]*ProjectClaGroup, error) {
+ return s.repo.GetProjectsIdsForClaGroup(ctx, claGroupID)
+}
+
+// GetProjectsIdsForFoundation service method
+func (s Service) GetProjectsIdsForFoundation(ctx context.Context, foundationSFID string) ([]*ProjectClaGroup, error) {
+ return s.repo.GetProjectsIdsForFoundation(ctx, foundationSFID)
+}
+
+// GetProjectsIdsForAllFoundation service method
+func (s Service) GetProjectsIdsForAllFoundation(ctx context.Context) ([]*ProjectClaGroup, error) {
+ return s.repo.GetProjectsIdsForAllFoundation(ctx)
+}
+
+// AssociateClaGroupWithProject service method
+func (s Service) AssociateClaGroupWithProject(ctx context.Context, claGroupID string, projectSFID string, foundationSFID string) error {
+ return s.repo.AssociateClaGroupWithProject(ctx, claGroupID, projectSFID, foundationSFID)
+}
+
+// RemoveProjectAssociatedWithClaGroup service method
+func (s Service) RemoveProjectAssociatedWithClaGroup(ctx context.Context, claGroupID string, projectSFIDList []string, all bool) error {
+ return s.repo.RemoveProjectAssociatedWithClaGroup(ctx, claGroupID, projectSFIDList, all)
+}
+
+// GetCLAGroupNameByID service method
+func (s Service) GetCLAGroupNameByID(ctx context.Context, claGroupID string) (string, error) {
+ return s.repo.GetCLAGroupNameByID(ctx, claGroupID)
+}
+
+// GetCLAGroup service method
+func (s Service) GetCLAGroup(ctx context.Context, claGroupID string) (*ProjectClaGroup, error) {
+ return s.repo.GetCLAGroup(ctx, claGroupID)
+}
+
+// IsExistingFoundationLevelCLAGroup service method
+func (s Service) IsExistingFoundationLevelCLAGroup(ctx context.Context, foundationSFID string) (bool, error) {
+ return s.repo.IsExistingFoundationLevelCLAGroup(ctx, foundationSFID)
+}
+
+// IsAssociated service method
+func (s Service) IsAssociated(ctx context.Context, projectSFID string, claGroupID string) (bool, error) {
+ return s.repo.IsAssociated(ctx, projectSFID, claGroupID)
+}
+
+// UpdateRepositoriesCount service method
+func (s Service) UpdateRepositoriesCount(ctx context.Context, projectSFID string, diff int64, reset bool) error {
+ return s.repo.UpdateRepositoriesCount(ctx, projectSFID, diff, reset)
+}
+
+// UpdateClaGroupName service method
+func (s Service) UpdateClaGroupName(ctx context.Context, projectSFID string, claGroupName string) error {
+ return s.repo.UpdateClaGroupName(ctx, projectSFID, claGroupName)
+}
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index d6ab26a50..1f5160b2e 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -9,7 +9,9 @@ import (
"fmt"
"io"
"io/ioutil"
+ "strconv"
"strings"
+ "time"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
@@ -42,16 +44,16 @@ type Service interface {
type service struct {
stage string // The AWS stage (dev, staging, prod)
templateRepo Repository
- docraptorClient docraptor.Client
+ docRaptorClient docraptor.Client
s3Client *s3manager.Uploader
}
// NewService API call
-func NewService(stage string, templateRepo Repository, docraptorClient docraptor.Client, awsSession *session.Session) service {
+func NewService(stage string, templateRepo Repository, docRaptorClient docraptor.Client, awsSession *session.Session) service {
return service{
stage: stage,
templateRepo: templateRepo,
- docraptorClient: docraptorClient,
+ docRaptorClient: docRaptorClient,
s3Client: s3manager.NewUploader(awsSession),
}
}
@@ -120,7 +122,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
return nil, errors.New("invalid value of template_for")
}
- pdf, err := s.docraptorClient.CreatePDF(templateHTML, templateFor)
+ pdf, err := s.docRaptorClient.CreatePDF(templateHTML, templateFor)
if err != nil {
return nil, err
}
@@ -133,7 +135,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
return ioutil.ReadAll(pdf)
}
-// CreateCLAGroupTemplate
+// CreateCLAGroupTemplate service method
func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error) {
f := logrus.Fields{
"functionName": "v1.template.service.CreateCLAGroupTemplate",
@@ -149,8 +151,6 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
return models.TemplatePdfs{}, err
}
- // Verify the caller is authorized for the project that owns this CLA Group
-
// Get Template
template, err := s.templateRepo.GetTemplate(claGroupFields.TemplateID)
if err != nil {
@@ -181,7 +181,7 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
// Invoke the go routine - any errors will be handled below
eg.Go(func() error {
log.WithFields(f).Debugf("Creating PDF for %s", claTypeICLA)
- iclaPdf, iclaErr := s.docraptorClient.CreatePDF(iclaTemplateHTML, claTypeICLA)
+ iclaPdf, iclaErr := s.docRaptorClient.CreatePDF(iclaTemplateHTML, claTypeICLA)
if iclaErr != nil {
log.WithFields(f).WithError(iclaErr).Warn("Problem generating ICLA template via docraptor client - returning empty template PDFs")
return err
@@ -208,7 +208,7 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
// Invoke the go routine - any errors will be handled below
eg.Go(func() error {
log.WithFields(f).Debugf("Creating PDF for %s", claTypeCCLA)
- cclaPdf, cclaErr := s.docraptorClient.CreatePDF(cclaTemplateHTML, claTypeCCLA)
+ cclaPdf, cclaErr := s.docRaptorClient.CreatePDF(cclaTemplateHTML, claTypeCCLA)
if cclaErr != nil {
log.WithFields(f).WithError(cclaErr).Warn("Problem generating CCLA template via docraptor client - returning empty template PDFs")
return err
@@ -317,7 +317,7 @@ func (s service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType
return nil, err
}
- doc := claGroupDocuments[0]
+ doc := getLatestDocument(ctx, claGroupDocuments)
pdfS3URL := doc.DocumentS3URL
if pdfS3URL == "" {
err = fmt.Errorf("s3 url is empty for groupID : %s and document %s", claGroupID, doc.DocumentFileID)
@@ -357,7 +357,82 @@ func (s service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType
return b, nil
}
-// InjectProjectInformationIntoTemplate
+func getLatestDocument(ctx context.Context, documents []models.ClaGroupDocument) *models.ClaGroupDocument {
+ f := logrus.Fields{
+ "functionName": "v1.template.service.getLatestDocument",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+ var latestDocument *models.ClaGroupDocument
+ var latestMajorVersion = 0
+ var latestMinorVersion = 0
+ var latestDateTime time.Time
+ for _, currentDocument := range documents {
+ if latestDocument == nil {
+ latestDocument = ¤tDocument // nolint
+ // Grab and save the major version
+ major, convertErr := strconv.Atoi(latestDocument.DocumentMajorVersion)
+ if convertErr != nil {
+ log.WithFields(f).WithError(convertErr).Warnf("problem converting document major version to int: %s", latestDocument.DocumentMajorVersion)
+ major = 0
+ }
+ latestMajorVersion = major
+
+ // Grab and save the major version
+ minor, convertErr := strconv.Atoi(latestDocument.DocumentMinorVersion)
+ if convertErr != nil {
+ log.WithFields(f).WithError(convertErr).Warnf("problem converting document minor version to int: %s", latestDocument.DocumentMinorVersion)
+ minor = 0
+ }
+ latestMinorVersion = minor
+
+ dateTime, dateTimeErr := utils.ParseDateTime(latestDocument.DocumentCreationDate)
+ if dateTimeErr != nil {
+ log.WithFields(f).WithError(dateTimeErr).Warnf("problem converting document creation date to time object: %s", latestDocument.DocumentCreationDate)
+ }
+ latestDateTime = dateTime
+
+ continue
+ }
+
+ // Grab and save the major version
+ major, convertErr := strconv.Atoi(currentDocument.DocumentMajorVersion)
+ if convertErr != nil {
+ log.WithFields(f).WithError(convertErr).Warnf("problem converting document major version to int: %s", currentDocument.DocumentMajorVersion)
+ major = 0
+ }
+
+ // Grab and save the major version
+ minor, convertErr := strconv.Atoi(currentDocument.DocumentMinorVersion)
+ if convertErr != nil {
+ log.WithFields(f).WithError(convertErr).Warnf("problem converting document minor version to int: %s", currentDocument.DocumentMinorVersion)
+ minor = 0
+ }
+
+ dateTime, dateTimeErr := utils.ParseDateTime(currentDocument.DocumentCreationDate)
+ if dateTimeErr != nil {
+ log.WithFields(f).WithError(dateTimeErr).Warnf("problem converting document creation date to time object: %s", currentDocument.DocumentCreationDate)
+ }
+
+ if major > latestMajorVersion {
+ latestDocument = ¤tDocument // nolint
+ continue
+ }
+
+ if minor > latestMinorVersion {
+ latestDocument = ¤tDocument // nolint
+ continue
+ }
+
+ if dateTime.After(latestDateTime) {
+ latestDocument = ¤tDocument // nolint
+ continue
+ }
+ }
+
+ return latestDocument
+}
+
+// InjectProjectInformationIntoTemplate service function
func (s service) InjectProjectInformationIntoTemplate(template models.Template, metaFields []*models.MetaField) (string, string, error) {
f := logrus.Fields{
"functionName": "v1.template.service.InjectProjectInformationIntoTemplate",
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index d07051c75..288044ae9 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -128,7 +128,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
utils.ErrorResponseBadRequest(reqID, "no new values passed, nothing to change, aborting."))
}
- projectCLAGroupModels, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ projectCLAGroupModels, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
if projectCLAGroupErr != nil {
msg := fmt.Sprintf("unable to load the Project to CLA Group mappings for CLA Group: %s - is this CLA Group configured?", params.ClaGroupID)
log.WithFields(f).Warn(msg)
@@ -531,7 +531,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, pare
log.WithFields(f).Debugf("user does not have access any of the provided project SFID: %s", projectSFIDs)
log.WithFields(f).Debug("user doesn't have direct access to the parentProjectSFID or the provided projects SFIDs - loading CLA Group from project id...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(parentProjectSFID)
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, parentProjectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
return false
@@ -555,7 +555,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, pare
// Lookup the other project IDs for the CLA Group
log.WithFields(f).Debug("looking up other projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(projectCLAGroupModel.ClaGroupID)
+ projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectCLAGroupModel.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - returning false")
return false
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 2506e7897..8e8189363 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -87,7 +87,7 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
// Look up any existing configuration with this foundation SFID in our database...
log.WithFields(f).Debug("loading existing project IDs by foundation SFID...")
- claGroupProjectModels, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(foundationSFID)
+ claGroupProjectModels, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, foundationSFID)
if lookupErr != nil {
log.WithFields(f).Warnf("problem looking up foundation level CLA group using foundation ID: %s, error: %+v", foundationSFID, lookupErr)
return false, lookupErr
@@ -249,7 +249,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
}
// check if projects are not already enabled
- enabledProjects, err := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(foundationSFID)
+ enabledProjects, err := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, foundationSFID)
if err != nil {
return err
}
@@ -357,7 +357,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
}
// check if projects are already enrolled/enabled
- enabledProjects, err := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(foundationSFID)
+ enabledProjects, err := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, foundationSFID)
if err != nil {
return err
}
@@ -407,11 +407,11 @@ func (s *service) AssociateCLAGroupWithProjects(ctx context.Context, request *As
go func(projectSFID, parentProjectSFID, claGroupID string) {
defer wg.Done()
log.WithFields(f).Debugf("associating cla_group with project: %s", projectSFID)
- err := s.projectsClaGroupsRepo.AssociateClaGroupWithProject(claGroupID, projectSFID, parentProjectSFID)
+ err := s.projectsClaGroupsRepo.AssociateClaGroupWithProject(ctx, claGroupID, projectSFID, parentProjectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("associating cla_group with project: %s failed", projectSFID)
log.WithFields(f).Debug("deleting stale entries from cla_group project association")
- deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(claGroupID, request.ProjectSFIDList, false)
+ deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(ctx, claGroupID, request.ProjectSFIDList, false)
if deleteErr != nil {
log.WithFields(f).WithError(deleteErr).Warn("deleting stale entries from cla_group project association failed")
}
@@ -454,7 +454,7 @@ func (s *service) UnassociateCLAGroupWithProjects(ctx context.Context, request *
"projectSFIDList": strings.Join(request.ProjectSFIDList, ","),
}
- deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(request.CLAGroupID, request.ProjectSFIDList, false)
+ deleteErr := s.projectsClaGroupsRepo.RemoveProjectAssociatedWithClaGroup(ctx, request.CLAGroupID, request.ProjectSFIDList, false)
if deleteErr != nil {
log.WithFields(f).Warnf("problem disassociating projects with CLA Group, error: %+v", deleteErr)
return deleteErr
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index d5aa44c65..aef0ee9ec 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -193,7 +193,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
}
// Build the response model
- subProjectList, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(claGroup.ProjectID)
+ subProjectList, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, claGroup.ProjectID)
if err != nil {
return nil, err
}
@@ -289,7 +289,7 @@ func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGr
}
// Load the project IDs for this CLA Group
- subProjectList, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(claGroupModel.ProjectID)
+ subProjectList, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, claGroupModel.ProjectID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem getting project IDs for CLA Group")
return nil, err
@@ -530,7 +530,7 @@ func (s *service) buildClaGroupSummaryResponseModel(ctx context.Context, f logru
}
// How many SF projects are associated with this CLA Group?
- cgprojects, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(v1ClaGroup.ProjectID)
+ cgprojects, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, v1ClaGroup.ProjectID)
if err != nil {
return nil, &utils.ProjectCLAGroupMappingNotFound{CLAGroupID: v1ClaGroup.ProjectID, Err: err}
}
@@ -566,7 +566,7 @@ func (s *service) buildClaGroupSummaryResponseModel(ctx context.Context, f logru
func (s *service) appendCLAGroupsForFoundation(ctx context.Context, f logrus.Fields, projectOrFoundationSFID string, v1ClaGroups *v1Models.ClaGroups) error {
log.WithFields(f).Debug("found 'project group' in platform project service. Locating CLA Groups for foundation...")
- projectCLAGroupMappings, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectOrFoundationSFID)
+ projectCLAGroupMappings, lookupErr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, projectOrFoundationSFID)
if lookupErr != nil {
log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
return &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
@@ -640,7 +640,7 @@ func (s *service) appendCLAGroupsForProject(ctx context.Context, f logrus.Fields
}
log.WithFields(f).Debug("locating CLA Group mapping...")
- projectCLAGroup, lookupErr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectOrFoundationSFID)
+ projectCLAGroup, lookupErr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, projectOrFoundationSFID)
if lookupErr != nil {
log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
return "", "", &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
@@ -679,9 +679,9 @@ func (s *service) ListAllFoundationClaGroups(ctx context.Context, foundationID *
var out []*projects_cla_groups.ProjectClaGroup
var err error
if foundationID != nil {
- out, err = s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(*foundationID)
+ out, err = s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, utils.StringValue(foundationID))
} else {
- out, err = s.projectsClaGroupsRepo.GetProjectsIdsForAllFoundation()
+ out, err = s.projectsClaGroupsRepo.GetProjectsIdsForAllFoundation(ctx)
}
if err != nil {
return nil, err
@@ -708,7 +708,7 @@ func (s *service) DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.Cl
oscClient := organization_service.GetClient()
// Get a list of project CLA Group entries - need to know which SF Projects we're dealing with...
- projectCLAGroupEntries, projErr := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(claGroupModel.ProjectID)
+ projectCLAGroupEntries, projErr := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, claGroupModel.ProjectID)
if projErr != nil {
log.WithFields(f).Warnf("unable to fetch project IDs for CLA Group, error: %+v", projErr)
return projErr
diff --git a/cla-backend-go/v2/cla_manager/handlers.go b/cla-backend-go/v2/cla_manager/handlers.go
index a86b48f58..8cfd93bca 100644
--- a/cla-backend-go/v2/cla_manager/handlers.go
+++ b/cla-backend-go/v2/cla_manager/handlers.go
@@ -68,7 +68,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
}
log.WithFields(f).Debug("looking up CLA Group for projectSFID...")
- cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if err != nil {
if err == projects_cla_groups.ErrProjectNotAssociatedWithClaGroup {
msg := fmt.Sprintf("no CLA Group associated with this project: %s", params.ProjectSFID)
@@ -119,7 +119,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
return cla_manager.NewDeleteCLAManagerBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ cginfo, err := projectClaGroupRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if err != nil {
msg := fmt.Sprintf("no CLA Group associated with this project: %s", params.ProjectSFID)
log.WithFields(f).WithError(err).Warn(msg)
@@ -190,7 +190,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyService v1C
log.WithFields(f).Debugf("processing CLA Manager Designee by group request")
log.WithFields(f).Debugf("getting project IDs for CLA group")
- projectCLAGroups, getErr := projectClaGroupRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ projectCLAGroups, getErr := projectClaGroupRepo.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
if getErr != nil {
msg := fmt.Sprintf("error getting SF projects for claGroup: %s ", params.ClaGroupID)
log.WithFields(f).WithError(getErr).Warn(msg)
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index aa755b1d2..c8e6e5a25 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -446,7 +446,7 @@ func (s *service) IsCLAManagerDesignee(ctx context.Context, companySFID, claGrou
}
log.WithFields(f).Debugf("Getting project sf mappings for claGroupID: %s ", claGroupID)
- pcgs, pcgErr := s.projectCGRepo.GetProjectsIdsForClaGroup(claGroupID)
+ pcgs, pcgErr := s.projectCGRepo.GetProjectsIdsForClaGroup(ctx, claGroupID)
if pcgErr != nil {
log.WithFields(f).Warnf("Problem getting mappings for claGroup: %s , error: %+v ", claGroupID, pcgErr)
return nil, pcgErr
@@ -890,7 +890,7 @@ func (s *service) InviteCompanyAdmin(ctx context.Context, contactAdmin bool, com
// Get project cla Group records
log.WithFields(f).Debugf("Getting SalesForce Projects for claGroup: %s ", projectID)
- projectCLAGroups, getErr := s.projectCGRepo.GetProjectsIdsForClaGroup(projectID)
+ projectCLAGroups, getErr := s.projectCGRepo.GetProjectsIdsForClaGroup(ctx, projectID)
if getErr != nil {
msg := fmt.Sprintf("Error getting SF projects for claGroup: %s ", projectID)
log.Debug(msg)
@@ -1191,7 +1191,7 @@ func (s *service) NotifyCLAManagers(ctx context.Context, notifyCLAManagers *mode
// Get mappings
var projectSFIDs []string
- pcgs, pcgErr := s.projectCGRepo.GetProjectsIdsForClaGroup(notifyCLAManagers.ClaGroupID)
+ pcgs, pcgErr := s.projectCGRepo.GetProjectsIdsForClaGroup(ctx, notifyCLAManagers.ClaGroupID)
if pcgErr != nil {
log.WithFields(f).Warnf("problem getting cla_group_mappings by claGroupID: %s ", notifyCLAManagers.ClaGroupID)
return pcgErr
diff --git a/cla-backend-go/v2/company/handlers.go b/cla-backend-go/v2/company/handlers.go
index 9a6b1bf8f..408021e2b 100644
--- a/cla-backend-go/v2/company/handlers.go
+++ b/cla-backend-go/v2/company/handlers.go
@@ -761,7 +761,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// other projects or the parent project group/foundation
log.WithFields(f).Debug("user doesn't have direct access to the project only, project + organization, or organization only - loading CLA Group from project id...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
return false
@@ -798,7 +798,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Lookup the other project IDs associated with this CLA Group
log.WithFields(f).Debug("looking up other projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(projectCLAGroupModel.ClaGroupID)
+ projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectCLAGroupModel.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - returning false")
return false
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 1e44f218d..63b0547c3 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1194,20 +1194,20 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, proj
// Determine query index (foundation or project)
if !utils.IsProjectCategory(projectDetails, parentProjectDetails) {
// get all projects for all cla group under foundation
- allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForFoundation(projectSFID)
+ allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForFoundation(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get project IDs for foundation SFID: %s", projectSFID)
return nil, err
}
} else {
// get cla group id from project
- projectMapping, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ projectMapping, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if perr != nil {
log.WithFields(f).WithError(perr).Warnf("unable to get CLA group IDs for project SFID: %s", projectSFID)
return nil, err
}
// get all projects for that cla group
- allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForClaGroup(projectMapping.ClaGroupID)
+ allProjectMapping, err = s.projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectMapping.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get project IDs for CLA Group: %s", projectMapping.ClaGroupID)
return nil, err
@@ -1609,7 +1609,7 @@ func (s *service) getCompanyAndClaGroup(ctx context.Context, companyID, projectS
t := time.Now()
var pm *projects_cla_groups.ProjectClaGroup
log.WithFields(f).Debugf("loading CLA Group by project SFID: %s", projectSFID)
- pm, projectErr := s.projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ pm, projectErr := s.projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if projectErr != nil {
log.WithFields(f).Debugf("cla group mapping not found for projectSFID %s", projectSFID)
// Return the result through the channel
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index c7bc874bd..79a7fabac 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -65,16 +65,16 @@ type autoEnableServiceProvider struct {
}
func (a *autoEnableServiceProvider) CreateAutoEnabledRepository(repo *github.Repository) (*models.GithubRepository, error) {
+ ctx := utils.NewContext()
repositoryFullName := *repo.FullName
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
-
f := logrus.Fields{
"functionName": "handleRepositoryAddedAction",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryFullName": repositoryFullName,
}
organizationName := strings.Split(repositoryFullName, "/")[0]
- ctx := context.Background()
orgModel, err := a.githubOrgRepo.GetGithubOrganization(ctx, organizationName)
if err != nil {
log.Warnf("fetching github org failed : %v", err)
@@ -106,7 +106,7 @@ func (a *autoEnableServiceProvider) CreateAutoEnabledRepository(repo *github.Rep
return nil, listErr
}
}
- claGroupModel, err := a.claRepository.GetCLAGroup(claGroupID)
+ claGroupModel, err := a.claRepository.GetCLAGroup(ctx, claGroupID)
if err != nil {
log.Warnf("fetching the cla group for cla group id : %s failed : %v", claGroupID, err)
return nil, err
diff --git a/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go b/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
index f83ef6d99..9a90182ec 100644
--- a/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
+++ b/cla-backend-go/v2/dynamo_events/cla_groups_db_handler.go
@@ -7,18 +7,21 @@ import (
"github.com/aws/aws-lambda-go/events"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
)
func (s *service) ProcessCLAGroupUpdateEvents(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
f := logrus.Fields{
- "functionName": "ProcessCLAGroupUpdateEvents",
- "eventID": event.EventID,
- "eventName": event.EventName,
- "eventSource": event.EventSource,
- "event": event,
- "newImage": event.Change.NewImage,
- "oldImage": event.Change.OldImage,
+ "functionName": "ProcessCLAGroupUpdateEvents",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "eventID": event.EventID,
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ "event": event,
+ "newImage": event.Change.NewImage,
+ "oldImage": event.Change.OldImage,
}
log.WithFields(f).Debug("processing event")
@@ -52,14 +55,14 @@ func (s *service) ProcessCLAGroupUpdateEvents(event events.DynamoDBEventRecord)
}
if oldProject.ProjectName != updatedProject.ProjectName {
- claProjects, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(updatedProject.ProjectID)
+ claProjects, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, updatedProject.ProjectID)
if err != nil {
log.WithFields(f).Warnf("unabled to update cla group name : %v", err)
return nil
}
for _, claProject := range claProjects {
- if err := s.projectsClaGroupRepo.UpdateClaGroupName(claProject.ProjectSFID, updatedProject.ProjectName); err != nil {
+ if err := s.projectsClaGroupRepo.UpdateClaGroupName(ctx, claProject.ProjectSFID, updatedProject.ProjectName); err != nil {
log.WithFields(f).Warnf("updating cla project : %s with name : %s failed : %v", claProject.ProjectSFID, updatedProject.ProjectName, err)
return nil
}
diff --git a/cla-backend-go/v2/dynamo_events/cla_manager.go b/cla-backend-go/v2/dynamo_events/cla_manager.go
index 917e418cb..bc17275c9 100644
--- a/cla-backend-go/v2/dynamo_events/cla_manager.go
+++ b/cla-backend-go/v2/dynamo_events/cla_manager.go
@@ -19,7 +19,7 @@ import (
"github.com/sirupsen/logrus"
)
-// SetInitialCLAManagerACSPermissions
+// SetInitialCLAManagerACSPermissions establishes the initial CLA manager permissions
func (s *service) SetInitialCLAManagerACSPermissions(ctx context.Context, signatureID string) error {
f := logrus.Fields{
"functionName": "SetInitialCLAManagerACSPermissions",
@@ -124,7 +124,7 @@ func (s *service) SetInitialCLAManagerACSPermissions(ctx context.Context, signat
// fetch list of projects under cla group
log.WithFields(f).Debug("locating SF projects associated with the CLA Group...")
- projectList, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(sig.ProjectID)
+ projectList, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, sig.ProjectID)
if err != nil {
log.WithFields(f).Warnf("unable to fetch list of projects associated with CLA Group: %s, error: %+v",
sig.ProjectID, err)
diff --git a/cla-backend-go/v2/dynamo_events/events.go b/cla-backend-go/v2/dynamo_events/events.go
index 094aa24a3..6fd6b910e 100644
--- a/cla-backend-go/v2/dynamo_events/events.go
+++ b/cla-backend-go/v2/dynamo_events/events.go
@@ -34,7 +34,7 @@ func (s *service) EventAddedEvent(event events.DynamoDBEventRecord) error {
} else {
companySFID = companyModel.CompanyExternalID
}
- pmList, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(newEvent.EventProjectID)
+ pmList, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, newEvent.EventProjectID)
if err != nil || len(pmList) == 0 {
log.WithFields(f).Error("unable to get project mapping detail", err)
} else {
diff --git a/cla-backend-go/v2/dynamo_events/github_repository.go b/cla-backend-go/v2/dynamo_events/github_repository.go
index abc27f7d7..068e93284 100644
--- a/cla-backend-go/v2/dynamo_events/github_repository.go
+++ b/cla-backend-go/v2/dynamo_events/github_repository.go
@@ -16,7 +16,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
-// GithubRepoModifyEvent github repository modify event
+// GithubRepoModifyAddEvent github repository modify add event
func (s *service) GithubRepoModifyAddEvent(event events.DynamoDBEventRecord) error {
ctx := utils.NewContext()
f := logrus.Fields{
@@ -201,7 +201,7 @@ func (s *service) setRepositoryCount(ctx context.Context, claGroupID string, par
// Update projects-cla-group table
log.WithFields(f).Debugf("Updating the projects-cla-groups-table for projectSFID: %s ", projectSFID)
- pcgErr := s.projectsClaGroupRepo.UpdateRepositoriesCount(projectSFID, int64(repoCount), true)
+ pcgErr := s.projectsClaGroupRepo.UpdateRepositoriesCount(ctx, projectSFID, int64(repoCount), true)
if pcgErr != nil {
log.WithFields(f).WithError(updateErr).Debugf("Failed to set repositories_count for project: %s ", projectSFID)
return pcgErr
diff --git a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
index a2754e384..a7b25a612 100644
--- a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
+++ b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
@@ -378,7 +378,7 @@ func (s *service) addCLAManagerDesigneePermissions(ctx context.Context, claGroup
} else {
// Signed at Project level Use case
- pcgs, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(claGroupID)
+ pcgs, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, claGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem getting project cla Groups for claGroupID: %s", claGroupID)
return err
diff --git a/cla-backend-go/v2/dynamo_events/signatures.go b/cla-backend-go/v2/dynamo_events/signatures.go
index a83513209..a7faff862 100644
--- a/cla-backend-go/v2/dynamo_events/signatures.go
+++ b/cla-backend-go/v2/dynamo_events/signatures.go
@@ -186,7 +186,7 @@ func (s *service) SignatureSignedEvent(event events.DynamoDBEventRecord) error {
// Load the list of SF projects associated with this CLA Group
log.WithFields(f).Debugf("querying SF projects for CLA Group: %s", newSignature.SignatureProjectID)
- projectCLAGroups, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(newSignature.SignatureProjectID)
+ projectCLAGroups, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, newSignature.SignatureProjectID)
log.WithFields(f).Debugf("found %d SF projects for CLA Group: %s", len(projectCLAGroups), newSignature.SignatureProjectID)
// Only proceed if we have one or more SF projects - otherwise, we can't assign and cleanup/adjust roles
@@ -421,7 +421,7 @@ func (s *service) assignContributor(ctx context.Context, newSignature Signature,
}
// Load the list of SF projects associated with this CLA Group
log.WithFields(f).Debugf("querying SF projects for CLA Group: %s", newSignature.SignatureProjectID)
- projectCLAGroups, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(newSignature.SignatureProjectID)
+ projectCLAGroups, err := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, newSignature.SignatureProjectID)
log.WithFields(f).Debugf("found %d SF projects for CLA Group: %s", len(projectCLAGroups), newSignature.SignatureProjectID)
if err != nil {
@@ -483,7 +483,7 @@ func (s *service) updateCLAManagerPermissions(signature Signature, managers []st
return orgErr
}
- projectCLAGroups, pcgErr := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(signature.SignatureProjectID)
+ projectCLAGroups, pcgErr := s.projectsClaGroupRepo.GetProjectsIdsForClaGroup(ctx, signature.SignatureProjectID)
if pcgErr != nil {
log.WithFields(f).WithError(pcgErr).Warnf("unable to get project mappings for claGroupID: %s ", signature.SignatureProjectID)
return pcgErr
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index 4feb29cf6..a5d88af22 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -176,7 +176,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
})
}
- pm, err := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ pm, err := projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if err != nil {
if err == projects_cla_groups.ErrProjectNotAssociatedWithClaGroup {
msg := fmt.Sprintf("no cla group associated with this project: %s", params.ProjectSFID)
@@ -226,7 +226,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
// Lookup the CLA Group associated with this Project SFID...
log.WithFields(f).Debugf("loading CLA Group for projectSFID: %s", params.ProjectSFID)
- pm, err := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ pm, err := projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if err != nil {
msg := fmt.Sprintf("problem loading CLA Group from Project SFID:: %s", params.ProjectSFID)
log.WithFields(f).Warn(msg)
@@ -313,7 +313,7 @@ func Configure(api *operations.EasyclaAPI, service v1Events.Service, v1CompanyRe
result, err = service.GetCompanyFoundationEvents(v1Company.CompanyExternalID, "", params.ProjectSFID, params.NextKey, params.PageSize, aws.BoolValue(params.ReturnAllEvents))
} else {
log.WithFields(f).Debugf("loading project level events for projectSFID :%s...", params.ProjectSFID)
- pm, perr := projectsClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ pm, perr := projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if perr != nil {
if perr == projects_cla_groups.ErrProjectNotAssociatedWithClaGroup {
// Although the API should view this as a bad request since the project doesn't seem to belong to a
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index 0f5de712b..7118eb1a1 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -110,7 +110,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
XRequestID: reqID,
})
}
- ok, err := projectsClaGroupsRepo.IsAssociated(params.ProjectSFID, params.ClaGroupID)
+ ok, err := projectsClaGroupsRepo.IsAssociated(ctx, params.ProjectSFID, params.ClaGroupID)
if err != nil {
return gerrits.NewAddGerritBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
@@ -183,7 +183,7 @@ func Configure(api *operations.EasyclaAPI, v1Service v1Gerrits.Service, projectS
}
log.WithFields(f).Debug("checking if project CLA Group mapping...")
- ok, err := projectsClaGroupsRepo.IsAssociated(params.ProjectSFID, params.ClaGroupID)
+ ok, err := projectsClaGroupsRepo.IsAssociated(ctx, params.ProjectSFID, params.ClaGroupID)
if err != nil {
msg := fmt.Sprintf("unable to determine project CLA group association for project: %s and CLA Group: %s", params.ProjectSFID, params.ClaGroupID)
log.WithFields(f).WithError(err).Warn(msg)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 94e4350c2..b0de5faea 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -135,7 +135,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
autoEnabledCLAGroupName := ""
if org.AutoEnabledClaGroupID != "" {
log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitHub auth enabled CLA Group response", org.AutoEnabledClaGroupID)
- claGroupMode, claGroupLookupErr := s.projectsCLAGroupService.GetCLAGroup(org.AutoEnabledClaGroupID)
+ claGroupMode, claGroupLookupErr := s.projectsCLAGroupService.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
if claGroupLookupErr != nil {
log.WithFields(f).WithError(claGroupLookupErr).Warnf("Unable to lookup CLA Group by ID: %s", org.AutoEnabledClaGroupID)
}
diff --git a/cla-backend-go/v2/metrics/handlers.go b/cla-backend-go/v2/metrics/handlers.go
index b69c9369a..b17552ca1 100644
--- a/cla-backend-go/v2/metrics/handlers.go
+++ b/cla-backend-go/v2/metrics/handlers.go
@@ -120,7 +120,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1CompanyRepo v1Comp
})
}
- result, err := service.ListCompanyProjectMetrics(params.CompanyID, params.ProjectSFID)
+ result, err := service.ListCompanyProjectMetrics(ctx, params.CompanyID, params.ProjectSFID)
if err != nil {
return metrics.NewListCompanyProjectMetricsBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
diff --git a/cla-backend-go/v2/metrics/repository.go b/cla-backend-go/v2/metrics/repository.go
index 7fcc31042..54f39ef43 100644
--- a/cla-backend-go/v2/metrics/repository.go
+++ b/cla-backend-go/v2/metrics/repository.go
@@ -4,6 +4,7 @@
package metrics
import (
+ "context"
"errors"
"fmt"
"time"
@@ -967,7 +968,7 @@ type claGroup struct {
func (repo *repo) getClaGroupProjectsMapping() (map[string]*claGroup, error) {
r := make(map[string]*claGroup)
- cgpList, err := repo.projectsClaGroupsRepo.GetProjectsIdsForAllFoundation()
+ cgpList, err := repo.projectsClaGroupsRepo.GetProjectsIdsForAllFoundation(context.Background())
if err != nil {
return r, err
}
@@ -1073,7 +1074,7 @@ func (repo *repo) projectHelperMap(in *CompanyProjectMetrics, claGroupMapping ma
log.Printf("length of projectIDArray %d", len(projectIDArray))
for projectSFID := range projectIDArray {
- projectData, err := repo.projectsClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ projectData, err := repo.projectsClaGroupsRepo.GetClaGroupIDForProject(context.Background(), projectSFID)
if err != nil {
log.Warnf("projectHelperMap/GetClaGroupIDForProject error = unable to get project details from easycla. %s", projectSFID)
continue
diff --git a/cla-backend-go/v2/metrics/service.go b/cla-backend-go/v2/metrics/service.go
index 13da21b41..0b9bd6194 100644
--- a/cla-backend-go/v2/metrics/service.go
+++ b/cla-backend-go/v2/metrics/service.go
@@ -4,6 +4,7 @@
package metrics
import (
+ "context"
"errors"
"math"
"sort"
@@ -35,7 +36,7 @@ type Service interface {
GetTopCompanies() (*models.TopCompanies, error)
GetTopProjects() (*models.TopProjects, error)
ListProjectMetrics(paramPageSize *int64, paramNextKey *string) (*models.ListProjectMetric, error)
- ListCompanyProjectMetrics(companyID string, projectSFID string) (*models.CompanyProjectMetrics, error)
+ ListCompanyProjectMetrics(ctx context.Context, companyID string, projectSFID string) (*models.CompanyProjectMetrics, error)
}
type service struct {
@@ -287,7 +288,7 @@ func (s *service) ListProjectMetrics(paramPageSize *int64, paramNextKey *string)
return &out, nil
}
-func (s *service) ListCompanyProjectMetrics(companyID string, projectSFID string) (*models.CompanyProjectMetrics, error) {
+func (s *service) ListCompanyProjectMetrics(ctx context.Context, companyID string, projectSFID string) (*models.CompanyProjectMetrics, error) {
psc := project_service.GetClient()
claGroupList := utils.NewStringSet()
project, err := psc.GetProject(projectSFID)
@@ -295,7 +296,7 @@ func (s *service) ListCompanyProjectMetrics(companyID string, projectSFID string
return nil, err
}
if project.ProjectType == FoundationType {
- cgmList, cgerr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(projectSFID)
+ cgmList, cgerr := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, projectSFID)
if cgerr != nil {
return nil, err
}
@@ -303,7 +304,7 @@ func (s *service) ListCompanyProjectMetrics(companyID string, projectSFID string
claGroupList.Add(cgm.ClaGroupID)
}
} else {
- cgm, cgerr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ cgm, cgerr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if cgerr != nil {
return nil, err
}
diff --git a/cla-backend-go/v2/project/service.go b/cla-backend-go/v2/project/service.go
index 09189dcb4..b7b281fc0 100644
--- a/cla-backend-go/v2/project/service.go
+++ b/cla-backend-go/v2/project/service.go
@@ -50,7 +50,7 @@ func (s *service) GetCLAProjectsByID(ctx context.Context, foundationSFID string)
}
enabledClas := make([]*models.EnabledCla, 0)
- claGroupsMapping, err := s.projectsClaGroups.GetProjectsIdsForFoundation(foundationSFID)
+ claGroupsMapping, err := s.projectsClaGroups.GetProjectsIdsForFoundation(ctx, foundationSFID)
if err != nil {
return nil, err
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index ce5e73db2..a47796026 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -97,7 +97,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
parentProjectSFID = project.Parent
}
- allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(aws.StringValue(input.ClaGroupID))
+ allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, aws.StringValue(input.ClaGroupID))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to get project IDs for CLA Group")
return nil, err
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 01d181491..683299365 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -176,7 +176,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
if project.Parent == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
// this is root project
- cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(utils.StringValue(input.ProjectSfid))
+ cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(ctx, utils.StringValue(input.ProjectSfid))
if perr != nil {
log.WithFields(f).WithError(err).Warn("unable to lookup other projects associated with this project SFID")
return nil, perr
@@ -197,7 +197,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
claGroupID = (claGroups.List())[0]
} else {
- cgm, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(utils.StringValue(input.ProjectSfid))
+ cgm, perr := s.projectClaGroupsRepo.GetClaGroupIDForProject(ctx, utils.StringValue(input.ProjectSfid))
if perr != nil {
log.WithFields(f).WithError(err).Warn("unable to lookup CLA Group ID for this project SFID")
return nil, perr
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index a09d1a252..15195a8e6 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -529,7 +529,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
// Locate the CLA Group for the provided project SFID
log.WithFields(f).Debug("loading project signatures...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(params.ProjectSFID)
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, params.ProjectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping")
return signatures.NewGetProjectCompanyEmployeeSignaturesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(
@@ -758,7 +758,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
}
// Lookup the Project to CLA Group mapping table entries - this will have the correct details
- projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
// Should have at least one entry if we're setup correctly - it will have the foundation (parent project/project group) and project details set
if projectCLAGroupErr != nil || len(projectCLAGroupEntries) == 0 {
msg := fmt.Sprintf("unable to load project CLA Group mappings for CLA Group: %s - has this project been migrated to v2?", params.ClaGroupID)
@@ -936,7 +936,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
}
// Lookup the Project to CLA Group mapping table entries - this will have the correct details
- projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(params.ClaGroupID)
+ projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
// Should have at least one entry if we're setup correctly - it will have the foundation (parent project/project group) and project details set
if projectCLAGroupErr != nil || len(projectCLAGroupEntries) == 0 {
msg := fmt.Sprintf("unable to load project CLA Group mappings for CLA Group: %s - has this project been migrated to v2?", params.ClaGroupID)
@@ -1318,7 +1318,7 @@ func isUserHaveAccessOfSignedSignaturePDF(ctx context.Context, authUser *auth.Us
}
var projectCLAGroup *v1Models.ClaGroup
- projects, err := projectClaGroupRepo.GetProjectsIdsForClaGroup(signature.ProjectID)
+ projects, err := projectClaGroupRepo.GetProjectsIdsForClaGroup(ctx, signature.ProjectID)
if err != nil {
log.WithFields(f).WithError(err).Warn("error loading load project IDs for CLA Group")
return false, err
@@ -1429,7 +1429,7 @@ func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User
// Lookup the project IDs for the CLA Group
log.WithFields(f).Debug("looking up projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(claGroupID)
+ projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, claGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - failed permission check")
return false
@@ -1500,7 +1500,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
log.WithFields(f).Debugf("user %s/%s doesn't have direct access to the project SFID: %s - loading CLA Group from project id...", authUser.UserName, authUser.Email, projectSFID)
log.WithFields(f).Debug("loading CLA Group from project id...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
return false
@@ -1524,7 +1524,7 @@ func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, proj
// Lookup the other project IDs for the CLA Group
log.WithFields(f).Debug("looking up other projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(projectCLAGroupModel.ClaGroupID)
+ projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectCLAGroupModel.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - returning false")
return false
@@ -1589,7 +1589,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// other projects or the parent project group/foundation
log.WithFields(f).Debugf("user %s/%s doesn't have direct access to the project only, project + organization, or organization only - loading CLA Group from project id...", authUser.UserName, authUser.Email)
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
return false
@@ -1627,7 +1627,7 @@ func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *aut
// Lookup the other project IDs associated with this CLA Group
log.WithFields(f).Debug("looking up other projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(projectCLAGroupModel.ClaGroupID)
+ projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectCLAGroupModel.ClaGroupID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - returning false")
return false
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 2cd4e629a..1984e5ed7 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -88,7 +88,7 @@ func NewService(awsSession *session.Session, signaturesBucketName string, v1Proj
}
func (s *service) GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error) {
- pm, err := s.projectsClaGroupsRepo.GetClaGroupIDForProject(projectSFID)
+ pm, err := s.projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
return nil, err
}
diff --git a/cla-backend-go/v2/template/handlers.go b/cla-backend-go/v2/template/handlers.go
index 7a88e18ae..9f305052b 100644
--- a/cla-backend-go/v2/template/handlers.go
+++ b/cla-backend-go/v2/template/handlers.go
@@ -5,7 +5,11 @@ package template
import (
"context"
+ "fmt"
"net/http"
+ "strings"
+
+ v1ProjectsCLAGroups "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/sirupsen/logrus"
@@ -25,14 +29,14 @@ import (
)
// Configure API call
-func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsService v1Events.Service) {
+func Configure(api *operations.EasyclaAPI, service v1Template.Service, v1ProjectClaGroupService v1ProjectsCLAGroups.Service, eventsService v1Events.Service) {
// Retrieve a list of available templates
api.TemplateGetTemplatesHandler = template.GetTemplatesHandlerFunc(func(params template.GetTemplatesParams, user *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "TemplateGetTemplatesHandler",
+ "functionName": "v2.template.handlers.TemplateGetTemplatesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
@@ -50,23 +54,39 @@ func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsSer
return template.NewGetTemplatesOK().WithPayload(response)
})
- api.TemplateCreateCLAGroupTemplateHandler = template.CreateCLAGroupTemplateHandlerFunc(func(params template.CreateCLAGroupTemplateParams, user *auth.User) middleware.Responder {
+ api.TemplateCreateCLAGroupTemplateHandler = template.CreateCLAGroupTemplateHandlerFunc(func(params template.CreateCLAGroupTemplateParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
- utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "TemplateCreateCLAGroupTemplateHandler",
+ "functionName": "v2.template.handlers.TemplateCreateCLAGroupTemplateHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
}
+ projectCLAGroups, lookupErr := v1ProjectClaGroupService.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
+ if lookupErr != nil || len(projectCLAGroups) == 0 {
+ msg := fmt.Sprintf("unable to lookup CLA Group mapping using CLA Group ID: %s", params.ClaGroupID)
+ return template.NewGetTemplatesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, lookupErr))
+ }
+ projectSFIDs := getProjectSFIDList(projectCLAGroups)
+
+ // Check authorization
+ if !utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("authUser '%s' does not have access to create CLA Group template with Project scope of any %s",
+ authUser.UserName, strings.Join(projectSFIDs, ","))
+ log.WithFields(f).Debug(msg)
+ return template.NewGetTemplatesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
input := &v1Models.CreateClaGroupTemplate{}
err := copier.Copy(input, ¶ms.Body)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem converting templates")
return template.NewGetTemplatesInternalServerError().WithPayload(errorResponse(reqID, err))
}
- pdfUrls, err := service.CreateCLAGroupTemplate(params.HTTPRequest.Context(), params.ClaGroupID, input)
+
+ pdfUrls, err := service.CreateCLAGroupTemplate(ctx, params.ClaGroupID, input)
if err != nil {
log.WithFields(f).WithError(err).Warnf("Error generating PDFs from provided templates, error: %v", err)
return template.NewGetTemplatesBadRequest().WithPayload(errorResponse(reqID, err))
@@ -75,7 +95,7 @@ func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsSer
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLATemplateCreated,
ProjectID: params.ClaGroupID,
- LfUsername: user.UserName,
+ LfUsername: authUser.UserName,
EventData: &events.CLATemplateCreatedEventData{},
})
@@ -94,7 +114,7 @@ func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsSer
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(user, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "TemplateTemplatePreviewHandler",
+ "functionName": "v2.template.handlers.TemplateTemplatePreviewHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"templateFor": params.TemplateFor,
}
@@ -123,10 +143,10 @@ func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsSer
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "TemplateGetCLATemplatePreviewHandler",
+ "functionName": "v2.template.handlers.TemplateGetCLATemplatePreviewHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
- pdf, err := service.GetCLATemplatePreview(params.HTTPRequest.Context(), params.ClaGroupID, params.ClaType, *params.Watermark)
+ pdf, err := service.GetCLATemplatePreview(ctx, params.ClaGroupID, params.ClaType, *params.Watermark)
if err != nil {
log.WithFields(f).WithError(err).Warnf("Error getting PDFs for provided cla group ID : %s, error: %v", params.ClaGroupID, err)
return writeResponse(http.StatusBadRequest, runtime.JSONMime, runtime.JSONProducer(), reqID, errorResponse(reqID, err))
@@ -142,6 +162,15 @@ func Configure(api *operations.EasyclaAPI, service v1Template.Service, eventsSer
})
}
+// getProjectSFIDList is a helper function to extract the project SFID values from the list of project to CLA group mapping records
+func getProjectSFIDList(groups []*v1ProjectsCLAGroups.ProjectClaGroup) []string {
+ var response []string
+ for _, projectCLAGroup := range groups {
+ response = append(response, projectCLAGroup.ProjectSFID)
+ }
+ return response
+}
+
type codedResponse interface {
Code() string
}
From 81d0f3adf180acbd86cf0cdc762703223e785690 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Wed, 26 May 2021 21:40:43 +0300
Subject: [PATCH 0294/1276] [Snyk] Upgrade graceful-fs from 4.2.4 to 4.2.6
(#2962)
---
cla-frontend-contributor-console/edge/package.json | 2 +-
cla-frontend-contributor-console/edge/yarn.lock | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/package.json b/cla-frontend-contributor-console/edge/package.json
index 2ad0aae7c..c9b992a58 100644
--- a/cla-frontend-contributor-console/edge/package.json
+++ b/cla-frontend-contributor-console/edge/package.json
@@ -10,7 +10,7 @@
"test": "./node_modules/.bin/jasmine --config=jasmine.json"
},
"dependencies": {
- "graceful-fs": "^4.2.2"
+ "graceful-fs": "^4.2.6"
},
"resolutions": {
"node-sass": "4.13.1",
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index d30e8eecf..77b6a1c18 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -1347,11 +1347,16 @@ globule@^1.0.0:
lodash "~4.17.10"
minimatch "~3.0.2"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.2:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.6:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
From 52fc993e24ec55c48a6e0907210d8fd1bfb57a72 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Wed, 26 May 2021 22:10:57 +0300
Subject: [PATCH 0295/1276] [Snyk] Upgrade graceful-fs from 4.2.4 to 4.2.6
(#2965)
---
cla-frontend-corporate-console/edge/package.json | 2 +-
cla-frontend-corporate-console/edge/yarn.lock | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-corporate-console/edge/package.json b/cla-frontend-corporate-console/edge/package.json
index 85c1a2443..e1af3dea4 100644
--- a/cla-frontend-corporate-console/edge/package.json
+++ b/cla-frontend-corporate-console/edge/package.json
@@ -11,7 +11,7 @@
},
"dependencies": {
"acorn": "^5.7.4",
- "graceful-fs": "^4.2.2"
+ "graceful-fs": "^4.2.6"
},
"devDependencies": {
"babel-core": "^6.26.0",
diff --git a/cla-frontend-corporate-console/edge/yarn.lock b/cla-frontend-corporate-console/edge/yarn.lock
index c0667c228..1514185fe 100644
--- a/cla-frontend-corporate-console/edge/yarn.lock
+++ b/cla-frontend-corporate-console/edge/yarn.lock
@@ -1094,11 +1094,16 @@ globals@^9.18.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.2:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.6:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
From 46006a06e159d42daa59334f881ccde5796fbacb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 26 May 2021 12:30:35 -0700
Subject: [PATCH 0296/1276] Bump chart.js from 2.8.0 to 2.9.4 in
/cla-frontend-corporate-console/src (#2942)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/package.json | 2 +-
cla-frontend-corporate-console/src/yarn.lock | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/cla-frontend-corporate-console/src/package.json b/cla-frontend-corporate-console/src/package.json
index f0996d309..d6c33e8a6 100644
--- a/cla-frontend-corporate-console/src/package.json
+++ b/cla-frontend-corporate-console/src/package.json
@@ -39,7 +39,7 @@
"@types/lodash": "4.14.112",
"@types/node": "^8.0.17",
"aws-sdk": "^2.897.0",
- "chart.js": "^2.5.0",
+ "chart.js": "^2.9.4",
"google-libphonenumber": "^2.0.18",
"graceful-fs": "^4.2.2",
"ionic-angular": "3.9.2",
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 22ee0fefa..9e2359380 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -726,9 +726,10 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.0, chalk@^2.4.1:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chart.js@^2.5.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.8.0.tgz#b703b10d0f4ec5079eaefdcd6ca32dc8f826e0e9"
+chart.js@^2.9.4:
+ version "2.9.4"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
+ integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
From ff7f2a5bee25fb72f773e3dd9d9df89d3b3e8681 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 26 May 2021 12:44:34 -0700
Subject: [PATCH 0297/1276] Bump chart.js from 2.8.0 to 2.9.4 in
/cla-frontend-project-console/src (#2940)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/package.json | 2 +-
cla-frontend-project-console/src/yarn.lock | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/cla-frontend-project-console/src/package.json b/cla-frontend-project-console/src/package.json
index e5e27ee59..5fecf5de1 100644
--- a/cla-frontend-project-console/src/package.json
+++ b/cla-frontend-project-console/src/package.json
@@ -40,7 +40,7 @@
"@types/node": "^8.0.17",
"auth0-js": "^9.13.2",
"aws-sdk": "^2.304.0",
- "chart.js": "^2.5.0",
+ "chart.js": "^2.9.4",
"google-libphonenumber": "^2.0.18",
"graceful-fs": "^4.2.2",
"ionic-angular": "3.9.2",
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index dc655ea79..ad1b1bdba 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -738,9 +738,10 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.0, chalk@^2.4.1:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chart.js@^2.5.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.8.0.tgz#b703b10d0f4ec5079eaefdcd6ca32dc8f826e0e9"
+chart.js@^2.9.4:
+ version "2.9.4"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
+ integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
From 8867bacc7557f7aacab1b454508de3e07c26ad12 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 28 May 2021 14:06:57 -0700
Subject: [PATCH 0298/1276] Added Template ID to CLA Group Model (#2969)
- Added template ID to swagger models
- Updated internal DB models
- Updated repository save logic to add template ID
- Updated CLA Group update logic to ensure templates are now changed during an update operation
- Updated list cla-groups for foundation/project to include template info
Signed-off-by: David Deal
---
cla-backend-go/project/models.go | 1 +
cla-backend-go/project/projections.go | 1 +
cla-backend-go/project/repository.go | 15 +++-
cla-backend-go/swagger/cla.v2.yaml | 78 +----------------
.../swagger/common/cla-group-summary.yaml | 86 +++++++++++++++++++
cla-backend-go/swagger/common/cla-group.yaml | 6 +-
cla-backend-go/template/repository.go | 7 ++
cla-backend-go/template/service.go | 10 ++-
cla-backend-go/v2/cla_groups/helpers.go | 27 +++++-
cla-backend-go/v2/cla_groups/service.go | 4 +
10 files changed, 148 insertions(+), 87 deletions(-)
create mode 100644 cla-backend-go/swagger/common/cla-group-summary.yaml
diff --git a/cla-backend-go/project/models.go b/cla-backend-go/project/models.go
index 4d9ea93ac..2caa81c81 100644
--- a/cla-backend-go/project/models.go
+++ b/cla-backend-go/project/models.go
@@ -15,6 +15,7 @@ type DBProjectModel struct {
ProjectNameLower string `dynamodbav:"project_name_lower"`
ProjectDescription string `dynamodbav:"project_description"`
Version string `dynamodbav:"version"`
+ ProjectTemplateID string `dynamodbav:"project_template_id"`
ProjectCclaEnabled bool `dynamodbav:"project_ccla_enabled"`
ProjectCclaRequiresIclaSignature bool `dynamodbav:"project_ccla_requires_icla_signature"`
ProjectIclaEnabled bool `dynamodbav:"project_icla_enabled"`
diff --git a/cla-backend-go/project/projections.go b/cla-backend-go/project/projections.go
index 0fd15fa31..f31c811b4 100644
--- a/cla-backend-go/project/projections.go
+++ b/cla-backend-go/project/projections.go
@@ -24,6 +24,7 @@ func buildProjection() expression.ProjectionBuilder {
expression.Name("project_corporate_documents"),
expression.Name("project_individual_documents"),
expression.Name("project_member_documents"),
+ expression.Name("project_template_id"),
expression.Name("date_created"),
expression.Name("date_modified"),
expression.Name("version"),
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 9c69d9705..79ee2d552 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -111,6 +111,7 @@ func (repo *repo) CreateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
addStringAttribute(input.Item, "foundation_sfid", claGroupModel.FoundationSFID)
addStringAttribute(input.Item, "project_description", claGroupModel.ProjectDescription)
addStringAttribute(input.Item, "project_name", claGroupModel.ProjectName)
+ addStringAttribute(input.Item, "project_template_id", claGroupModel.ProjectTemplateID)
addStringAttribute(input.Item, "project_name_lower", strings.ToLower(claGroupModel.ProjectName))
addStringSliceAttribute(input.Item, "project_acl", claGroupModel.ProjectACL)
addBooleanAttribute(input.Item, "project_icla_enabled", claGroupModel.ProjectICLAEnabled)
@@ -355,15 +356,13 @@ func (repo *repo) GetClaGroupsByFoundationSFID(ctx context.Context, foundationSF
}
}
- // log.WithFields(f).Debugf("foundation projects!: %#v ", projects)
-
return &models.ClaGroups{
ResultCount: int64(len(projects)),
Projects: projects,
}, nil
}
-// GetClaGroupsByProjectSFID returns cla_group associated with project
+// GetClaGroupByProjectSFID returns cla_group associated with project
func (repo *repo) GetClaGroupByProjectSFID(ctx context.Context, projectSFID string, loadRepoDetails bool) (*models.ClaGroup, error) {
f := logrus.Fields{
"functionName": "project.repository.GetClaGroupByProjectSFID",
@@ -647,6 +646,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
"ProjectICLAEnabled": claGroupModel.ProjectICLAEnabled,
"ProjectCCLAEnabled": claGroupModel.ProjectCCLAEnabled,
"ProjectCCLARequiresICLA": claGroupModel.ProjectCCLARequiresICLA,
+ "ProjectTemplateID": claGroupModel.ProjectTemplateID,
"ProjectLive": claGroupModel.ProjectLive,
"tableName": repo.claGroupTable}
log.WithFields(f).Debugf("processing update CLA Group request")
@@ -665,6 +665,14 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
return nil, ErrProjectDoesNotExist
}
+ // We don't allow CLA Group templates to be changed - this requires a legal review
+ if existingCLAGroup.ProjectTemplateID != claGroupModel.ProjectTemplateID {
+ msg := fmt.Sprintf("problem updating CLA Group - changing CLA templates is not allowed - project: %s with ID: %s - previous template: %s updated template: %s",
+ claGroupModel.ProjectName, claGroupModel.ProjectID, claGroupModel.ProjectTemplateID, claGroupModel.ProjectTemplateID)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
expressionAttributeNames := map[string]*string{}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
updateExpression := "SET "
@@ -890,6 +898,7 @@ func (repo *repo) buildCLAGroupModel(ctx context.Context, dbModel DBProjectModel
ProjectCCLAEnabled: dbModel.ProjectCclaEnabled,
ProjectICLAEnabled: dbModel.ProjectIclaEnabled,
ProjectCCLARequiresICLA: dbModel.ProjectCclaRequiresIclaSignature,
+ ProjectTemplateID: dbModel.ProjectTemplateID,
ProjectLive: dbModel.ProjectLive,
ProjectCorporateDocuments: buildCLAGroupDocumentModels(dbModel.ProjectCorporateDocuments),
ProjectIndividualDocuments: buildCLAGroupDocumentModels(dbModel.ProjectIndividualDocuments),
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 7f2a2a7f3..dbf93745e 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -5116,83 +5116,7 @@ definitions:
$ref: '#/definitions/cla-group-summary'
cla-group-summary:
- type: object
- properties:
- foundationLevelCLA:
- description: Flag indicating whether CLA is signed at Foundation level (true) or Project level (false)
- type: boolean
- x-omitempty: false
- cla_group_id:
- type: string
- example: 'b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f'
- description: id of the CLA group
- x-omitempty: false
- cla_group_name:
- $ref: './common/properties/cla-group-name.yaml'
- x-omitempty: false
- cla_group_description:
- $ref: './common/properties/cla-group-description.yaml'
- x-omitempty: false
- ccla_enabled:
- type: boolean
- example: true
- description: flag to indicate if CCLA is enabled
- x-omitempty: false
- ccla_requires_icla:
- type: boolean
- example: true
- description: flag to indicate if corporate contributors requires to sign ICLA
- x-omitempty: false
- icla_enabled:
- type: boolean
- example: true
- description: flag to indicate if ICLA is enabled
- x-omitempty: false
- foundation_sfid:
- type: string
- example: 'a09410000182dD2AAI'
- description: foundation sfid under which this CLA group is created
- x-omitempty: false
- root_project_repositories_count:
- type: integer
- description: number of repositories added to this CLA Group from root project
- x-omitempty: false
- foundation_name:
- type: string
- example: 'Academy Software Foundation'
- description: foundation name under which this CLA group is created
- x-omitempty: false
- repositories_count:
- type: integer
- description: total repositories under this cla-group
- x-omitempty: false
- total_signatures:
- type: integer
- description: aggregate count of ICLA and CCLA contributors within this CLA Group
- x-omitempty: false
- project_list:
- x-omitempty: false
- description: list of projects under foundation for which this CLA group is created
- type: array
- items:
- $ref: '#/definitions/cla-group-project'
- icla_pdf_url:
- description: template URL for ICLA document
- type: string
- example: 'https://cla-signature-files-dev.s3.amazonaws.com/contract-group/b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f/template/icla.pdf'
- x-omitempty: false
- ccla_pdf_url:
- description: template URL for CCLA document
- type: string
- example: 'https://cla-signature-files-dev.s3.amazonaws.com/contract-group/b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f/template/ccla.pdf'
- x-omitempty: false
- setup_completion_pct:
- description: the CLA Group setup complete percentage, values range from 0-100 inclusive
- type: integer
- minimum: 0
- maximum: 100
- example: 100
- x-omitempty: false
+ $ref: './common/cla-group-summary.yaml'
cla-group-project:
type: object
diff --git a/cla-backend-go/swagger/common/cla-group-summary.yaml b/cla-backend-go/swagger/common/cla-group-summary.yaml
new file mode 100644
index 000000000..454d2d0a4
--- /dev/null
+++ b/cla-backend-go/swagger/common/cla-group-summary.yaml
@@ -0,0 +1,86 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+title: CLA Group Summary
+description: a summary of the CLA Group information
+properties:
+ foundationLevelCLA:
+ description: Flag indicating whether CLA is signed at Foundation level (true) or Project level (false)
+ type: boolean
+ x-omitempty: false
+ cla_group_id:
+ type: string
+ example: 'b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f'
+ description: id of the CLA group
+ x-omitempty: false
+ cla_group_name:
+ $ref: './common/properties/cla-group-name.yaml'
+ x-omitempty: false
+ cla_group_description:
+ $ref: './common/properties/cla-group-description.yaml'
+ x-omitempty: false
+ ccla_enabled:
+ type: boolean
+ example: true
+ description: flag to indicate if CCLA is enabled
+ x-omitempty: false
+ ccla_requires_icla:
+ type: boolean
+ example: true
+ description: flag to indicate if corporate contributors requires to sign ICLA
+ x-omitempty: false
+ icla_enabled:
+ type: boolean
+ example: true
+ description: flag to indicate if ICLA is enabled
+ x-omitempty: false
+ template_id:
+ title: CLA group template
+ description: the ID of the template - used to generate the ICLA and CCLA PDFs
+ $ref: './common/properties/internal-id.yaml'
+ foundation_sfid:
+ type: string
+ example: 'a09410000182dD2AAI'
+ description: foundation sfid under which this CLA group is created
+ x-omitempty: false
+ root_project_repositories_count:
+ type: integer
+ description: number of repositories added to this CLA Group from root project
+ x-omitempty: false
+ foundation_name:
+ type: string
+ example: 'Academy Software Foundation'
+ description: foundation name under which this CLA group is created
+ x-omitempty: false
+ repositories_count:
+ type: integer
+ description: total repositories under this cla-group
+ x-omitempty: false
+ total_signatures:
+ type: integer
+ description: aggregate count of ICLA and CCLA contributors within this CLA Group
+ x-omitempty: false
+ project_list:
+ x-omitempty: false
+ description: list of projects under foundation for which this CLA group is created
+ type: array
+ items:
+ $ref: '#/definitions/cla-group-project'
+ icla_pdf_url:
+ description: template URL for ICLA document
+ type: string
+ example: 'https://cla-signature-files-dev.s3.amazonaws.com/contract-group/b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f/template/icla.pdf'
+ x-omitempty: false
+ ccla_pdf_url:
+ description: template URL for CCLA document
+ type: string
+ example: 'https://cla-signature-files-dev.s3.amazonaws.com/contract-group/b1e86e26-d8c8-4fd8-9f8d-5c723d5dac9f/template/ccla.pdf'
+ x-omitempty: false
+ setup_completion_pct:
+ description: the CLA Group setup complete percentage, values range from 0-100 inclusive
+ type: integer
+ minimum: 0
+ maximum: 100
+ example: 100
+ x-omitempty: false
diff --git a/cla-backend-go/swagger/common/cla-group.yaml b/cla-backend-go/swagger/common/cla-group.yaml
index 4489f32fd..6f11b52ba 100644
--- a/cla-backend-go/swagger/common/cla-group.yaml
+++ b/cla-backend-go/swagger/common/cla-group.yaml
@@ -31,12 +31,16 @@ properties:
description: Flag indicating whether CLA is signed at Foundation level (true) or Project level (false)
example: true
type: boolean
- x-omitempty: false
+ x-omitempty: false
projectCCLAEnabled:
description: Flag to indicate if the Corporate/Company Contributor License Agreement is enabled
example: true
type: boolean
x-omitempty: false
+ projectTemplateID:
+ title: CLA group template
+ description: the ID of the template - used to generate the ICLA and CCLA PDFs
+ $ref: './common/properties/internal-id.yaml'
projectICLAEnabled:
description: Flag to indicate if the Individual Contributor License Agreement is enabled
example: true
diff --git a/cla-backend-go/template/repository.go b/cla-backend-go/template/repository.go
index 6fba94bb1..546ba85dc 100644
--- a/cla-backend-go/template/repository.go
+++ b/cla-backend-go/template/repository.go
@@ -39,6 +39,7 @@ var (
type Repository interface {
GetTemplates(ctx context.Context) ([]models.Template, error)
GetTemplate(templateID string) (models.Template, error)
+ CLAGroupTemplateExists(ctx context.Context, templateID string) bool
GetCLAGroup(claGroupID string) (*models.ClaGroup, error)
GetCLADocuments(claGroupID string, claType string) ([]models.ClaGroupDocument, error)
UpdateDynamoContractGroupTemplates(ctx context.Context, ContractGroupID string, template models.Template, pdfUrls models.TemplatePdfs, projectCCLAEnabled, projectICLAEnabled bool) error
@@ -137,6 +138,12 @@ func (r repository) GetTemplate(templateID string) (models.Template, error) {
return template, nil
}
+// CLAGroupTemplateExists return true if the specified template ID exists, false otherwise
+func (r repository) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
+ _, ok := templateMap[templateID]
+ return ok
+}
+
// GetCLAGroup This method belongs in the contract group package. We are leaving it here
// because it accesses DynamoDB, but the contract group repository is designed
// to connect to postgres
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index 1f5160b2e..a73aa043b 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -39,6 +39,7 @@ type Service interface {
CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error)
CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error)
GetCLATemplatePreview(ctx context.Context, claGroupID, claType string, watermark bool) ([]byte, error)
+ CLAGroupTemplateExists(ctx context.Context, templateID string) bool
}
type service struct {
@@ -154,7 +155,7 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
// Get Template
template, err := s.templateRepo.GetTemplate(claGroupFields.TemplateID)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("Unable to fetch template fields: %s - returning empty template PDFs",
+ log.WithFields(f).WithError(err).Warnf("Unable to fetch template id: %s - returning empty template PDFs",
claGroupFields.TemplateID)
return models.TemplatePdfs{}, err
}
@@ -495,7 +496,7 @@ func (s service) generateTemplateS3FilePath(claGroupID, claType string) string {
return fileName
}
-// SaveTemplateToS3
+// SaveTemplateToS3 uploads the specified template contents to S3 storage
func (s service) SaveTemplateToS3(bucket, filepath string, template io.ReadCloser) (string, error) {
f := logrus.Fields{
"functionName": "v1.template.service.SaveTemplateToS3",
@@ -523,3 +524,8 @@ func (s service) SaveTemplateToS3(bucket, filepath string, template io.ReadClose
return result.Location, nil
}
+
+// CLAGroupTemplateExists return true if the specified template ID exists, false otherwise
+func (s service) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
+ return s.templateRepo.CLAGroupTemplateExists(ctx, templateID)
+}
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 8e8189363..fd639b5f4 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -35,11 +35,12 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
if input.ClaGroupName == nil {
return false, fmt.Errorf("missing CLA Group parameter")
}
+
foundationSFID := *input.FoundationSfid
claGroupName := *input.ClaGroupName
f := logrus.Fields{
- "functionName": "validateClaGroupInput",
+ "functionName": "v2.cla_groups.helpers.validateClaGroupInput",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"ClaGroupName": claGroupName,
"ClaGroupDescription": input.ClaGroupDescription,
@@ -48,19 +49,37 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
"CclaEnabled": *input.CclaEnabled,
"CclaRequiresIcla": *input.CclaRequiresIcla,
"ProjectSfidList": strings.Join(input.ProjectSfidList, ","),
+ "templateID": input.TemplateFields.TemplateID,
}
log.WithFields(f).Debug("validating CLA Group input...")
+
+ if input.TemplateFields.TemplateID == "" {
+ msg := "missing CLA Group template ID value"
+ log.WithFields(f).Warn(msg)
+ return false, errors.New(msg)
+ }
+ if !s.v1TemplateService.CLAGroupTemplateExists(ctx, input.TemplateFields.TemplateID) {
+ msg := "invalid template ID"
+ log.WithFields(f).Warn(msg)
+ return false, errors.New(msg)
+ }
// First, check that all the required flags are set and make sense
if foundationSFID == "" {
- return false, fmt.Errorf("bad request: foundation_sfid cannot be empty")
+ msg := "bad request: foundation_sfid cannot be empty"
+ log.WithFields(f).Warn(msg)
+ return false, errors.New(msg)
}
if !*input.IclaEnabled && !*input.CclaEnabled {
- return false, fmt.Errorf("bad request: can not create cla group with both icla and ccla disabled")
+ msg := "bad request: can not create cla group with both icla and ccla disabled"
+ log.WithFields(f).Warn(msg)
+ return false, errors.New(msg)
}
if *input.CclaRequiresIcla {
if !(*input.IclaEnabled && *input.CclaEnabled) {
- return false, fmt.Errorf("bad request: ccla_requires_icla can not be enabled if one of icla/ccla is disabled")
+ msg := "bad request: ccla_requires_icla can not be enabled if one of icla/ccla is disabled"
+ log.WithFields(f).Warn(msg)
+ return false, errors.New(msg)
}
}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index aef0ee9ec..f333dc930 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -106,6 +106,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
"CclaRequiresIcla": aws.BoolValue(input.CclaRequiresIcla),
"ProjectSfidList": strings.Join(input.ProjectSfidList, ","),
"projectManagerLFID": projectManagerLFID,
+ "claGroupTemplate": input.TemplateFields.TemplateID,
}
log.WithFields(f).Debug("validating CLA Group input")
@@ -142,6 +143,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
ProjectACL: []string{projectManagerLFID},
ProjectICLAEnabled: *input.IclaEnabled,
ProjectName: *input.ClaGroupName,
+ ProjectTemplateID: input.TemplateFields.TemplateID,
Version: "v2",
})
if err != nil {
@@ -220,6 +222,7 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
ClaGroupDescription: claGroup.ProjectDescription,
ClaGroupID: claGroup.ProjectID,
ClaGroupName: claGroup.ProjectName,
+ TemplateID: claGroup.ProjectTemplateID,
FoundationSfid: claGroup.FoundationSFID,
FoundationName: foundationName,
IclaEnabled: claGroup.ProjectICLAEnabled,
@@ -518,6 +521,7 @@ func (s *service) buildClaGroupSummaryResponseModel(ctx context.Context, f logru
ClaGroupDescription: v1ClaGroup.ProjectDescription,
ClaGroupID: v1ClaGroup.ProjectID,
ClaGroupName: v1ClaGroup.ProjectName,
+ TemplateID: v1ClaGroup.ProjectTemplateID,
FoundationSfid: v1ClaGroup.FoundationSFID,
FoundationName: foundationName,
IclaEnabled: v1ClaGroup.ProjectICLAEnabled,
From 6a62224cce4e8bfd3f5526c83ab83cb828b757fd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 28 May 2021 17:38:08 -0700
Subject: [PATCH 0299/1276] Resolved PCC-1009 - Search Term Query (#2970)
---
cla-backend-go/signatures/repository.go | 144 +++++++++++------------
cla-backend-go/v2/signatures/handlers.go | 1 -
2 files changed, 72 insertions(+), 73 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index d08661a7c..dc3a0f6ad 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -468,21 +468,21 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
builder := expression.NewBuilder().
@@ -581,21 +581,21 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
builder := expression.NewBuilder().
@@ -718,13 +718,14 @@ func (repo repository) GetSignatureACL(ctx context.Context, signatureID string)
return dbModel.SignatureACL, nil
}
-func addConditionToFilter(filter expression.ConditionBuilder, cond expression.ConditionBuilder, filterAdded *bool) expression.ConditionBuilder {
+func addAndCondition(filter expression.ConditionBuilder, cond expression.ConditionBuilder, filterAdded *bool) expression.ConditionBuilder {
if !(*filterAdded) {
*filterAdded = true
filter = cond
} else {
filter = filter.And(cond)
}
+ *filterAdded = true
return filter
}
@@ -780,7 +781,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
} else {
if params.SearchField != nil {
searchFieldExpression := expression.Name("signature_reference_type").Equal(expression.Value(params.SearchField))
- filter = addConditionToFilter(filter, searchFieldExpression, &filterAdded)
+ filter = addAndCondition(filter, searchFieldExpression, &filterAdded)
}
if params.SignatureType != nil {
@@ -789,13 +790,13 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(*params.SignatureType))))
} else {
signatureTypeExpression := expression.Name("signature_type").Equal(expression.Value(params.SignatureType))
- filter = addConditionToFilter(filter, signatureTypeExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureTypeExpression, &filterAdded)
}
if *params.SignatureType == utils.ClaTypeCCLA {
signatureReferenceIDExpression := expression.Name("signature_reference_id").AttributeExists()
signatureUserCclaCompanyIDExpression := expression.Name("signature_user_ccla_company_id").AttributeNotExists()
- filter = addConditionToFilter(filter, signatureReferenceIDExpression, &filterAdded)
- filter = addConditionToFilter(filter, signatureUserCclaCompanyIDExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureReferenceIDExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureUserCclaCompanyIDExpression, &filterAdded)
}
}
@@ -808,7 +809,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
log.WithFields(f).Debugf("adding filters signature_reference_name_lower: %s or user_email: %s", strings.ToLower(utils.StringValue(params.SearchTerm)), strings.ToLower(utils.StringValue(params.SearchTerm)))
searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
}
}
@@ -817,21 +818,21 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
if filterAdded {
@@ -997,7 +998,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
} else {
if params.SearchField != nil {
searchFieldExpression := expression.Name("signature_reference_type").Equal(expression.Value(params.SearchField))
- filter = addConditionToFilter(filter, searchFieldExpression, &filterAdded)
+ filter = addAndCondition(filter, searchFieldExpression, &filterAdded)
}
if params.SignatureType != nil {
@@ -1006,13 +1007,13 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(*params.SignatureType))))
} else {
signatureTypeExpression := expression.Name("signature_type").Equal(expression.Value(params.SignatureType))
- filter = addConditionToFilter(filter, signatureTypeExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureTypeExpression, &filterAdded)
}
if *params.SignatureType == utils.ClaTypeCCLA {
signatureReferenceIDExpression := expression.Name("signature_reference_id").AttributeExists()
signatureUserCclaCompanyIDExpression := expression.Name("signature_user_ccla_company_id").AttributeNotExists()
- filter = addConditionToFilter(filter, signatureReferenceIDExpression, &filterAdded)
- filter = addConditionToFilter(filter, signatureUserCclaCompanyIDExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureReferenceIDExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureUserCclaCompanyIDExpression, &filterAdded)
}
}
@@ -1023,7 +1024,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
} else {
searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
}
}
@@ -1032,21 +1033,21 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
if len(params.Body) > 0 {
@@ -1056,9 +1057,9 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
referenceIDExpressions = append(referenceIDExpressions, expression.Value(value))
}
if len(referenceIDExpressions) == 1 {
- filter = addConditionToFilter(filter, expression.Name("signature_reference_id").In(referenceIDExpressions[0]), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_id").In(referenceIDExpressions[0]), &filterAdded)
} else if len(referenceIDExpressions) > 1 {
- filter = addConditionToFilter(filter, expression.Name("signature_reference_id").In(referenceIDExpressions[0], referenceIDExpressions[1:]...), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_id").In(referenceIDExpressions[0], referenceIDExpressions[1:]...), &filterAdded)
}
}
@@ -1231,21 +1232,21 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
limit := int64(10)
@@ -1393,10 +1394,10 @@ func (repo repository) ProjectSignatures(ctx context.Context, projectID string)
// Filter condition to cater for approved and signed signatures
signatureApprovedExpression := expression.Name("signature_approved").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureApprovedExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureApprovedExpression, &filterAdded)
signatureSignedExpression := expression.Name("signature_signed").Equal(expression.Value(true))
- filter = addConditionToFilter(filter, signatureSignedExpression, &filterAdded)
+ filter = addAndCondition(filter, signatureSignedExpression, &filterAdded)
if filterAdded {
builder = builder.WithFilter(filter)
@@ -2954,8 +2955,6 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
"searchTerm": utils.StringValue(searchTerm),
- "approved": utils.BoolValue(approved),
- "signed": utils.BoolValue(signed),
}
var filterAdded bool
@@ -2965,32 +2964,38 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
if approved != nil {
- filterAdded = true
+ f["approved"] = utils.BoolValue(approved)
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if signed != nil {
- filterAdded = true
+ f["signed"] = utils.BoolValue(signed)
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if approved == nil && signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
- filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addConditionToFilter(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addConditionToFilter(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
if searchTerm != nil {
- log.WithFields(f).Debugf("adding search term filter for : %s ", *searchTerm)
- searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(*searchTerm)).Or(expression.Name("user_email").Contains(strings.ToLower(*searchTerm)))
- filter = addConditionToFilter(filter, searchTermExpression, &filterAdded)
+ searchTermValue := utils.StringValue(searchTerm)
+ f["searchTerm"] = searchTermValue
+ log.WithFields(f).Debugf("adding search term filter for: '%s'", searchTermValue)
+ searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(searchTermValue)).
+ Or(expression.Name("user_email").Contains(strings.ToLower(searchTermValue))).
+ Or(expression.Name("user_github_username").Contains(strings.ToLower(searchTermValue))).
+ Or(expression.Name("user_docusign_name").Contains(strings.ToLower(searchTermValue)))
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
+ log.WithFields(f).Debugf("filter: %+v", filter)
+
// Use the builder to create the expression
expr, err := expression.NewBuilder().
WithKeyCondition(condition).
@@ -3024,10 +3029,6 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
queryInput.Limit = &pageSize
- if searchTerm != nil {
- searchTerm = aws.String(strings.ToLower(*searchTerm))
- }
-
// If we have the next key, set the exclusive start key value
if nextKey != "" {
log.WithFields(f).Debugf("Received a nextKey, value: %s", nextKey)
@@ -3064,7 +3065,7 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
return nil, unmarshallError
}
- intermediateResponse = append(intermediateResponse, repo.getIntermediateICLAResponse(f, dbSignatures, searchTerm)...)
+ intermediateResponse = append(intermediateResponse, repo.getIntermediateICLAResponse(f, dbSignatures)...)
log.WithFields(f).Debugf("LastEvaluatedKey: %+v", results.LastEvaluatedKey["signature_id"])
if results.LastEvaluatedKey["signature_id"] != nil {
@@ -3100,16 +3101,10 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
return out, nil
}
-func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures []ItemSignature, searchTerm *string) []*iclaSignatureWithDetails {
+func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures []ItemSignature) []*iclaSignatureWithDetails {
var intermediateResponse []*iclaSignatureWithDetails
for _, sig := range dbSignatures {
- if searchTerm != nil {
- if !strings.Contains(sig.SignatureReferenceNameLower, *searchTerm) {
- continue
- }
- }
-
// Set the signed date/time
var sigSignedTime string
// Use the user docusign date signed value if it is present - older signatures do not have this
@@ -3117,15 +3112,15 @@ func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures
// Put the date into a standard format
t, err := utils.ParseDateTime(sig.UserDocusignDateSigned)
if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to parse signature docusign date signed time")
+ log.WithFields(f).WithError(err).Warnf("unable to parse signature docusign date signed time: %s", sig.UserDocusignDateSigned)
} else {
sigSignedTime = utils.TimeToString(t)
}
- } else {
+ } else if sig.DateCreated != "" {
// Put the date into a standard format
t, err := utils.ParseDateTime(sig.DateCreated)
if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to parse signature date created time")
+ log.WithFields(f).WithError(err).Warnf("unable to parse signature date created time: %s", sig.DateCreated)
} else {
sigSignedTime = utils.TimeToString(t)
}
@@ -3222,8 +3217,21 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
condition = condition.And(expression.Key("sigtype_signed_approved_id").BeginsWith(sortKeyPrefix))
}
+ // Create our builder
+ builder := expression.NewBuilder().WithKeyCondition(condition).WithProjection(buildProjection())
+
+ if searchTerm != nil {
+ searchTermValue := utils.StringValue(searchTerm)
+ f["searchTerm"] = searchTermValue
+ log.WithFields(f).Debugf("adding search term filter for: '%s'", searchTermValue)
+ builder.WithFilter(expression.Name("signature_reference_name_lower").Contains(strings.ToLower(searchTermValue)).
+ Or(expression.Name("user_email").Contains(strings.ToLower(searchTermValue))).
+ Or(expression.Name("github_username").Contains(strings.ToLower(searchTermValue))).
+ Or(expression.Name("userDocusignName").Contains(strings.ToLower(searchTermValue))))
+ }
+
// Use the builder to create the expression
- expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(buildProjection()).Build()
+ expr, err := builder.Build()
if err != nil {
log.WithFields(f).Warnf("error building expression for get cla group icla signatures, claGroupID: %s, error: %v",
claGroupID, err)
@@ -3236,15 +3244,13 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
ExpressionAttributeValues: expr.Values(),
KeyConditionExpression: expr.KeyCondition(),
ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
TableName: aws.String(repo.signatureTableName),
IndexName: aws.String(SignatureProjectIDSigTypeSignedApprovedIDIndex),
Limit: aws.Int64(HugePageSize),
}
out := &models.CorporateContributorList{List: make([]*models.CorporateContributor, 0)}
- if searchTerm != nil {
- searchTerm = aws.String(strings.ToLower(*searchTerm))
- }
for {
// Make the DynamoDB Query API call
@@ -3266,12 +3272,6 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
log.WithFields(f).Debugf("located %d signatures...", len(dbSignatures))
for _, sig := range dbSignatures {
- if searchTerm != nil {
- if !strings.Contains(sig.SignatureReferenceNameLower, *searchTerm) {
- continue
- }
- }
-
var sigCreatedTime = sig.DateCreated
t, err := utils.ParseDateTime(sig.DateCreated)
if err != nil {
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 15195a8e6..81cfc3921 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -850,7 +850,6 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
log.Warn(msg)
return signatures.NewGetProjectCompanyEmployeeSignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("searching for ICLA signatures...")
From 969081e4c5b3aff5f33b24750b16acc8228fbabb Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 1 Jun 2021 08:12:52 -0700
Subject: [PATCH 0300/1276] Resolved PCC-1014 Corporate Signature Query (#2971)
---
cla-backend-go/v2/signatures/handlers.go | 32 ++++++++++++++----------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 81cfc3921..07a5010ee 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -369,8 +369,6 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": params.ClaGroupID,
"signatureType": params.SignatureType,
- "approved": utils.BoolValue(params.Approved),
- "signed": utils.BoolValue(params.Signed),
}
log.WithFields(f).Debug("looking up CLA Group by ID...")
@@ -387,7 +385,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
f["foundationSFID"] = claGroupModel.FoundationSFID
// Check to see if this CLA Group is configured for ICLAs...
- if !claGroupModel.ProjectICLAEnabled {
+ if params.SignatureType != nil && utils.StringValue(params.ClaType) == utils.ClaTypeICLA && !claGroupModel.ProjectICLAEnabled {
log.WithFields(f).Warn(iclaNotSupportedForCLAGroup)
// Return 200 as the retool UI can't handle 400's
return signatures.NewGetProjectSignaturesOK().WithXRequestID(reqID).WithPayload(&models.Signatures{
@@ -396,19 +394,27 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
Signatures: []*models.Signature{}, // empty list
TotalCount: 0,
})
- //return signatures.NewGetProjectSignaturesBadRequest().WithXRequestID(reqID).WithPayload(
- // utils.ErrorResponseBadRequest(reqID, iclaNotSupportedForCLAGroup))
}
- if false {
- log.WithFields(f).Debug("checking access control permissions for user...")
- if !isUserHaveAccessToCLAGroupProjects(ctx, authUser, params.ClaGroupID, projectClaGroupsRepo, projectRepo) {
- msg := fmt.Sprintf("user '%s' is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
- log.Warn(msg)
- return signatures.NewGetProjectSignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
- }
- log.WithFields(f).Debug("user has access for this query")
+ // Check to see if this CLA Group is configured for CCLAs...
+ if params.SignatureType != nil && utils.StringValue(params.ClaType) == utils.ClaTypeCCLA && !claGroupModel.ProjectCCLAEnabled {
+ log.WithFields(f).Warn(cclaNotSupportedForCLAGroup)
+ // Return 200 as the retool UI can't handle 400's
+ return signatures.NewGetProjectSignaturesOK().WithXRequestID(reqID).WithPayload(&models.Signatures{
+ ProjectID: params.ClaGroupID,
+ ResultCount: 0,
+ Signatures: []*models.Signature{}, // empty list
+ TotalCount: 0,
+ })
+ }
+
+ log.WithFields(f).Debug("checking access control permissions for user...")
+ if !isUserHaveAccessToCLAGroupProjects(ctx, authUser, params.ClaGroupID, projectClaGroupsRepo, projectRepo) {
+ msg := fmt.Sprintf("user '%s' is not authorized to view project ICLA signatures any scope of project", authUser.UserName)
+ log.Warn(msg)
+ return signatures.NewGetProjectSignaturesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
+ log.WithFields(f).Debug("user has access for this query")
log.WithFields(f).Debug("loading project signatures...")
projectSignatures, err := v1SignatureService.GetProjectSignatures(ctx, v1Signatures.GetProjectSignaturesParams{
From dff91fc8dc31fa405799a094e6543fe0a4f3ba90 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Jun 2021 17:43:45 -0700
Subject: [PATCH 0301/1276] Bump urllib3 from 1.25.9 to 1.26.5 in /cla-backend
(#2972)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index c0f65349f..2b4150faf 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -53,7 +53,7 @@ six==1.13.0
soupsieve==1.9.5
termcolor==1.1.0
typed-ast==1.4.1
-urllib3==1.25.9
+urllib3==1.26.5
vintage==0.4.1
wcwidth==0.1.7
Werkzeug==0.15.5
From 2a8981b1616541d23321c52320f97c37c9d3a999 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 3 Jun 2021 08:21:23 -0700
Subject: [PATCH 0302/1276] Revert "Bump urllib3 from 1.25.9 to 1.26.5 in
/cla-backend" (#2974)
---
cla-backend/requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt
index 2b4150faf..c0f65349f 100644
--- a/cla-backend/requirements.txt
+++ b/cla-backend/requirements.txt
@@ -53,7 +53,7 @@ six==1.13.0
soupsieve==1.9.5
termcolor==1.1.0
typed-ast==1.4.1
-urllib3==1.26.5
+urllib3==1.25.9
vintage==0.4.1
wcwidth==0.1.7
Werkzeug==0.15.5
From 872ddc2b40dbadecef20e6e5cd4de9f738505f2a Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Thu, 3 Jun 2021 18:41:11 +0300
Subject: [PATCH 0303/1276] [#PCC-494]Feature/ GH Organization CRUD (#2973)
Co-authored-by: Harold Wanyama
---
cla-backend-go/github_organizations/mock.go | 8 +-
cla-backend-go/github_organizations/models.go | 2 +
.../github_organizations/repository.go | 99 +++++++++++++------
.../github_organizations/service.go | 2 +-
.../swagger/common/github-organization.yaml | 4 +
.../v2/github_organizations/service.go | 2 +-
6 files changed, 81 insertions(+), 36 deletions(-)
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index 48f1c20bc..29c6e5215 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -143,15 +143,15 @@ func (mr *MockRepositoryMockRecorder) GetGithubOrganizationsByParent(arg0, arg1
}
// UpdateGithubOrganization mocks base method
-func (m *MockRepository) UpdateGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 string, arg5 bool) error {
+func (m *MockRepository) UpdateGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 string, arg5 bool, arg6 *bool) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateGithubOrganization", arg0, arg1, arg2, arg3, arg4, arg5)
+ ret := m.ctrl.Call(m, "UpdateGithubOrganization", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateGithubOrganization indicates an expected call of UpdateGithubOrganization
-func (mr *MockRepositoryMockRecorder) UpdateGithubOrganization(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+func (mr *MockRepositoryMockRecorder) UpdateGithubOrganization(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGithubOrganization), arg0, arg1, arg2, arg3, arg4, arg5)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGithubOrganization), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
}
diff --git a/cla-backend-go/github_organizations/models.go b/cla-backend-go/github_organizations/models.go
index b63ce0958..955ee50ee 100644
--- a/cla-backend-go/github_organizations/models.go
+++ b/cla-backend-go/github_organizations/models.go
@@ -14,6 +14,7 @@ type GithubOrganization struct {
OrganizationNameLower string `json:"organization_name_lower,omitempty"`
OrganizationSFID string `json:"organization_sfid,omitempty"`
ProjectSFID string `json:"project_sfid"`
+ Enabled bool `json:"enabled"`
AutoEnabled bool `json:"auto_enabled"`
BranchProtectionEnabled bool `json:"branch_protection_enabled"`
AutoEnabledClaGroupID string `json:"auto_enabled_cla_group_id,omitempty"`
@@ -29,6 +30,7 @@ func ToModel(in *GithubOrganization) *models.GithubOrganization {
OrganizationName: in.OrganizationName,
OrganizationSfid: in.OrganizationSFID,
Version: in.Version,
+ Enabled: in.Enabled,
AutoEnabled: in.AutoEnabled,
AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
BranchProtectionEnabled: in.BranchProtectionEnabled,
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index e63c20257..21ed9e985 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -42,7 +42,7 @@ type Repository interface {
GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error)
- UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
+ UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error
}
@@ -108,12 +108,15 @@ func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectS
}
// Attempt to simply update the existing record - we should only have one
+ // activate GH org by updating the enabled flag
+ enabled := true
updateErr := repo.UpdateGithubOrganization(ctx,
projectSFID,
utils.StringValue(input.OrganizationName),
autoEnabled,
autoEnabledCLAGroupID,
branchProtectionEnabled,
+ &enabled,
)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warn("unable to update existing github organization record")
@@ -141,6 +144,7 @@ func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectS
// No existing records - create one
_, currentTime := utils.CurrentTime()
+ enabled := true
githubOrg := &GithubOrganization{
DateCreated: currentTime,
DateModified: currentTime,
@@ -149,6 +153,7 @@ func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectS
OrganizationNameLower: strings.ToLower(*input.OrganizationName),
OrganizationSFID: parentProjectSFID,
ProjectSFID: projectSFID,
+ Enabled: aws.BoolValue(&enabled),
AutoEnabled: aws.BoolValue(input.AutoEnabled),
AutoEnabledClaGroupID: input.AutoEnabledClaGroupID,
BranchProtectionEnabled: aws.BoolValue(input.BranchProtectionEnabled),
@@ -369,7 +374,7 @@ func (repo repository) GetGithubOrganization(ctx context.Context, githubOrganiza
}
// UpdateGithubOrganization updates the specified GitHub organization based on the update model provided
-func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.UpdateGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -393,34 +398,46 @@ func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID
return lookupErr
}
+ expressionAttributeNames := map[string]*string{
+ "#A": aws.String("auto_enabled"),
+ "#C": aws.String("auto_enabled_cla_group_id"),
+ "#B": aws.String("branch_protection_enabled"),
+ "#M": aws.String("date_modified"),
+ }
+ expressionAttributeValues := map[string]*dynamodb.AttributeValue{
+ ":a": {
+ BOOL: aws.Bool(autoEnabled),
+ },
+ ":c": {
+ S: aws.String(autoEnabledClaGroupID),
+ },
+ ":b": {
+ BOOL: aws.Bool(branchProtectionEnabled),
+ },
+ ":m": {
+ S: aws.String(currentTime),
+ },
+ }
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m,"
+
+ if enabled != nil {
+ expressionAttributeNames["#E"] = aws.String("enabled")
+ expressionAttributeValues[":e"] = &dynamodb.AttributeValue{
+ BOOL: aws.Bool(*enabled),
+ }
+ updateExpression = updateExpression + " #E = :e "
+ }
+
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
"organization_name": {
S: aws.String(githubOrg.OrganizationName),
},
},
- ExpressionAttributeNames: map[string]*string{
- "#A": aws.String("auto_enabled"),
- "#C": aws.String("auto_enabled_cla_group_id"),
- "#B": aws.String("branch_protection_enabled"),
- "#M": aws.String("date_modified"),
- },
- ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
- ":a": {
- BOOL: aws.Bool(autoEnabled),
- },
- ":c": {
- S: aws.String(autoEnabledClaGroupID),
- },
- ":b": {
- BOOL: aws.Bool(branchProtectionEnabled),
- },
- ":m": {
- S: aws.String(currentTime),
- },
- },
- UpdateExpression: aws.String("SET #A = :a, #C = :c, #B = :b, #M = :m"),
- TableName: aws.String(repo.githubOrgTableName),
+ ExpressionAttributeNames: expressionAttributeNames,
+ ExpressionAttributeValues: expressionAttributeValues,
+ UpdateExpression: &updateExpression,
+ TableName: aws.String(repo.githubOrgTableName),
}
log.WithFields(f).Debug("updating github organization record...")
@@ -456,14 +473,36 @@ func (repo repository) DeleteGithubOrganization(ctx context.Context, projectSFID
}
log.WithFields(f).Debug("Deleting GitHub organization...")
- _, err := repo.dynamoDBClient.DeleteItem(&dynamodb.DeleteItemInput{
- Key: map[string]*dynamodb.AttributeValue{
- "organization_name": {
- S: aws.String(githubOrganizationName),
+ // Update enabled flag as false
+ _, currentTime := utils.CurrentTime()
+ note := fmt.Sprintf("Enabled set to false due to org deletion at %s ", currentTime)
+ _, err := repo.dynamoDBClient.UpdateItem(
+ &dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "organization_name": {
+ S: aws.String(githubOrganizationName),
+ },
+ },
+ ExpressionAttributeNames: map[string]*string{
+ "#E": aws.String("enabled"),
+ "#N": aws.String("note"),
+ "#D": aws.String("date_modified"),
},
+ ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
+ ":e": {
+ BOOL: aws.Bool(false),
+ },
+ ":n": {
+ S: aws.String(note),
+ },
+ ":d": {
+ S: aws.String(currentTime),
+ },
+ },
+ UpdateExpression: aws.String("SET #E = :e, #N = :n, #D = :d"),
+ TableName: aws.String(repo.githubOrgTableName),
},
- TableName: aws.String(repo.githubOrgTableName),
- })
+ )
if err != nil {
errMsg := fmt.Sprintf("error deleting github organization: %s - %+v", githubOrgName, err)
log.WithFields(f).Warnf(errMsg)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index f434d64af..f1556b6fb 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -165,7 +165,7 @@ func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID strin
return err
}
}
- return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled)
+ return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
}
func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
diff --git a/cla-backend-go/swagger/common/github-organization.yaml b/cla-backend-go/swagger/common/github-organization.yaml
index 327bd724f..d66f4d5b0 100644
--- a/cla-backend-go/swagger/common/github-organization.yaml
+++ b/cla-backend-go/swagger/common/github-organization.yaml
@@ -28,6 +28,10 @@ properties:
projectSFID:
type: string
example: "a0941000002wBz4AAA"
+ enabled:
+ type: boolean
+ description: Flag that indicates whether this Github Organization is active
+ x-omitempty: false
autoEnabled:
type: boolean
description: Flag to indicate if this GitHub Organization is configured to allow new repositories to be auto-enabled/auto-enrolled in EasyCLA.
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index b0de5faea..d70513a83 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -310,7 +310,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
- return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled)
+ return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
}
func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
From 2bd40e39d1a468a674f44e910301c648d1661f1e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 3 Jun 2021 12:06:18 -0700
Subject: [PATCH 0304/1276] Added Build for Mac Arm64 (#2976)
---
cla-backend-go/.gitignore | 2 +-
cla-backend-go/Makefile | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/.gitignore b/cla-backend-go/.gitignore
index b73124e8a..9dc1cd0b5 100644
--- a/cla-backend-go/.gitignore
+++ b/cla-backend-go/.gitignore
@@ -3,7 +3,7 @@
# Project specific ignores
cla-backend-go
cla
-cla-mac
+cla-mac*
backend-aws-lambda
backend-aws-lambda-mac
backend-aws-lambda-linux
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 2c3577f48..6f09624a3 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -62,7 +62,7 @@ setup-deploy:
@yarn add serverless && yarn install
clean:
- @rm -rf cla cla-mac cla-linux \
+ @rm -rf cla cla-mac* cla-linux \
./v2/project-service/client ./v2/project-service/models \
./v2/organization-service/client ./v2/organization-service/models \
./v2/user-service/client ./v2/user-service/models \
@@ -206,6 +206,11 @@ build-mac: deps
env GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(SERVICE)-mac main.go
@chmod +x $(SERVICE)-mac
+build-mac-arm: deps
+ @echo "Building Mac OSX Arm64 binary..."
+ env GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(SERVICE)-mac-arm64 main.go
+ @chmod +x $(SERVICE)-mac
+
rebuild-mac: clean fmt build-mac lint
./$(SERVICE)-mac
From 0268ca26355e9f34e8de99575d22c4ee01297e61 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 4 Jun 2021 12:28:49 -0700
Subject: [PATCH 0305/1276] Updated Mac Arm64 Build Output File (#2977)
---
cla-backend-go/Makefile | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 6f09624a3..468f39d57 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -206,10 +206,10 @@ build-mac: deps
env GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(SERVICE)-mac main.go
@chmod +x $(SERVICE)-mac
-build-mac-arm: deps
+build-mac-arm64: deps
@echo "Building Mac OSX Arm64 binary..."
env GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(SERVICE)-mac-arm64 main.go
- @chmod +x $(SERVICE)-mac
+ @chmod +x $(SERVICE)-mac-arm64
rebuild-mac: clean fmt build-mac lint
./$(SERVICE)-mac
@@ -258,7 +258,6 @@ build-metrics-report-lambda-mac: deps
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(METRICS_REPORT_BIN)-mac cmd/metrics_report_lambda/main.go
@chmod +x $(METRICS_REPORT_BIN)-mac
-
build-dynamo-events-lambda: build-dynamo-events-lambda-linux
build-dynamo-events-lambda-linux: deps
@echo "Building a statically linked Linux amd64 binary..."
From 613d38c25728c7c96adae24255f0dfe5be249aa4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 7 Jun 2021 11:47:04 -0700
Subject: [PATCH 0306/1276] Documentation Updates (#2978)
---
README.md | 37 ++++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 425dee4ee..97e882b36 100644
--- a/README.md
+++ b/README.md
@@ -16,30 +16,45 @@ This platform supports both GitHub and Gerrit source code repositories. Addition
[EasyCLA](#easycla-architecture)
-Besides integration with Auth0 and Salesforce, the CLA system has the following third party services:
+The EasyCL system leverages the following third party services:
* [Docusign](https://www.docusign.com/) for CLA agreement e-sign flow
-* [Docraptor](https://docraptor.com/) for converting html CLA template to PDF file
+* [Docraptor](https://docraptor.com/) for converting CLA templates into PDF files
+* [GitHub](https://github.com/) for GitHub PR CLA authorization checking/gating
+* Gerrit for CLA authorization review checking/gating
+* Auth0 For Single Sign On
+* Salesforce via the LFX Platform APIs
## CLA Backend
The CLA project has two backend components:
-* The majority of the backend APIs are implemented in python, and can be found in the [cla-backend](cla-backend/) directory.
-
-* Recent backend development is implemented in Golang, and can be found in the
-[cla-backend-go](cla-backend-go/) directory. In particular, this backend contains APIs powering
-Automated Templates, GitHub Approval Lists, and Duplicate Company handling in the
-Corporate Console.
+* Python - some older APIs are implemented in python and can be found in the [cla-backend](cla-backend) directory.
+* GoLang - Most of the backend development is implemented in Golang, and can be found in the
+ [cla-backend-go](cla-backend-go) directory. In particular, this backend contains APIs powering most of the v2 APIs
+ which integrate with the LFX Platform (including Salesforce data), and the LFX platform permissions model.
## CLA Frontend
-CLA frontend consists of three independent SPA built with [Ionic](https://ionicframework.com/) framework.
+For EasyCLA version 2, the various consoles are managed in separate repositories.
+
+* [Project Control Center](https://projectadmin.lfx.linuxfoundation.org/) contains all the old v1 Project Console
+ capabilities plus many new features. This new console includes not only the EasyCLA components, but also the project
+ related features for LF ITX and other LFX Platform projects.
+* [Corporate Console](https://organization.lfx.linuxfoundation.org/company/dashboard) contains the old v1 Company Console
+ capabilities. This new console includes not only the EasyCLA components, but also the company related features for LF
+ ITX and other LFX Platform projects.
+* [Contributor Console](https://github.com/communitybridge/easycla-contributor-console) contains the old v1 Contributor Console
+ capabilities with new features that integrate with the LFX Platform (including the Salesforce data).
-* [cla-frontend-project-console](cla-frontend-project-console/) for the LinuxFoundation director/admin/user to manage project CLA
-* [cla-frontend-corporate-console](cla-frontend-corporate-console/) for any concrete company CCLA manager to sign a CCLA and manage employee CLA approved list
+For EasyCLA version 1, the consoles are:
+
+* [cla-frontend-project-console](cla-frontend-project-console) for the LinuxFoundation director/admin/user to manage project CLA
+* [cla-frontend-corporate-console](cla-frontend-corporate-console) for any concrete company CCLA manager to sign a CCLA and manage employee CLA approved list
* [cla-frontend-contributor-console](cla-frontend-contributor-console) for any project contributor to sign ICLA or CCLA
+These CLA frontend components of three independent SPA built with [Ionic](https://ionicframework.com/) framework.
+
## EasyCLA Architecture
The following diagram explains the EasyCLA architecture.
From a35a7dfd5cf2223a492a8dc5b4127ea2879241b7 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 7 Jun 2021 17:20:52 -0700
Subject: [PATCH 0307/1276] Minor README Update (#2979)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 97e882b36..22e37ca71 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ The CLA project has two backend components:
## CLA Frontend
-For EasyCLA version 2, the various consoles are managed in separate repositories.
+For EasyCLA version 2, all three consoles are hosted in separate repositories.
* [Project Control Center](https://projectadmin.lfx.linuxfoundation.org/) contains all the old v1 Project Console
capabilities plus many new features. This new console includes not only the EasyCLA components, but also the project
From b729646c5c2bee47b4c56d15492763b0e39db042 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 8 Jun 2021 11:45:52 -0700
Subject: [PATCH 0308/1276] Resolved V1 Signature Query Issue (#2983)
---
cla-backend-go/signatures/repository.go | 45 +++++++++++++------------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index dc3a0f6ad..bf63f8f91 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -718,15 +718,14 @@ func (repo repository) GetSignatureACL(ctx context.Context, signatureID string)
return dbModel.SignatureACL, nil
}
+// Adds the specified expression to the current filter using the And operator. This routine checks the filter added flag
+// to determine if a previous filter was set. After this function executes, the filterAdded value will be set to true.
func addAndCondition(filter expression.ConditionBuilder, cond expression.ConditionBuilder, filterAdded *bool) expression.ConditionBuilder {
- if !(*filterAdded) {
- *filterAdded = true
- filter = cond
- } else {
- filter = filter.And(cond)
+ if *filterAdded {
+ return filter.And(cond)
}
*filterAdded = true
- return filter
+ return cond
}
// GetProjectSignatures returns a list of signatures for the specified project
@@ -743,8 +742,6 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
"pageSize": aws.Int64Value(params.PageSize),
"nextKey": aws.StringValue(params.NextKey),
"sortOrder": aws.StringValue(params.SortOrder),
- "approved": utils.BoolValue(params.Approved),
- "signed": utils.BoolValue(params.Signed),
}
// Always sort by date
@@ -757,42 +754,48 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// This is the key we want to match
condition := expression.Key("signature_project_id").Equal(expression.Value(params.ProjectID))
+ builder := expression.NewBuilder().WithKeyCondition(condition).WithProjection(buildProjection())
- builder := expression.NewBuilder().WithProjection(buildProjection())
var filter expression.ConditionBuilder
- var filterAdded bool
+ var filterAdded = false
if params.ClaType != nil {
- filterAdded = true
if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
+ log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
+ log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
And(expression.Name("signature_user_ccla_company_id").AttributeExists())
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
+ log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCCLA, utils.SignatureReferenceTypeCompany)
filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)).
And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany))).
And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
}
} else {
if params.SearchField != nil {
+ log.WithFields(f).Debugf("adding filters: signature_type: %s", expression.Value(params.SearchField))
searchFieldExpression := expression.Name("signature_reference_type").Equal(expression.Value(params.SearchField))
filter = addAndCondition(filter, searchFieldExpression, &filterAdded)
}
if params.SignatureType != nil {
if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" && (params.FullMatch != nil && !*params.FullMatch) {
+ log.WithFields(f).Debugf("adding filters: signature_type: %s", strings.ToLower(utils.StringValue(params.SignatureType)))
indexName = SignatureProjectIDTypeIndex
- condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(*params.SignatureType))))
+ condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SignatureType)))))
} else {
- signatureTypeExpression := expression.Name("signature_type").Equal(expression.Value(params.SignatureType))
+ log.WithFields(f).Debugf("adding filters: signature_type: %s", utils.StringValue(params.SignatureType))
+ signatureTypeExpression := expression.Name("signature_type").Equal(expression.Value(utils.StringValue(params.SignatureType)))
filter = addAndCondition(filter, signatureTypeExpression, &filterAdded)
}
if *params.SignatureType == utils.ClaTypeCCLA {
+ log.WithFields(f).Debug("adding filters: signature_reference_id: exists, signature_user_ccla_company_id: not exists")
signatureReferenceIDExpression := expression.Name("signature_reference_id").AttributeExists()
signatureUserCclaCompanyIDExpression := expression.Name("signature_user_ccla_company_id").AttributeNotExists()
filter = addAndCondition(filter, signatureReferenceIDExpression, &filterAdded)
@@ -815,13 +818,11 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
if params.Approved != nil {
- filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
- filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
@@ -829,22 +830,22 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
- filterAdded = true
log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
- filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(true)).
+ And(expression.Name("signature_signed").Equal(expression.Value(true)))
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
+ log.WithFields(f).Debugf("filterAdded: %t", filterAdded)
if filterAdded {
+ log.WithFields(f).Debugf("filter: %+v", filter)
builder = builder.WithFilter(filter)
}
- builder = builder.WithKeyCondition(condition)
- // Use the nice builder to create the expression
+ // Use the builder to create the expression
expr, err := builder.Build()
if err != nil {
- log.WithFields(f).Warnf("error building expression for project signature query, projectID: %s, error: %v",
- params.ProjectID, err)
+ log.WithFields(f).WithError(err).Warnf("error building expression for project signature query, projectID: %s, error: %v", params.ProjectID, err)
return nil, err
}
From aead96ad5d11524346efe4a8255eaf0ac3d0da64 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Wed, 9 Jun 2021 17:58:57 +0300
Subject: [PATCH 0309/1276] Feature/ Get GH Org (#2984)
---
cla-backend-go/github_organizations/repository.go | 3 +++
cla-backend/cla/models/dynamo_models.py | 11 ++++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 21ed9e985..9d12823ab 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -199,6 +199,9 @@ func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID s
condition := expression.Key("project_sfid").Equal(expression.Value(projectSFID))
builder := expression.NewBuilder().WithKeyCondition(condition)
+ filter := expression.Name("enabled").Equal(expression.Value(true))
+ builder = builder.WithFilter(filter)
+
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index a462d4eeb..c1d2d342f 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -3526,6 +3526,7 @@ class Meta:
organization_company_id = UnicodeAttribute(null=True)
auto_enabled = BooleanAttribute(null=True)
branch_protection_enabled = BooleanAttribute(null=True)
+ enabled = BooleanAttribute(null=True)
note = UnicodeAttribute(null=True)
@@ -3536,7 +3537,7 @@ class GitHubOrg(model_interfaces.GitHubOrg): # pylint: disable=too-many-public-
def __init__(
self, organization_name=None, organization_installation_id=None, organization_sfid=None,
- auto_enabled=False, branch_protection_enabled=False, note=None,
+ auto_enabled=False, branch_protection_enabled=False, note=None, enabled=True
):
super(GitHubOrg).__init__()
self.model = GitHubOrgModel()
@@ -3548,6 +3549,7 @@ def __init__(
self.model.auto_enabled = auto_enabled
self.model.branch_protection_enabled = branch_protection_enabled
self.model.note = note
+ self.model.enabled = enabled
def __str__(self):
return (
@@ -3559,6 +3561,7 @@ def __str__(self):
f'auto_enabled: {self.model.auto_enabled},'
f'branch_protection_enabled: {self.model.branch_protection_enabled},'
f'note: {self.model.note}'
+ f'enabled: {self.model.enabled}'
)
def to_dict(self):
@@ -3611,6 +3614,9 @@ def get_note(self):
:rtype: str
"""
return self.model.note
+
+ def get_enabled(self):
+ return self.model.enabled
def set_organization_name(self, organization_name):
self.model.organization_name = organization_name
@@ -3640,6 +3646,9 @@ def set_branch_protection_enabled(self, branch_protection_enabled):
def set_note(self, note):
self.model.note = note
+
+ def set_enabled(self, enabled):
+ self.model.enabled = enabled
def get_organization_by_sfid(self, sfid) -> List:
organization_generator = self.model.organization_sfid_index.query(sfid)
From 08aa27e83c11d2cead6ed84ee479112f69ecc8c2 Mon Sep 17 00:00:00 2001
From: Steve Winslow
Date: Thu, 10 Jun 2021 11:01:02 -0400
Subject: [PATCH 0310/1276] Fix typo in description of licenses (#2986)
---
contributing.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contributing.md b/contributing.md
index 66e596871..7b7df513d 100644
--- a/contributing.md
+++ b/contributing.md
@@ -16,7 +16,7 @@ New **code files** should include a [short-form SPDX ID](https://spdx.org/ids) a
// SPDX-License-Identifier: MIT
```
-New **documentation files** should include a [short-form SPDX ID](https://spdx.org/ids) at the top, indicating the project license for code, which is CC-BY-4.0. This should look like the following:
+New **documentation files** should include a [short-form SPDX ID](https://spdx.org/ids) at the top, indicating the project license for documentation, which is CC-BY-4.0. This should look like the following:
```text
SPDX-License-Identifier: CC-BY-4.0
From 0b6fc5e5b1ca952655d54b56abd1d0842ccf81b1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 11 Jun 2021 10:50:55 -0700
Subject: [PATCH 0311/1276] Removed Landing Page Request Access Button/Link
(#2987)
Signed-off-by: David Deal
---
.../cla-console-section.component.html | 5 ----
.../cla-console-section.component.ts | 25 ++++++++-----------
2 files changed, 11 insertions(+), 19 deletions(-)
diff --git a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
index 65907e703..2115f109d 100644
--- a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
+++ b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.html
@@ -35,11 +35,6 @@
-
-
-
-
diff --git a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.ts b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.ts
index 2eb973ede..9d0d38c99 100644
--- a/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.ts
+++ b/cla-landing-page/src/app/components/cla-console-section/cla-console-section.component.ts
@@ -11,10 +11,11 @@ import { LandingPageService } from 'src/app/service/landing-page.service';
import { environment } from 'src/environments/environment';
declare let process: any;
+
@Component({
selector: 'app-cla-console-section',
templateUrl: './cla-console-section.component.html',
- styleUrls: ['./cla-console-section.component.scss']
+ styleUrls: ['./cla-console-section.component.scss'],
})
export class ClaConsoleSectionComponent implements OnInit {
@Input() consoleMetadata: any;
@@ -32,7 +33,7 @@ export class ClaConsoleSectionComponent implements OnInit {
private storageService: StorageService,
private router: ActivatedRoute,
private modalService: NgbModal,
- private landingPageService: LandingPageService
+ private landingPageService: LandingPageService,
) {
if (!this.landingPageService.hasEventInitilize) {
this.landingPageService.hasEventInitilize = true;
@@ -51,7 +52,7 @@ export class ClaConsoleSectionComponent implements OnInit {
this.version = this.router.snapshot.queryParamMap.get('version');
const element: any = document.getElementById('lfx-header');
let projectConsoleUrl = EnvConfig.default[AppSettings.PROJECT_CONSOLE_LINK] + '#/login';
- let corporateConsoleUrl = EnvConfig.default[AppSettings.CORPORATE_CONSOLE_LINK] + '#/login'
+ let corporateConsoleUrl = EnvConfig.default[AppSettings.CORPORATE_CONSOLE_LINK] + '#/login';
if (this.version === '2') {
// Set redirect URL to new V2 console.
projectConsoleUrl = EnvConfig.default[AppSettings.PROJECT_CONSOLE_LINK_V2];
@@ -60,16 +61,16 @@ export class ClaConsoleSectionComponent implements OnInit {
this.links = [
{
title: 'Project Login',
- emit: "project-login-event"
+ emit: 'project-login-event',
},
{
title: 'CLA Manager Login',
- emit: "corporate-login-event"
+ emit: 'corporate-login-event',
},
{
title: 'Developer',
- url: AppSettings.CONTRIBUTORS_LEARN_MORE
- }
+ url: AppSettings.CONTRIBUTORS_LEARN_MORE,
+ },
];
element.links = this.links;
}
@@ -92,14 +93,10 @@ export class ClaConsoleSectionComponent implements OnInit {
this.modelRef = this.modalService.open(this.versionModal, {
centered: true,
backdrop: 'static',
- keyboard: false
+ keyboard: false,
});
}
- onClickRequestAccess() {
- window.open(AppSettings.REQUEST_ACCESS_LINK, '_blank');
- }
-
onClickLearnMore() {
window.open(AppSettings.CONTRIBUTORS_LEARN_MORE, '_blank');
}
@@ -110,7 +107,7 @@ export class ClaConsoleSectionComponent implements OnInit {
onClickVersionProceed() {
if (this.selectedVersion === '') {
- this.error = 'Please select a EasyCLA Version.'
+ this.error = 'Please select a EasyCLA Version.';
} else {
this.redirectAsPerTypeAndVersion(this.consoleType, this.selectedVersion);
}
@@ -118,7 +115,7 @@ export class ClaConsoleSectionComponent implements OnInit {
redirectAsPerTypeAndVersion(type: string, version: string) {
let projectConsoleUrl = EnvConfig.default[AppSettings.PROJECT_CONSOLE_LINK] + '#/login';
- let corporateConsoleUrl = EnvConfig.default[AppSettings.CORPORATE_CONSOLE_LINK] + '#/login'
+ let corporateConsoleUrl = EnvConfig.default[AppSettings.CORPORATE_CONSOLE_LINK] + '#/login';
if (version === '2') {
projectConsoleUrl = EnvConfig.default[AppSettings.PROJECT_CONSOLE_LINK_V2];
From 8d980ed0aa04072a23ff84e6bca7fe63dfb8f0bf Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 11 Jun 2021 13:19:46 -0700
Subject: [PATCH 0312/1276] Resolved PCC-1134 - Updated Signature Tall Query
(#2988)
---
cla-backend-go/signatures/repository.go | 138 +++++++++++++-----------
cla-backend-go/v2/cla_groups/service.go | 25 ++++-
2 files changed, 96 insertions(+), 67 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index bf63f8f91..6f51ab76b 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -460,9 +460,10 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
// These are the keys we want to match for an ICLA Signature with a given CLA Group and User ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(userID)))
- filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ var filter expression.ConditionBuilder
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
if approved != nil {
filterAdded = true
@@ -573,21 +574,20 @@ func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, co
// These are the keys we want to match for an CCLA Signature with a given CLA Group and Company ID
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
- filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
- And(expression.Name("signature_reference_type").Equal(expression.Value("company"))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ var filter expression.ConditionBuilder
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
if approved != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(approved))
- searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
- filter = addAndCondition(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved))), &filterAdded)
}
if signed != nil {
filterAdded = true
log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(signed))
- searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed)))
- filter = addAndCondition(filter, searchTermExpression, &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(signed))), &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
@@ -761,21 +761,20 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
if params.ClaType != nil {
if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
- log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
-
+ log.WithFields(f).Debugf("adding ICLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
- log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeExists())
+ log.WithFields(f).Debugf("adding ECLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeExists(), &filterAdded)
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
- log.WithFields(f).Debugf("adding filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCCLA, utils.SignatureReferenceTypeCompany)
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ log.WithFields(f).Debugf("adding CCLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCCLA, utils.SignatureReferenceTypeCompany)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
}
} else {
if params.SearchField != nil {
@@ -818,27 +817,24 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}
if params.Approved != nil {
- log.WithFields(f).Debugf("adding filter signature_approved: %t", aws.BoolValue(params.Approved))
- searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(params.Approved)))
+ log.WithFields(f).Debugf("adding signature_approved: %t filter", aws.BoolValue(params.Approved))
+ searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(params.Approved))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
if params.Signed != nil {
- log.WithFields(f).Debugf("adding filter signature_signed: %t", aws.BoolValue(params.Signed))
- searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(aws.BoolValue(params.Signed)))
+ log.WithFields(f).Debugf("adding signature_signed: %t filter", aws.BoolValue(params.Signed))
+ searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(params.Signed))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
// If no query option was provided for approved and signed and our configuration default is to only show active signatures then we add the required query filters
if params.Approved == nil && params.Signed == nil && config.GetConfig().SignatureQueryDefault == utils.SignatureQueryDefaultActive {
- log.WithFields(f).Debug("adding filter signature_approved: true and signature_signed: true")
- searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(true)).
- And(expression.Name("signature_signed").Equal(expression.Value(true)))
- filter = addAndCondition(filter, searchTermExpression, &filterAdded)
+ log.WithFields(f).Debug("adding signature_approved: true and signature_signed: true filters")
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
}
- log.WithFields(f).Debugf("filterAdded: %t", filterAdded)
if filterAdded {
- log.WithFields(f).Debugf("filter: %+v", filter)
builder = builder.WithFilter(filter)
}
@@ -883,6 +879,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
params.ProjectID, errQuery)
return nil, errQuery
}
+ log.WithFields(f).Debugf("returned %d results", len(results.Items))
// Convert the list of DB models to a list of response models
signatureList, modelErr := repo.buildProjectSignatureModels(ctx, results, params.ProjectID, LoadACLDetails)
@@ -895,7 +892,6 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// Add to the signatures response model to the list
sigs = append(sigs, signatureList...)
- //log.WithFields(f).Debugf("LastEvaluatedKey: %+v", results.LastEvaluatedKey)
if results.LastEvaluatedKey["signature_id"] != nil {
lastEvaluatedKey = *results.LastEvaluatedKey["signature_id"].S
queryInput.ExclusiveStartKey = results.LastEvaluatedKey
@@ -935,6 +931,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
log.WithFields(f).Debugf("lastEvaluatedKey encoded is: %s", encodedString)
}
+ log.WithFields(f).Debugf("returning %d signatures for CLA Group ID: %s", len(sigs), params.ProjectID)
return &models.Signatures{
ProjectID: params.ProjectID,
ResultCount: int64(len(sigs)),
@@ -983,18 +980,20 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
if params.ClaType != nil {
filterAdded = true
if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
-
+ log.WithFields(f).Debugf("adding ICLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeExists())
+ log.WithFields(f).Debugf("adding ECLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeExists(), &filterAdded)
} else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
- filter = expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+ log.WithFields(f).Debugf("adding CCLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCCLA, utils.SignatureReferenceTypeCompany)
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
}
} else {
if params.SearchField != nil {
@@ -1226,8 +1225,10 @@ func (repo repository) GetProjectCompanySignatures(ctx context.Context, companyI
//condition := expression.Key("signature_project_id").Equal(expression.Value(projectID))
condition := expression.Key("signature_project_id").Equal(expression.Value(projectID)).
And(expression.Key("signature_reference_id").Equal(expression.Value(companyID)))
- filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
- And(expression.Name("signature_reference_type").Equal(expression.Value("company")))
+ var filter expression.ConditionBuilder
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
if approved != nil {
filterAdded = true
@@ -1505,18 +1506,22 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
// This is the keys we want to match
condition := expression.Key("signature_user_ccla_company_id").Equal(expression.Value(params.CompanyID)).And(
expression.Key("signature_project_id").Equal(expression.Value(params.ProjectID)))
+
+ var filterAdded bool
+ var filter expression.ConditionBuilder
+
// Check for approved signatures
- filter := expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true))))
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
if criteria != nil && criteria.GitHubUsername != "" {
log.WithFields(f).Debugf("adding Githubusername criteria filter for :%s ", criteria.GitHubUsername)
- filter = filter.And(expression.Name("user_github_username").Equal(expression.Value(criteria.GitHubUsername)))
+ filter = addAndCondition(filter, expression.Name("user_github_username").Equal(expression.Value(criteria.GitHubUsername)), &filterAdded)
}
if criteria != nil && criteria.UserEmail != "" {
log.WithFields(f).Debugf("adding useremail criteria filter for : %s ", criteria.UserEmail)
- filter = filter.And(expression.Name("user_email").Equal(expression.Value(criteria.UserEmail)))
+ filter = addAndCondition(filter, expression.Name("user_email").Equal(expression.Value(criteria.UserEmail)), &filterAdded)
}
// Use the nice builder to create the expression
@@ -1630,12 +1635,13 @@ func (repo repository) GetCompanySignatures(ctx context.Context, params signatur
// This is the keys we want to match
condition := expression.Key("signature_reference_id").Equal(expression.Value(params.CompanyID))
- // Check for approved signatures
- filter := expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true))))
+ var filterAdded bool
+ var filter expression.ConditionBuilder
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
if params.SignatureType != nil {
- filter = filter.And(expression.Name("signature_type").Equal(expression.Value(*params.SignatureType)))
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(*params.SignatureType)), &filterAdded)
}
// Use the nice builder to create the expression
@@ -1754,10 +1760,16 @@ func (repo repository) GetCompanyIDsWithSignedCorporateSignatures(ctx context.Co
// These are the keys we want to match
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
- filter := expression.Name("signature_type").Equal(expression.Value("ccla")).
- And(expression.Name("signature_reference_type").Equal(expression.Value("company"))).
- And(expression.Name("signature_signed").Equal(expression.Value(aws.Bool(true)))).
- And(expression.Name("signature_approved").Equal(expression.Value(aws.Bool(true))))
+
+ var filterAdded bool
+ var filter expression.ConditionBuilder
+
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
+ // Check for approved signatures
+ filter = addAndCondition(filter, expression.Name("signature_approved").Equal(expression.Value(true)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_signed").Equal(expression.Value(true)), &filterAdded)
// Batch size
limit := int64(100)
@@ -2958,11 +2970,13 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
"searchTerm": utils.StringValue(searchTerm),
}
- var filterAdded bool
condition := expression.Key("signature_project_id").Equal(expression.Value(claGroupID))
- filter := expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)).
- And(expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser))).
- And(expression.Name("signature_user_ccla_company_id").AttributeNotExists())
+
+ var filter expression.ConditionBuilder
+ var filterAdded bool
+ filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
if approved != nil {
f["approved"] = utils.BoolValue(approved)
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index f333dc930..b70f919f7 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -458,15 +458,29 @@ func (s *service) loadMetrics(ctx context.Context, f logrus.Fields, responseMode
for idx, responseEntry := range responseModel.List {
go func(index int, responseEntry *models.ClaGroupSummary) {
- log.WithFields(f).Debugf("fetching project signature metrics for CLA Group (%d): %s - %s", index, responseEntry.ClaGroupID, responseEntry.ClaGroupName)
- iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeICLA), SignatureType: aws.String(utils.SignatureTypeCLA)})
+ log.WithFields(f).Debugf("loading project signature metrics for CLA Group (idx:%d): %s - %s", index, responseEntry.ClaGroupID, responseEntry.ClaGroupName)
+ iclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx,
+ signatures.GetProjectSignaturesParams{
+ Approved: utils.Bool(true),
+ ClaType: aws.String(utils.ClaTypeICLA),
+ ProjectID: responseEntry.ClaGroupID,
+ Signed: utils.Bool(true),
+ },
+ )
if err != nil {
- log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ log.WithFields(f).WithError(err).Warnf("error while getting ICLA Signature using CLA Group ID %s Error: %v", responseEntry.ClaGroupID, err)
}
- cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx, signatures.GetProjectSignaturesParams{ProjectID: responseEntry.ClaGroupID, ClaType: aws.String(utils.ClaTypeCCLA), SignatureType: aws.String(utils.SignatureTypeCCLA)})
+ cclaSignatureDetails, err := s.signatureService.GetProjectSignatures(ctx,
+ signatures.GetProjectSignaturesParams{
+ Approved: utils.Bool(true),
+ ProjectID: responseEntry.ClaGroupID,
+ ClaType: aws.String(utils.ClaTypeCCLA),
+ Signed: utils.Bool(true),
+ },
+ )
if err != nil {
- log.WithFields(f).Warnf("error while getting ICLA Signature using cla group ID %s Error: %v", responseEntry.ClaGroupID, err)
+ log.WithFields(f).WithError(err).Warnf("error while getting ICLA Signature using CLA Group ID %s Error: %v", responseEntry.ClaGroupID, err)
}
metricsResultChannel <- &MetricsResult{
@@ -482,6 +496,7 @@ func (s *service) loadMetrics(ctx context.Context, f logrus.Fields, responseMode
for range responseModel.List {
select {
case response := <-metricsResultChannel:
+ log.WithFields(f).Debugf("Signature Metrics: CCLA Signatures: %d, ICLA Signatures: %d", response.cclaSignatureCount, response.iclaSignatureCount)
responseModel.List[response.index].TotalSignatures = response.cclaSignatureCount + response.iclaSignatureCount
case <-ctx.Done():
log.WithError(ctx.Err()).Warnf("waiting for metrics failed with timeout")
From 7b933c24ba128cd27ae494b261fa6fd65ac12c9e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 16 Jun 2021 11:22:35 -0700
Subject: [PATCH 0313/1276] Updated Documentation Links (#2994)
---
docs/README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/README.md b/docs/README.md
index 0f59c3676..93a0c4f17 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,5 +1,5 @@
# Documentation
-User documentation for the EasyCLA application can be found
-[under the Linux Foundation documentation pages](https://docs.linuxfoundation.org/lfx/easycla).
-
+User documentation for the EasyCLA application can be found:
+- [EasyCLA v1 documentation](https://docs.linuxfoundation.org/lfx/easycla)
+- [EasyCLA v2 documentation](https://docs.linuxfoundation.org/lfx/v/v2/easycla)
From 91a71ab1fa6a8de25ba4ceffd7ef4a431f41a54c Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 17 Jun 2021 14:01:32 -0700
Subject: [PATCH 0314/1276] User Service Role Checks Cleanup/Debug (#2995)
---
cla-backend/cla/user_service.py | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/cla-backend/cla/user_service.py b/cla-backend/cla/user_service.py
index da8c42a34..6cbc0397f 100644
--- a/cla-backend/cla/user_service.py
+++ b/cla-backend/cla/user_service.py
@@ -6,7 +6,6 @@
from typing import List
from urllib.parse import quote
-
import requests
import cla
@@ -33,7 +32,7 @@ def get_user_by_sf_id(self, sf_user_id: str):
Queries the platform user service for the specified user id. The
result will return all the details for the user as a dictionary.
"""
- fn = 'user_service.get_user_by_sf_id'
+ fn = 'cla.user_service.get_user_by_sf_id'
headers = {
'Authorization': f'bearer {self.get_access_token()}',
@@ -58,7 +57,7 @@ def _get_users_by_key_value(self, key: str, value: str) -> List[dict]:
The result will return summary information for the users as a
dictionary.
"""
- fn = 'user_service._get_users_by_key_value'
+ fn = 'cla.user_service._get_users_by_key_value'
headers = {
'Authorization': f'bearer {self.get_access_token()}',
@@ -102,7 +101,7 @@ def get_users_by_lastname(self, last_name: str) -> List[dict]:
def get_users_by_email(self, email: str) -> List[dict]:
return self._get_users_by_key_value("email", email)
-
+
def has_role(self, username: str, role: str, organization_id: str, cla_group_id: str) -> bool:
"""
Function that checks whether lf user has a role
@@ -117,7 +116,7 @@ def has_role(self, username: str, role: str, organization_id: str, cla_group_id:
:rtype: bool
"""
scopes = {}
- function = 'has_role'
+ function = 'cla.user_service.has_role'
scopes = self._list_org_user_scopes(organization_id, role)
if scopes:
log.info(f'{function} - Found scopes : {scopes} for organization: {organization_id}')
@@ -144,7 +143,7 @@ def has_role(self, username: str, role: str, organization_id: str, cla_group_id:
log.info(f'{function} - {username} does not have role scope')
return False
-
+
def _has_project_org_scope(self, project_sfid: str, organization_id: str, username: str, scopes: dict) -> bool:
"""
Helper function that checks whether there exists project_org_scope for given role
@@ -158,23 +157,26 @@ def _has_project_org_scope(self, project_sfid: str, organization_id: str, userna
:type scopes: dict
:rtype: bool
"""
- function = '_has_project_org_scope_role'
+ function = 'cla.user_service._has_project_org_scope_role'
try:
user_roles = scopes['userroles']
- log.info(f'{function} - User roles: {user_roles}')
+ log.info(f'{function} - User roles for user: \'{username}\' are: {user_roles}')
except KeyError as err:
- log.warning(f'{function} - error: {err} ')
+ log.info(f'{function} - user: \'{username}\' scope does not have \'userroles\', error: {err} '
+ f'Returning False.')
return False
+
+ # For each user role assigned to the user...
for user_role in user_roles:
+ # If the username matches...
if user_role['Contact']['Username'] == username:
- #Since already filtered by role ...get first item
+ # Since already filtered by role ...get first item
for scope in user_role['RoleScopes'][0]['Scopes']:
log.info(f'{function}- Checking objectID for scope: {project_sfid}|{organization_id}')
if scope['ObjectID'] == f'{project_sfid}|{organization_id}':
return True
return False
-
def _list_org_user_scopes(self, organization_id: str, role: str) -> dict:
"""
Helper function that lists the org_user_scopes for a given organization related to given role
@@ -182,12 +184,10 @@ def _list_org_user_scopes(self, organization_id: str, role: str) -> dict:
:type organization_id: string
:param role: role to filter the user org scopes
:type role: string
- :param cla_group_id: cla_group_id thats mapped to salesforce projects
- :type cla_group_id: string
:return: json dict representing org user role scopes
:rtype: dict
"""
- function = '_list_org_user_scopes'
+ function = 'cla.user_service._list_org_user_scopes'
headers = {
'Authorization': f'bearer {self.get_access_token()}',
'accept': 'application/json'
@@ -199,11 +199,12 @@ def _list_org_user_scopes(self, organization_id: str, role: str) -> dict:
r = requests.get(url, headers=headers, params=params)
return r.json()
except requests.exceptions.HTTPError as err:
- log.warning('%s - Could not get user org scopes for organization: %s with role: %s , error: %s ', function, organization_id, role, err)
+ log.warning('%s - Could not get user org scopes for organization: %s with role: %s , error: %s ', function,
+ organization_id, role, err)
return None
def get_access_token(self):
- fn = 'user_service.get_access_token'
+ fn = 'cla.user_service.get_access_token'
# Use previously cached value, if not expired
if self.access_token and datetime.datetime.now() < self.access_token_expires:
cla.log.debug(f'{fn} - using cached access token: {self.access_token[0:10]}...')
From 8e41d329175c2d9f21f23e0ef1b2ce4e777a780a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 17 Jun 2021 14:02:26 -0700
Subject: [PATCH 0315/1276] Bump glob-parent from 5.1.1 to 5.1.2 in
/cla-backend (#2985)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index d672ee8df..9e8e865fb 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -2927,9 +2927,9 @@ glob-all@^3.1.0:
yargs "^15.3.1"
glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
From 96ef2e879fc9a8a3fec2cefc4540f56b320cf23f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 17 Jun 2021 14:19:20 -0700
Subject: [PATCH 0316/1276] Bump glob-parent from 5.1.1 to 5.1.2 (#2989)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 8e21dbb68..a72f22243 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2258,9 +2258,9 @@ github-from-package@0.0.0:
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
From e06a952b2179b6f7a5035b1ff880120ce32aa5eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 17 Jun 2021 14:19:38 -0700
Subject: [PATCH 0317/1276] Bump glob-parent from 5.1.1 to 5.1.2 in
/cla-backend-go (#2990)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index 1f3a8d7d4..a94e9c28c 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -2385,9 +2385,9 @@ github-from-package@0.0.0:
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
From ec984eb355e895832ce51579c68d5244c7e7864e Mon Sep 17 00:00:00 2001
From: Leopold Schabel
Date: Sat, 19 Jun 2021 03:06:05 +0200
Subject: [PATCH 0318/1276] Fix GitHub typo in email notification (#2997)
---
cla-backend-go/signatures/service.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index bfed7e6b6..3408a38a1 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -523,9 +523,9 @@ func buildApprovalListSummary(approvalListChanges *models.ApprovalList) string {
approvalListSummary += appendList(approvalListChanges.RemoveEmailApprovalList, "Removed Email:")
approvalListSummary += appendList(approvalListChanges.AddDomainApprovalList, "Added Domain:")
approvalListSummary += appendList(approvalListChanges.RemoveDomainApprovalList, "Removed Domain:")
- approvalListSummary += appendList(approvalListChanges.AddGithubUsernameApprovalList, "Added GithHub User:")
+ approvalListSummary += appendList(approvalListChanges.AddGithubUsernameApprovalList, "Added GitHub User:")
approvalListSummary += appendList(approvalListChanges.RemoveGithubUsernameApprovalList, "Removed GitHub User:")
- approvalListSummary += appendList(approvalListChanges.AddGithubOrgApprovalList, "Added GithHub Organization:")
+ approvalListSummary += appendList(approvalListChanges.AddGithubOrgApprovalList, "Added GitHub Organization:")
approvalListSummary += appendList(approvalListChanges.RemoveGithubOrgApprovalList, "Removed GitHub Organization:")
approvalListSummary += ""
return approvalListSummary
From 2835c0ad0e367af0c2183de7e04fceed60345092 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Sat, 19 Jun 2021 07:00:13 +0300
Subject: [PATCH 0319/1276] Feature/Enroll & Unenroll Projects (#2992)
Co-authored-by: Harold Wanyama
---
cla-backend-go/v2/cla_groups/helpers.go | 169 +++++++++++++++-----
cla-backend-go/v2/cla_groups/models.go | 8 +
cla-backend-go/v2/project-service/client.go | 33 ++++
3 files changed, 169 insertions(+), 41 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index fd639b5f4..772568f01 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -21,6 +21,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
psproject "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/client/project"
+ v2ProjectServiceModels "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/models"
"github.com/sirupsen/logrus"
)
@@ -221,9 +222,22 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
return err
}
+ foundationProjectSummary, err := psc.GetSummary(foundationSFID)
+ if err != nil {
+ log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
+ return err
+ }
+
+ // build Tree that tracks parent and child projects
+ projectTree, err := buildProjectTree(foundationProjectSummary)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to process project summary :%+v ", foundationProjectSummary)
+ return err
+ }
+
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
+ isLFParent, err := psc.IsTheLinuxFoundation(projectTree.Parent.ID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return err
@@ -235,7 +249,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
return err
}
- if foundationProjectDetails.Parent != "" && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
+ if projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
foundationProjectDetails.Parent, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
@@ -245,26 +259,12 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
}
// Check to see if all the provided enrolled projects are part of this foundation
- foundationProjectIDList := utils.NewStringSet()
- for _, pr := range foundationProjectDetails.Projects {
- foundationProjectIDList.Add(pr.ID)
- }
- invalidProjectSFIDs := utils.NewStringSet()
- for _, projectSFID := range projectSFIDList {
- // Ok to have foundation ID in the project list - this means it's a Foundation Level CLA Group
- if foundationSFID == projectSFID {
- continue
- }
- // If the input/provided project ID is not in the SF project list...
- if !foundationProjectIDList.Include(projectSFID) {
- invalidProjectSFIDs.Add(projectSFID)
- }
- }
+ exists := projectsExist(projectTree, projectSFIDList)
- if invalidProjectSFIDs.Length() != 0 {
- log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", invalidProjectSFIDs.List())
- return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the SF foundation", invalidProjectSFIDs.List())
+ if !exists {
+ log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", projectTree)
+ return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the SF foundation", projectTree)
}
// check if projects are not already enabled
@@ -276,7 +276,8 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
for _, pr := range enabledProjects {
enabledProjectList.Add(pr.ProjectSFID)
}
- invalidProjectSFIDs = utils.NewStringSet()
+
+ invalidProjectSFIDs := utils.NewStringSet()
for _, projectSFID := range projectSFIDList {
// Ok to have foundation ID in the project list - no need to check if it's already in the sub-project enabled list
if foundationSFID == projectSFID {
@@ -323,6 +324,19 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
+ foundationProjectSummary, err := psc.GetSummary(foundationSFID)
+ if err != nil {
+ log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
+ return err
+ }
+
+ // build Tree that tracks parent and child projects
+ projectTree, err := buildProjectTree(foundationProjectSummary)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to process project summary :%+v ", foundationProjectSummary)
+ return err
+ }
+
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
@@ -353,26 +367,11 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
} */
// Check to see if all the provided enrolled projects are part of this foundation
- foundationProjectIDList := utils.NewStringSet()
- for _, pr := range foundationProjectDetails.Projects {
- foundationProjectIDList.Add(pr.ID)
- }
- invalidProjectSFIDs := utils.NewStringSet()
- for _, projectSFID := range projectSFIDList {
- // Ok to have foundation ID in the project list - this means it's a Foundation Level CLA Group
- if foundationSFID == projectSFID {
- continue
- }
-
- // If the input/provided project ID is not in the SF project list...
- if !foundationProjectIDList.Include(projectSFID) {
- invalidProjectSFIDs.Add(projectSFID)
- }
- }
+ exists := projectsExist(projectTree, projectSFIDList)
- if invalidProjectSFIDs.Length() != 0 {
- log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", invalidProjectSFIDs.List())
- return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more of the provided projects are not under the SF foundation", invalidProjectSFIDs.List())
+ if !exists {
+ log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", projectTree)
+ return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the SF foundation", projectTree)
}
// check if projects are already enrolled/enabled
@@ -384,7 +383,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
for _, pr := range enabledProjects {
enabledProjectList.Add(pr.ProjectSFID)
}
- invalidProjectSFIDs = utils.NewStringSet()
+ invalidProjectSFIDs := utils.NewStringSet()
for _, projectSFID := range projectSFIDList {
// Ok to have foundation ID in the project list - no need to check if it's already in the sub-project enabled list
if foundationSFID == projectSFID {
@@ -728,3 +727,91 @@ func getUniqueCLAGroupIDs(projectCLAGroupMappings []*projects_cla_groups.Project
return keys
}
+
+// buildTree helper function that builds tree based on nessted Projects
+func buildProjectTree(projectSummaryList []*v2ProjectServiceModels.ProjectSummary) (*ProjectNode, error) {
+ f := logrus.Fields{
+ "functionName": "v2.cla_groups.helpers.buildTree",
+ }
+
+ log.WithFields(f).Debugf("Building project summary tree for : %+v ...", projectSummaryList)
+ var root ProjectNode
+
+ if len(projectSummaryList) == 0 {
+ msg := "project summary list is empty"
+ log.WithFields(f).Debugf(msg)
+ return nil, errors.New(msg)
+ }
+ projectSummary := projectSummaryList[0]
+ // Get ParentProject ID
+ parentProjectID, err := v2ProjectService.GetClient().GetParentProject(projectSummary.ID)
+ if err != nil {
+ log.WithFields(f).Debugf("unable to get parent project for : %s ", projectSummary.ID)
+ }
+
+ // Use Parent as root for projects to help in validation checks for enrolling / unenrolling
+ root = ProjectNode{
+ Parent: nil,
+ ID: parentProjectID,
+ Children: []*ProjectNode{{
+ ID: projectSummary.ID,
+ Name: projectSummary.Name,
+ }},
+ }
+
+ // Aggregate projects
+ for _, summary := range projectSummaryList {
+ for _, child := range root.Children {
+ child.addChildren(summary)
+ }
+ }
+
+ return &root, nil
+}
+
+func (n *ProjectNode) addChildren(summary *v2ProjectServiceModels.ProjectSummary) {
+ f := logrus.Fields{
+ "functionName": "v2.cla_groups.helpers.addChidlren",
+ }
+
+ log.WithFields(f).Debugf("Agrregating children for %+v ...", summary)
+ for _, subProject := range summary.Projects {
+ child := &ProjectNode{
+ Parent: n,
+ ID: subProject.ID,
+ Name: subProject.Name,
+ }
+ n.Children = append(n.Children, child)
+ log.WithFields(f).Debugf("added child : %+v ", child)
+ }
+}
+
+// findByID searches for given projectSFID recursively using DFS algorithm
+func findByID(node *ProjectNode, projectSFID string) *ProjectNode {
+ f := logrus.Fields{
+ "functionName": "v2.cla_groups.helpers.fundByID",
+ }
+ log.WithFields(f).Debugf("searching for :%s ...", projectSFID)
+ if node.ID == projectSFID {
+ return node
+ }
+
+ if len(node.Children) > 0 {
+ for _, child := range node.Children {
+ findByID(child, projectSFID)
+ }
+ }
+
+ return nil
+}
+
+// projectsExist searches for given list of projects in foundation items
+func projectsExist(node *ProjectNode, projectSFIDs []string) bool {
+ for _, projectSFID := range projectSFIDs {
+ found := findByID(node, projectSFID)
+ if found == nil {
+ return false
+ }
+ }
+ return true
+}
diff --git a/cla-backend-go/v2/cla_groups/models.go b/cla-backend-go/v2/cla_groups/models.go
index 1473ea896..81834db28 100644
--- a/cla-backend-go/v2/cla_groups/models.go
+++ b/cla-backend-go/v2/cla_groups/models.go
@@ -38,3 +38,11 @@ type UnassociateCLAGroupWithProjectsModel struct {
FoundationSFID string
ProjectSFIDList []string
}
+
+// ProjectNode representing nested projects
+type ProjectNode struct {
+ Parent *ProjectNode
+ ID string
+ Name string
+ Children []*ProjectNode
+}
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index a9072c2d9..e90795e7f 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -324,3 +324,36 @@ func (pmm *Client) DisableCLA(projectSFID string) error {
}
return pmm.updateEnabledServices(projectSFID, newEnabledServices, clientAuth)
}
+
+//GetSummary gets projects tree heirachy and project details
+func (pmm *Client) GetSummary(projectSFID string) ([]*models.ProjectSummary, error) {
+ f := logrus.Fields{
+ "functionName": "v2.project-service.client.Summary",
+ "projectID": projectSFID,
+ }
+
+ tok, err := token.GetToken()
+ if err != nil {
+ return nil, err
+ }
+
+ clientAuth := runtimeClient.BearerToken(tok)
+
+ filter := fmt.Sprintf("id eq %s", projectSFID)
+ log.WithFields(f).Debugf("Getting project summary for :%s ", projectSFID)
+ view := "pcc"
+
+ params := &project.GetSummaryParams{
+ DollarFilter: &filter,
+ View: &view,
+ }
+
+ result, err := pmm.cl.Project.GetSummary(params, clientAuth)
+
+ if err != nil {
+ log.WithFields(f).Debugf("unable to query project summary for : %s , error: %+v ", projectSFID, err)
+ return nil, err
+ }
+
+ return result.Payload.Data, nil
+}
From 49c1129be338a2c92a107fe4396cf89e1bb4161d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 21 Jun 2021 15:26:19 -0700
Subject: [PATCH 0320/1276] [PCC-1165] Added Context to Project Summary API
Call (#3001)
---
cla-backend-go/tests/project_service_test.go | 34 ++++++++++
cla-backend-go/v2/cla_groups/helpers.go | 10 +--
cla-backend-go/v2/project-service/client.go | 69 +++++++++++++-------
3 files changed, 86 insertions(+), 27 deletions(-)
create mode 100644 cla-backend-go/tests/project_service_test.go
diff --git a/cla-backend-go/tests/project_service_test.go b/cla-backend-go/tests/project_service_test.go
new file mode 100644
index 000000000..3ecd3f3b8
--- /dev/null
+++ b/cla-backend-go/tests/project_service_test.go
@@ -0,0 +1,34 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "os"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+ "github.com/communitybridge/easycla/cla-backend-go/token"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestProjectServiceSummary(t *testing.T) {
+ var awsSession = session.Must(session.NewSession(&aws.Config{}))
+ stage := os.Getenv("STAGE")
+ assert.NotEmpty(t, stage)
+ configFile, err := config.LoadConfig("", awsSession, stage)
+ assert.Nil(t, err, "load config error")
+ token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
+ project_service.InitClient(configFile.PlatformAPIGatewayURL)
+
+ client := project_service.GetClient()
+ projectSummaryModel, err := client.GetSummary(utils.NewContext(), "a096s000000VluyAAC")
+ assert.Nil(t, err, "Error is nil")
+ assert.NotNil(t, projectSummaryModel, "Project Summary Response not nil")
+}
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 772568f01..7496813b1 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -222,7 +222,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
return err
}
- foundationProjectSummary, err := psc.GetSummary(foundationSFID)
+ foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
if err != nil {
log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
return err
@@ -324,7 +324,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
- foundationProjectSummary, err := psc.GetSummary(foundationSFID)
+ foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
if err != nil {
log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
return err
@@ -516,7 +516,7 @@ func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, cla
go func(psClient *v2ProjectService.Client, claGroupID, projectSFID string) {
defer wg.Done()
log.WithFields(f).Debugf("enabling project CLA service for project: %s...", projectSFID)
- enableProjectErr := psClient.EnableCLA(projectSFID)
+ enableProjectErr := psClient.EnableCLA(ctx, projectSFID)
if enableProjectErr != nil {
log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", projectSFID, enableProjectErr)
errorList = append(errorList, enableProjectErr)
@@ -543,7 +543,7 @@ func (s *service) EnableCLAService(ctx context.Context, authUser *auth.User, cla
log.WithFields(f).Debugf("skipping setting the enabled services on The Linux Foundation parent project(s) for parent project SFID: %s", parentProjectSFID)
} else {
log.WithFields(f).Debugf("enabling parent project CLA service for project SFID: %s...", parentProjectSFID)
- enableProjectErr := psClient.EnableCLA(parentProjectSFID)
+ enableProjectErr := psClient.EnableCLA(ctx, parentProjectSFID)
if enableProjectErr != nil {
log.WithFields(f).WithError(enableProjectErr).Warnf("unable to enable CLA service for project: %s, error: %+v", parentProjectSFID, enableProjectErr)
errorList = append(errorList, enableProjectErr)
@@ -598,7 +598,7 @@ func (s *service) DisableCLAService(ctx context.Context, authUser *auth.User, cl
// Execute as a go routine
go func(psClient *v2ProjectService.Client, claGroupID, projectSFID string) {
defer wg.Done()
- disableProjectErr := psClient.DisableCLA(projectSFID)
+ disableProjectErr := psClient.DisableCLA(ctx, projectSFID)
if disableProjectErr != nil {
log.WithFields(f).WithError(disableProjectErr).
Warnf("unable to disable CLA service for project: %s, error: %+v", projectSFID, disableProjectErr)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index e90795e7f..28bb7f54f 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -4,6 +4,7 @@
package project_service
import (
+ "context"
"errors"
"fmt"
"strings"
@@ -106,11 +107,12 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
}
// GetProjectByName returns project details for the associated project name
-func (pmm *Client) GetProjectByName(projectName string) (*models.ProjectListSearch, error) {
+func (pmm *Client) GetProjectByName(ctx context.Context, projectName string) (*models.ProjectListSearch, error) {
f := logrus.Fields{
- "functionName": "v2.project-service.client.GetProjectByName",
- "projectName": projectName,
- "apiGWHost": apiGWHost,
+ "functionName": "v2.project-service.client.GetProjectByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectName": projectName,
+ "apiGWHost": apiGWHost,
}
tok, err := token.GetToken()
if err != nil {
@@ -120,7 +122,8 @@ func (pmm *Client) GetProjectByName(projectName string) (*models.ProjectListSear
clientAuth := runtimeClient.BearerToken(tok)
result, err := pmm.cl.Project.SearchProjects(&project.SearchProjectsParams{
- Name: []string{projectName},
+ Name: []string{projectName},
+ Context: ctx,
}, clientAuth)
if err != nil {
log.WithFields(f).WithError(err).Warning("problem searching projects by name")
@@ -227,11 +230,12 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
}
// EnableCLA enables CLA service in project-service
-func (pmm *Client) EnableCLA(projectSFID string) error {
+func (pmm *Client) EnableCLA(ctx context.Context, projectSFID string) error {
f := logrus.Fields{
- "functionName": "v2.project-service.client.EnableCLA",
- "projectSFID": projectSFID,
- "apiGWHost": apiGWHost,
+ "functionName": "v2.project-service.client.EnableCLA",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
theLF, lookupErr := pmm.IsTheLinuxFoundation(projectSFID)
@@ -267,33 +271,45 @@ func (pmm *Client) EnableCLA(projectSFID string) error {
enabledServices := projectDetails.EnabledServices
enabledServices = append(enabledServices, CLA)
- return pmm.updateEnabledServices(projectSFID, enabledServices, clientAuth)
+ return pmm.updateEnabledServices(ctx, projectSFID, enabledServices, clientAuth)
}
-func (pmm *Client) updateEnabledServices(projectSFID string, enabledServices []string, clientAuth runtime.ClientAuthInfoWriter) error {
+func (pmm *Client) updateEnabledServices(ctx context.Context, projectSFID string, enabledServices []string, clientAuth runtime.ClientAuthInfoWriter) error {
+ f := logrus.Fields{
+ "functionName": "v2.project-service.client.updateEnabledServices",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "enabledServices": enabledServices,
+ "apiGWHost": apiGWHost,
+ }
+
params := project.NewUpdateProjectParams()
params.ProjectID = projectSFID
if len(enabledServices) == 0 {
enabledServices = append(enabledServices, NA)
}
+
params.Body = &models.ProjectInput{
ProjectCommon: models.ProjectCommon{
EnabledServices: enabledServices,
},
}
+
_, err := pmm.cl.Project.UpdateProject(params, clientAuth) //nolint
if err != nil {
- return err
+ log.WithFields(f).WithError(err).Warnf("problem updating project enabled services")
}
+
return err
}
// DisableCLA enables CLA service in project-service
-func (pmm *Client) DisableCLA(projectSFID string) error {
+func (pmm *Client) DisableCLA(ctx context.Context, projectSFID string) error {
f := logrus.Fields{
- "functionName": "v2.project-service.client.DisableCLA",
- "projectSFID": projectSFID,
- "apiGWHost": apiGWHost,
+ "functionName": "v2.project-service.client.DisableCLA",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
}
tok, err := token.GetToken()
@@ -322,14 +338,15 @@ func (pmm *Client) DisableCLA(projectSFID string) error {
// CLA already disabled
return nil
}
- return pmm.updateEnabledServices(projectSFID, newEnabledServices, clientAuth)
+ return pmm.updateEnabledServices(ctx, projectSFID, newEnabledServices, clientAuth)
}
-//GetSummary gets projects tree heirachy and project details
-func (pmm *Client) GetSummary(projectSFID string) ([]*models.ProjectSummary, error) {
+//GetSummary gets projects tree hierarchy and project details
+func (pmm *Client) GetSummary(ctx context.Context, projectSFID string) ([]*models.ProjectSummary, error) {
f := logrus.Fields{
- "functionName": "v2.project-service.client.Summary",
- "projectID": projectSFID,
+ "functionName": "v2.project-service.client.Summary",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectID": projectSFID,
}
tok, err := token.GetToken()
@@ -342,16 +359,24 @@ func (pmm *Client) GetSummary(projectSFID string) ([]*models.ProjectSummary, err
filter := fmt.Sprintf("id eq %s", projectSFID)
log.WithFields(f).Debugf("Getting project summary for :%s ", projectSFID)
view := "pcc"
+ offsetDefault := int64(0)
+ orderByDefault := string("createddate")
+ pageSizeDefault := int64(100)
params := &project.GetSummaryParams{
DollarFilter: &filter,
+ MyProjects: nil,
+ Offset: &offsetDefault,
+ OrderBy: &orderByDefault,
+ PageSize: &pageSizeDefault,
View: &view,
+ Context: ctx, // must set for the GetSummary API call, otherwise we get a Err:context.deadlineExceededError{}
}
result, err := pmm.cl.Project.GetSummary(params, clientAuth)
if err != nil {
- log.WithFields(f).Debugf("unable to query project summary for : %s , error: %+v ", projectSFID, err)
+ log.WithFields(f).WithError(err).Debugf("unable to query project summary for : %s , error: %+v ", projectSFID, err)
return nil, err
}
From 571b317f97497c45e149866b30da20cc8cf4cf7b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 21 Jun 2021 15:37:51 -0700
Subject: [PATCH 0321/1276] [PCC-1165] Added Context to Project Summary API
Call (#3005)
---
cla-backend-go/tests/project_service_test.go | 33 ++++++++++----------
cla-backend-go/v2/project-service/client.go | 3 +-
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/cla-backend-go/tests/project_service_test.go b/cla-backend-go/tests/project_service_test.go
index 3ecd3f3b8..bec878f70 100644
--- a/cla-backend-go/tests/project_service_test.go
+++ b/cla-backend-go/tests/project_service_test.go
@@ -4,31 +4,32 @@
package tests
import (
- "os"
-
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/communitybridge/easycla/cla-backend-go/config"
"github.com/communitybridge/easycla/cla-backend-go/token"
"github.com/communitybridge/easycla/cla-backend-go/utils"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
-
- "testing"
-
"github.com/stretchr/testify/assert"
+ "os"
+ "testing"
)
+var functionalTestEnabled = false
+
func TestProjectServiceSummary(t *testing.T) {
- var awsSession = session.Must(session.NewSession(&aws.Config{}))
- stage := os.Getenv("STAGE")
- assert.NotEmpty(t, stage)
- configFile, err := config.LoadConfig("", awsSession, stage)
- assert.Nil(t, err, "load config error")
- token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
- project_service.InitClient(configFile.PlatformAPIGatewayURL)
+ if functionalTestEnabled { // nolint
+ var awsSession = session.Must(session.NewSession(&aws.Config{}))
+ stage := os.Getenv("STAGE")
+ assert.NotEmpty(t, stage)
+ configFile, err := config.LoadConfig("", awsSession, stage)
+ assert.Nil(t, err, "load config error")
+ token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
+ project_service.InitClient(configFile.PlatformAPIGatewayURL)
- client := project_service.GetClient()
- projectSummaryModel, err := client.GetSummary(utils.NewContext(), "a096s000000VluyAAC")
- assert.Nil(t, err, "Error is nil")
- assert.NotNil(t, projectSummaryModel, "Project Summary Response not nil")
+ client := project_service.GetClient()
+ projectSummaryModel, err := client.GetSummary(utils.NewContext(), "a096s000000VluyAAC")
+ assert.Nil(t, err, "Error is nil")
+ assert.NotNil(t, projectSummaryModel, "Project Summary Response not nil")
+ }
}
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 28bb7f54f..e6d99a31a 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -7,9 +7,8 @@ import (
"context"
"errors"
"fmt"
- "strings"
-
"github.com/sirupsen/logrus"
+ "strings"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
From a8cd3145de96c342578b790db0eb3feb36a7e55d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 21 Jun 2021 16:55:37 -0700
Subject: [PATCH 0322/1276] Resolve Lint Issue (#3006)
---
cla-backend-go/tests/project_service_test.go | 5 +++--
cla-backend-go/v2/project-service/client.go | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/tests/project_service_test.go b/cla-backend-go/tests/project_service_test.go
index bec878f70..1b18f47bc 100644
--- a/cla-backend-go/tests/project_service_test.go
+++ b/cla-backend-go/tests/project_service_test.go
@@ -4,6 +4,9 @@
package tests
import (
+ "os"
+ "testing"
+
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/communitybridge/easycla/cla-backend-go/config"
@@ -11,8 +14,6 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/stretchr/testify/assert"
- "os"
- "testing"
)
var functionalTestEnabled = false
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index e6d99a31a..28bb7f54f 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -7,9 +7,10 @@ import (
"context"
"errors"
"fmt"
- "github.com/sirupsen/logrus"
"strings"
+ "github.com/sirupsen/logrus"
+
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
From 4e75671ebe3d3b01eb767d3e43b720cc113185da Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 22 Jun 2021 11:49:04 -0700
Subject: [PATCH 0323/1276] [PCC-1165] Resolved Helper Invalid Memory Address
(#3007)
---
cla-backend-go/.golangci.yaml | 4 +-
.../cla_manager/cla_manager.go | 2 +-
cla-backend-go/events/mockrepo.go | 2 +-
.../branch_protection/protected_branch_v4.go | 21 +++++-----
.../github_organizations/handlers.go | 2 +-
.../github_organizations/repository.go | 32 +++++++++------
.../github_organizations/service.go | 33 +++++++++------
cla-backend-go/signatures/repository.go | 6 +--
cla-backend-go/template/handlers.go | 2 +-
cla-backend-go/template/repository.go | 33 +++++++--------
cla-backend-go/template/service.go | 31 +++++++-------
cla-backend-go/user/repository.go | 21 +++++-----
cla-backend-go/user/service.go | 4 +-
cla-backend-go/v2/cla_groups/helpers.go | 12 ++++--
cla-backend-go/v2/cla_groups/service.go | 4 +-
cla-backend-go/v2/dynamo_events/autoenable.go | 4 +-
cla-backend-go/v2/dynamo_events/service.go | 4 +-
cla-backend-go/v2/dynamo_events/signatures.go | 7 ++--
cla-backend-go/v2/github_activity/service.go | 6 +--
.../v2/github_organizations/service.go | 6 +--
cla-backend-go/v2/signatures/handlers.go | 4 +-
cla-backend-go/v2/signatures/service.go | 40 +++++++++----------
cla-backend-go/v2/template/handlers.go | 2 +-
23 files changed, 153 insertions(+), 129 deletions(-)
diff --git a/cla-backend-go/.golangci.yaml b/cla-backend-go/.golangci.yaml
index d7b828cf9..29ee03471 100644
--- a/cla-backend-go/.golangci.yaml
+++ b/cla-backend-go/.golangci.yaml
@@ -34,7 +34,7 @@ linters-settings:
check-blank: true
govet:
check-shadowing: true
- golint:
+ revive:
min-confidence: 0
dupl:
threshold: 100
@@ -48,7 +48,7 @@ linters-settings:
linters:
disable-all: true
enable:
- - golint
+ - revive
- govet
- errcheck
- deadcode
diff --git a/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go b/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
index a7edc9b98..fa015d3ab 100644
--- a/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
+++ b/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
@@ -16,7 +16,7 @@ import (
var (
claManagerToken string
claProspectiveManagerToken string
- claManagerCreateRequestID string = "no-set"
+ claManagerCreateRequestID = "no-set"
)
const (
diff --git a/cla-backend-go/events/mockrepo.go b/cla-backend-go/events/mockrepo.go
index 61c647466..82f9025a8 100644
--- a/cla-backend-go/events/mockrepo.go
+++ b/cla-backend-go/events/mockrepo.go
@@ -88,7 +88,7 @@ func (repo *mockRepository) LogEventWithContext(ctx context.Context, args *LogEv
var events []*models.Event
// NewMockRepository creates a new instance of the mock event repository
-func NewMockRepository() *mockRepository {
+func NewMockRepository() *mockRepository { // nolint
return &mockRepository{}
}
diff --git a/cla-backend-go/github/branch_protection/protected_branch_v4.go b/cla-backend-go/github/branch_protection/protected_branch_v4.go
index 20ce3ca71..935354870 100644
--- a/cla-backend-go/github/branch_protection/protected_branch_v4.go
+++ b/cla-backend-go/github/branch_protection/protected_branch_v4.go
@@ -76,22 +76,24 @@ type UpdateRepoBranchProtectionMutation struct {
} `graphql:"updateBranchProtectionRule(input: $input)"`
}
-type branchProtectionRepositoryV4 struct {
+// BranchProtectionRepositoryV4 wraps a v4 github client
+type BranchProtectionRepositoryV4 struct {
client *githubv4.Client
}
-// NewBranchProtectionRepositoryV4 creates a new branchProtectionRepositoryV4
-func NewBranchProtectionRepositoryV4(installationID int64) (*branchProtectionRepositoryV4, error) {
+// NewBranchProtectionRepositoryV4 creates a new BranchProtectionRepositoryV4
+func NewBranchProtectionRepositoryV4(installationID int64) (*BranchProtectionRepositoryV4, error) {
client, clientErr := github.NewGithubV4AppClient(installationID)
if clientErr != nil {
return nil, clientErr
}
- return &branchProtectionRepositoryV4{
+ return &BranchProtectionRepositoryV4{
client: client,
}, nil
}
-func (r *branchProtectionRepositoryV4) GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error) {
+// GetRepositoryBranchProtections returns the repository branch protections for the specified repository
+func (r *BranchProtectionRepositoryV4) GetRepositoryBranchProtections(ctx context.Context, repositoryOwner, repositoryName string) (*RepoBranchProtectionQueryResult, error) {
var queryResult RepoBranchProtectionQueryResult
variables := map[string]interface{}{
@@ -107,7 +109,8 @@ func (r *branchProtectionRepositoryV4) GetRepositoryBranchProtections(ctx contex
return &queryResult, nil
}
-func (r *branchProtectionRepositoryV4) CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
+// CreateBranchProtection creates the repository branch protections for the specified repository
+func (r *BranchProtectionRepositoryV4) CreateBranchProtection(ctx context.Context, input *githubv4.CreateBranchProtectionRuleInput) (*CreateRepoBranchProtectionMutation, error) {
var createMutationResult CreateRepoBranchProtectionMutation
err := r.client.Mutate(ctx, &createMutationResult, *input, nil)
if err != nil {
@@ -116,7 +119,8 @@ func (r *branchProtectionRepositoryV4) CreateBranchProtection(ctx context.Contex
return &createMutationResult, nil
}
-func (r *branchProtectionRepositoryV4) UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
+// UpdateBranchProtection updates the repository branch protections for the specified repository
+func (r *BranchProtectionRepositoryV4) UpdateBranchProtection(ctx context.Context, input *githubv4.UpdateBranchProtectionRuleInput) (*UpdateRepoBranchProtectionMutation, error) {
var updateMutationResult UpdateRepoBranchProtectionMutation
err := r.client.Mutate(ctx, &updateMutationResult, *input, nil)
if err != nil {
@@ -126,7 +130,7 @@ func (r *branchProtectionRepositoryV4) UpdateBranchProtection(ctx context.Contex
}
// GetRepositoryIDFromName when provided the organization and repository name, returns the repository ID
-func (r *branchProtectionRepositoryV4) GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error) {
+func (r *BranchProtectionRepositoryV4) GetRepositoryIDFromName(ctx context.Context, repositoryOwner, repositoryName string) (string, error) {
// Define the graphql query
//"query": "query{repository(name: \"test1\", owner: \"deal-test-org\") {id}}"
@@ -155,6 +159,5 @@ func (r *branchProtectionRepositoryV4) GetRepositoryIDFromName(ctx context.Conte
// EnableBranchProtectionForPattern enables branch protection for the given branch protection input
func EnableBranchProtectionForPattern(ctx context.Context, repositoryOwner, repositoryName string, input *BranchProtectionRule) error {
-
return nil
}
diff --git a/cla-backend-go/github_organizations/handlers.go b/cla-backend-go/github_organizations/handlers.go
index c9f1b690c..85495681c 100644
--- a/cla-backend-go/github_organizations/handlers.go
+++ b/cla-backend-go/github_organizations/handlers.go
@@ -22,7 +22,7 @@ import (
)
// Configure setups handlers on api with service
-func Configure(api *operations.ClaAPI, service Service, eventService events.Service) {
+func Configure(api *operations.ClaAPI, service ServiceInterface, eventService events.Service) {
api.GithubOrganizationsGetProjectGithubOrganizationsHandler = github_organizations.GetProjectGithubOrganizationsHandlerFunc(
func(params github_organizations.GetProjectGithubOrganizationsParams, claUser *user.CLAUser) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 9d12823ab..5f3ab77b1 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -35,8 +35,8 @@ var (
ErrOrganizationDoesNotExist = errors.New("github organization does not exist in cla")
)
-// Repository interface defines the functions for the github organizations data model
-type Repository interface {
+// RepositoryInterface interface defines the functions for the github organizations data model
+type RepositoryInterface interface {
AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
@@ -47,15 +47,16 @@ type Repository interface {
DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error
}
-type repository struct {
+// Repository object/struct
+type Repository struct {
stage string
dynamoDBClient *dynamodb.DynamoDB
githubOrgTableName string
}
// NewRepository creates a new instance of the githubOrganizations repository
-func NewRepository(awsSession *session.Session, stage string) repository {
- return repository{
+func NewRepository(awsSession *session.Session, stage string) Repository {
+ return Repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
githubOrgTableName: fmt.Sprintf("cla-%s-github-orgs", stage),
@@ -63,7 +64,7 @@ func NewRepository(awsSession *session.Session, stage string) repository {
}
// AddGithubOrganization add github organization logic
-func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -189,7 +190,7 @@ func (repo repository) AddGithubOrganization(ctx context.Context, parentProjectS
}
// GetGithubOrganizations get github organizations based on the project SFID
-func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+func (repo Repository) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -244,7 +245,8 @@ func (repo repository) GetGithubOrganizations(ctx context.Context, projectSFID s
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-func (repo repository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
+// GetGithubOrganizationsByParent returns a list of github organizations by parent project SFID
+func (repo Repository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGithubOrganizationsByParent",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -295,7 +297,8 @@ func (repo repository) GetGithubOrganizationsByParent(ctx context.Context, paren
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-func (repo repository) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
+// GetGithubOrganizationByName get github organization by name
+func (repo Repository) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -343,7 +346,8 @@ func (repo repository) GetGithubOrganizationByName(ctx context.Context, githubOr
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-func (repo repository) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
+// GetGithubOrganization by organization name
+func (repo Repository) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -377,7 +381,7 @@ func (repo repository) GetGithubOrganization(ctx context.Context, githubOrganiza
}
// UpdateGithubOrganization updates the specified GitHub organization based on the update model provided
-func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
+func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.UpdateGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -453,7 +457,8 @@ func (repo repository) UpdateGithubOrganization(ctx context.Context, projectSFID
return nil
}
-func (repo repository) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
+// DeleteGithubOrganization deletes the github organization by project SFID
+func (repo Repository) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -515,7 +520,8 @@ func (repo repository) DeleteGithubOrganization(ctx context.Context, projectSFID
return nil
}
-func (repo repository) DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error {
+// DeleteGithubOrganizationByParent deletes the github organization by parent SFID
+func (repo Repository) DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index f1556b6fb..a4224055b 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -19,8 +19,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/repositories"
)
-// Service contains functions of GithubOrganizations service
-type Service interface {
+// ServiceInterface contains functions of GithubOrganizations service
+type ServiceInterface interface {
AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
@@ -30,22 +30,24 @@ type Service interface {
RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization
}
-type service struct {
- repo Repository
+// Service object/struct
+type Service struct {
+ repo RepositoryInterface
ghRepository repositories.Repository
claRepository projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo Repository, ghRepository repositories.Repository, claRepository projects_cla_groups.Repository) Service {
- return service{
+func NewService(repo RepositoryInterface, ghRepository repositories.Repository, claRepository projects_cla_groups.Repository) Service {
+ return Service{
repo: repo,
ghRepository: ghRepository,
claRepository: claRepository,
}
}
-func (s service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+// AddGithubOrganization adds the github organization for the specified project
+func (s Service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -71,7 +73,8 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
return s.repo.AddGithubOrganization(ctx, parentProjectSFID, projectSFID, input)
}
-func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+// GetGithubOrganizations returns the github organization for the specified project
+func (s Service) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -130,11 +133,13 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return &gitHubOrgModels, err
}
-func (s service) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
+// GetGithubOrganizationsByParent returns the github organizations for the specified parent project SFID
+func (s Service) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
return s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
}
-func (s service) GetGithubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error) {
+// GetGithubOrganizationByName returns the github organizations for the specified github organization name
+func (s Service) GetGithubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -158,7 +163,8 @@ func (s service) GetGithubOrganizationByName(ctx context.Context, githubOrgName
return gitHubOrgs.List[0], err
}
-func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+// UpdateGithubOrganization updates the specified github organization based on the project SFID, organization name provided values
+func (s Service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
if _, err := s.claRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
@@ -168,7 +174,8 @@ func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID strin
return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
}
-func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
+// DeleteGithubOrganization removes the specified github organization under the projectSFID
+func (s Service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -193,7 +200,7 @@ func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
}
// RemoveDuplicates removes any duplicates from the specified list
-func (s service) RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization {
+func (s Service) RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization {
if input == nil {
return nil
}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 6f51ab76b..03af483fb 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -108,13 +108,13 @@ type repository struct {
usersRepo users.UserRepository
eventsService events.Service
repositoriesRepo repositories.Repository
- ghOrgRepo github_organizations.Repository
+ ghOrgRepo github_organizations.RepositoryInterface
gerritService gerrits.Service
signatureTableName string
}
// NewRepository creates a new instance of the whitelist service
-func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.Repository, gerritService gerrits.Service) SignatureRepository {
+func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.RepositoryInterface, gerritService gerrits.Service) SignatureRepository {
return repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
@@ -2563,7 +2563,7 @@ func (repo repository) sendEmail(ctx context.Context, email string, approvalList
}
// check for signature type (CCLA, ICLA, ECLA)
- var removalType string = ""
+ var removalType = ""
// case 1 CCLA
if len(iclas) == 0 && len(eclas) == 0 {
diff --git a/cla-backend-go/template/handlers.go b/cla-backend-go/template/handlers.go
index c4074da60..4bb758510 100644
--- a/cla-backend-go/template/handlers.go
+++ b/cla-backend-go/template/handlers.go
@@ -14,7 +14,7 @@ import (
)
// Configure API call
-func Configure(api *operations.ClaAPI, service Service, eventsService events.Service) {
+func Configure(api *operations.ClaAPI, service ServiceInterface, eventsService events.Service) {
// Retrieve a list of available templates
api.TemplateGetTemplatesHandler = template.GetTemplatesHandlerFunc(func(params template.GetTemplatesParams, claUser *user.CLAUser) middleware.Responder {
diff --git a/cla-backend-go/template/repository.go b/cla-backend-go/template/repository.go
index 546ba85dc..99267674f 100644
--- a/cla-backend-go/template/repository.go
+++ b/cla-backend-go/template/repository.go
@@ -35,8 +35,8 @@ var (
ASWFStyleTemplateID = "18b8ad08-d7d4-4d75-ad25-30bbfffd59cf"
)
-// Repository interface functions
-type Repository interface {
+// RepositoryInterface interface functions
+type RepositoryInterface interface {
GetTemplates(ctx context.Context) ([]models.Template, error)
GetTemplate(templateID string) (models.Template, error)
CLAGroupTemplateExists(ctx context.Context, templateID string) bool
@@ -45,7 +45,8 @@ type Repository interface {
UpdateDynamoContractGroupTemplates(ctx context.Context, ContractGroupID string, template models.Template, pdfUrls models.TemplatePdfs, projectCCLAEnabled, projectICLAEnabled bool) error
}
-type repository struct {
+// Repository object/struct
+type Repository struct {
stage string // The AWS stage (dev, staging, prod)
dynamoDBClient *dynamodb.DynamoDB
}
@@ -98,16 +99,16 @@ type DocumentTab struct {
DocumentTabAnchorIgnoreIfNotPresent bool `json:"document_tab_anchor_ignore_if_not_present"`
}
-// NewRepository creates a new instance of the repository service
-func NewRepository(awsSession *session.Session, stage string) repository {
- return repository{
+// NewRepository creates a new instance of the Repository service
+func NewRepository(awsSession *session.Session, stage string) Repository {
+ return Repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
}
}
// GetTemplates returns a list containing all the template models
-func (r repository) GetTemplates(ctx context.Context) ([]models.Template, error) {
+func (r Repository) GetTemplates(ctx context.Context) ([]models.Template, error) {
f := logrus.Fields{
"functionName": "GetTemplates",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -129,7 +130,7 @@ func (r repository) GetTemplates(ctx context.Context) ([]models.Template, error)
}
// GetTemplate returns the template based on the template ID
-func (r repository) GetTemplate(templateID string) (models.Template, error) {
+func (r Repository) GetTemplate(templateID string) (models.Template, error) {
template, ok := templateMap[templateID]
if !ok {
return models.Template{}, ErrTemplateNotFound
@@ -139,15 +140,15 @@ func (r repository) GetTemplate(templateID string) (models.Template, error) {
}
// CLAGroupTemplateExists return true if the specified template ID exists, false otherwise
-func (r repository) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
+func (r Repository) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
_, ok := templateMap[templateID]
return ok
}
// GetCLAGroup This method belongs in the contract group package. We are leaving it here
-// because it accesses DynamoDB, but the contract group repository is designed
+// because it accesses DynamoDB, but the contract group Repository is designed
// to connect to postgres
-func (r repository) GetCLAGroup(claGroupID string) (*models.ClaGroup, error) {
+func (r Repository) GetCLAGroup(claGroupID string) (*models.ClaGroup, error) {
log.Debugf("GetCLAGroup - claGroupID: %s", claGroupID)
dbModel, err := r.fetchCLAGroup(claGroupID)
@@ -158,7 +159,7 @@ func (r repository) GetCLAGroup(claGroupID string) (*models.ClaGroup, error) {
}
// GetCLADocuments fetches the cla documents inside of the CLA Group, it's separate method for perf reasons
-func (r repository) GetCLADocuments(claGroupID string, claType string) ([]models.ClaGroupDocument, error) {
+func (r Repository) GetCLADocuments(claGroupID string, claType string) ([]models.ClaGroupDocument, error) {
log.Debugf("GetCLADocuments - claGroupID: %s - claType : %s", claGroupID, claType)
dbModel, err := r.fetchCLAGroup(claGroupID)
if err != nil {
@@ -179,7 +180,7 @@ func (r repository) GetCLADocuments(claGroupID string, claType string) ([]models
return projectDocuments, nil
}
-func (r repository) buildProjectDocuments(dbProjectDocumentModels []DBProjectDocumentModel) []models.ClaGroupDocument {
+func (r Repository) buildProjectDocuments(dbProjectDocumentModels []DBProjectDocumentModel) []models.ClaGroupDocument {
if len(dbProjectDocumentModels) == 0 {
return nil
}
@@ -204,7 +205,7 @@ func (r repository) buildProjectDocuments(dbProjectDocumentModels []DBProjectDoc
}
// fetchCLAGroup brings back the CLA db model from dynamodb
-func (r repository) fetchCLAGroup(claGroupID string) (*DBProjectModel, error) {
+func (r Repository) fetchCLAGroup(claGroupID string) (*DBProjectModel, error) {
var dbModel DBProjectModel
tableName := fmt.Sprintf("cla-%s-projects", r.stage)
@@ -230,7 +231,7 @@ func (r repository) fetchCLAGroup(claGroupID string) (*DBProjectModel, error) {
}
// buildProjectModel maps the database model to the API response model
-func (r repository) buildProjectModel(dbModel DBProjectModel) *models.ClaGroup {
+func (r Repository) buildProjectModel(dbModel DBProjectModel) *models.ClaGroup {
return &models.ClaGroup{
ProjectID: dbModel.ProjectID,
ProjectExternalID: dbModel.ProjectExternalID,
@@ -246,7 +247,7 @@ func (r repository) buildProjectModel(dbModel DBProjectModel) *models.ClaGroup {
}
// UpdateDynamoContractGroupTemplates updates the templates in the data store
-func (r repository) UpdateDynamoContractGroupTemplates(ctx context.Context, claGroupID string, template models.Template, pdfUrls models.TemplatePdfs, projectCCLAEnabled, projectICLAEnabled bool) error {
+func (r Repository) UpdateDynamoContractGroupTemplates(ctx context.Context, claGroupID string, template models.Template, pdfUrls models.TemplatePdfs, projectCCLAEnabled, projectICLAEnabled bool) error {
f := logrus.Fields{
"functionName": "UpdateDynamoContractGroupTemplates",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index a73aa043b..4782a5d4e 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -33,8 +33,8 @@ const (
claTypeCCLA = "ccla"
)
-// Service interface
-type Service interface {
+// ServiceInterface interface
+type ServiceInterface interface {
GetTemplates(ctx context.Context) ([]models.Template, error)
CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error)
CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error)
@@ -42,16 +42,17 @@ type Service interface {
CLAGroupTemplateExists(ctx context.Context, templateID string) bool
}
-type service struct {
+// Service object/struct
+type Service struct {
stage string // The AWS stage (dev, staging, prod)
- templateRepo Repository
+ templateRepo RepositoryInterface
docRaptorClient docraptor.Client
s3Client *s3manager.Uploader
}
// NewService API call
-func NewService(stage string, templateRepo Repository, docRaptorClient docraptor.Client, awsSession *session.Session) service {
- return service{
+func NewService(stage string, templateRepo RepositoryInterface, docRaptorClient docraptor.Client, awsSession *session.Session) Service {
+ return Service{
stage: stage,
templateRepo: templateRepo,
docRaptorClient: docRaptorClient,
@@ -60,7 +61,7 @@ func NewService(stage string, templateRepo Repository, docRaptorClient docraptor
}
// GetTemplates API call
-func (s service) GetTemplates(ctx context.Context) ([]models.Template, error) {
+func (s Service) GetTemplates(ctx context.Context) ([]models.Template, error) {
f := logrus.Fields{
"functionName": "v1.template.service.GetTemplates",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -82,7 +83,8 @@ func (s service) GetTemplates(ctx context.Context) ([]models.Template, error) {
return templates, nil
}
-func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error) {
+// CreateTemplatePreview returns a PDF using the specified CLA Group field values and template identifier
+func (s Service) CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error) {
f := logrus.Fields{
"functionName": "v1.template.service.CreateTemplatePreview",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -137,7 +139,7 @@ func (s service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
}
// CreateCLAGroupTemplate service method
-func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error) {
+func (s Service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error) {
f := logrus.Fields{
"functionName": "v1.template.service.CreateCLAGroupTemplate",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -266,7 +268,8 @@ func (s service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
return pdfUrls, nil
}
-func (s service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType string, watermark bool) ([]byte, error) {
+// GetCLATemplatePreview returns a preview of the specified CLA Group and CLA type
+func (s Service) GetCLATemplatePreview(ctx context.Context, claGroupID, claType string, watermark bool) ([]byte, error) {
f := logrus.Fields{
"functionName": "v1.template.service.GetCLATemplatePreview",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -434,7 +437,7 @@ func getLatestDocument(ctx context.Context, documents []models.ClaGroupDocument)
}
// InjectProjectInformationIntoTemplate service function
-func (s service) InjectProjectInformationIntoTemplate(template models.Template, metaFields []*models.MetaField) (string, string, error) {
+func (s Service) InjectProjectInformationIntoTemplate(template models.Template, metaFields []*models.MetaField) (string, string, error) {
f := logrus.Fields{
"functionName": "v1.template.service.InjectProjectInformationIntoTemplate",
"templateName": template.Name,
@@ -480,7 +483,7 @@ func (s service) InjectProjectInformationIntoTemplate(template models.Template,
}
// generateTemplateS3FilePath helper function to generate a suitable s3 path and filename for the template
-func (s service) generateTemplateS3FilePath(claGroupID, claType string) string {
+func (s Service) generateTemplateS3FilePath(claGroupID, claType string) string {
fileNameTemplate := "contract-group/%s/template/%s"
var ext string
switch claType {
@@ -497,7 +500,7 @@ func (s service) generateTemplateS3FilePath(claGroupID, claType string) string {
}
// SaveTemplateToS3 uploads the specified template contents to S3 storage
-func (s service) SaveTemplateToS3(bucket, filepath string, template io.ReadCloser) (string, error) {
+func (s Service) SaveTemplateToS3(bucket, filepath string, template io.ReadCloser) (string, error) {
f := logrus.Fields{
"functionName": "v1.template.service.SaveTemplateToS3",
"bucket": bucket,
@@ -526,6 +529,6 @@ func (s service) SaveTemplateToS3(bucket, filepath string, template io.ReadClose
}
// CLAGroupTemplateExists return true if the specified template ID exists, false otherwise
-func (s service) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
+func (s Service) CLAGroupTemplateExists(ctx context.Context, templateID string) bool {
return s.templateRepo.CLAGroupTemplateExists(ctx, templateID)
}
diff --git a/cla-backend-go/user/repository.go b/cla-backend-go/user/repository.go
index 976c2e463..c531c0506 100644
--- a/cla-backend-go/user/repository.go
+++ b/cla-backend-go/user/repository.go
@@ -11,26 +11,27 @@ import (
"github.com/jmoiron/sqlx"
)
-// Repository interface methods
-type Repository interface {
+// RepositoryInterface interface methods
+type RepositoryInterface interface {
GetUserAndProfilesByLFID(lfidUsername string) (CLAUser, error)
GetUserProjectIDs(userID string) ([]string, error)
GetClaManagerCorporateClaIDs(userID string) ([]string, error)
}
-type repository struct {
+// Repository object/struct
+type Repository struct {
db *sqlx.DB
}
// NewRepository creates a new user repository
-func NewRepository(db *sqlx.DB) repository {
- return repository{
+func NewRepository(db *sqlx.DB) Repository {
+ return Repository{
db: db,
}
}
// GetUserAndProfilesByLFID get user profile by LFID
-func (repo repository) GetUserAndProfilesByLFID(lfidUsername string) (CLAUser, error) {
+func (repo Repository) GetUserAndProfilesByLFID(lfidUsername string) (CLAUser, error) {
log.Debugf("lfidUsername: %s", lfidUsername)
sql := `
SELECT
@@ -74,13 +75,13 @@ func (repo repository) GetUserAndProfilesByLFID(lfidUsername string) (CLAUser, e
}
// GetUserByGithubID returns the user details based on the github ID
-func (repo repository) GetUserByGithubID(githubID string) (CLAUser, error) {
+func (repo Repository) GetUserByGithubID(githubID string) (CLAUser, error) {
// TODO: Implement when adding authentication to the Corporate Console
return CLAUser{}, nil
}
// GetUserProjectIDs get the user project ID's based on the specified user ID
-func (repo repository) GetUserProjectIDs(userID string) ([]string, error) {
+func (repo Repository) GetUserProjectIDs(userID string) ([]string, error) {
getUserProjectIDsSQL := `
SELECT
project_sfdc_id
@@ -113,7 +114,7 @@ func (repo repository) GetUserProjectIDs(userID string) ([]string, error) {
}
// GetClaManagerCorporateClaIDs returns a list of CLA manager corporate CLAs associated with the specified user
-func (repo repository) GetClaManagerCorporateClaIDs(userID string) ([]string, error) {
+func (repo Repository) GetClaManagerCorporateClaIDs(userID string) ([]string, error) {
getClaManagerCorporateClaIDsSQL := `
SELECT
corporate_cla_group_id
@@ -146,6 +147,6 @@ func (repo repository) GetClaManagerCorporateClaIDs(userID string) ([]string, er
}
// GetUserCompanyIDs returns a list of company IDs based on the user
-func (repo repository) GetUserCompanyIDs(userID string) ([]string, error) {
+func (repo Repository) GetUserCompanyIDs(userID string) ([]string, error) {
return []string{}, nil
}
diff --git a/cla-backend-go/user/service.go b/cla-backend-go/user/service.go
index 2ed8164d8..24daf8b0a 100644
--- a/cla-backend-go/user/service.go
+++ b/cla-backend-go/user/service.go
@@ -11,11 +11,11 @@ type Service interface { // nolint
}
type service struct {
- repo Repository
+ repo RepositoryInterface
}
// NewService creates a new user service
-func NewService(repo Repository) Service {
+func NewService(repo RepositoryInterface) Service {
return service{
repo: repo,
}
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 7496813b1..1d35947d4 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -237,10 +237,14 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(projectTree.Parent.ID)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
- return err
+ isLFParent := false
+ // if we have a project tree parent ID - check to see if it is one of our root parents
+ if projectTree != nil && projectTree.Parent != nil && projectTree.Parent.ID != "" {
+ isLFParent, err = psc.IsTheLinuxFoundation(projectTree.Parent.ID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
+ return err
+ }
}
for _, projectSFID := range projectSFIDList {
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index b70f919f7..87d436c4f 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -41,7 +41,7 @@ import (
type service struct {
v1ProjectService v1Project.Service
- v1TemplateService v1Template.Service
+ v1TemplateService v1Template.ServiceInterface
projectsClaGroupsRepo projects_cla_groups.Repository
claManagerRequests v1ClaManager.IService
signatureService signatureService.SignatureService
@@ -68,7 +68,7 @@ type Service interface {
}
// NewService returns instance of CLA group service
-func NewService(projectService v1Project.Service, templateService v1Template.Service, projectsClaGroupsRepo projects_cla_groups.Repository, claMangerRequests v1ClaManager.IService, signatureService signatureService.SignatureService, metricsRepo metrics.Repository, gerritService gerrits.Service, repositoriesService repositories.Service, eventsService events.Service) Service {
+func NewService(projectService v1Project.Service, templateService v1Template.ServiceInterface, projectsClaGroupsRepo projects_cla_groups.Repository, claMangerRequests v1ClaManager.IService, signatureService signatureService.SignatureService, metricsRepo metrics.Repository, gerritService gerrits.Service, repositoriesService repositories.Service, eventsService events.Service) Service {
return &service{
v1ProjectService: projectService, // aka cla_group service of v1
v1TemplateService: templateService,
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index 79a7fabac..a0d9a7dfb 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -41,7 +41,7 @@ type AutoEnableService interface {
// NewAutoEnableService creates a new AutoEnableService
func NewAutoEnableService(repositoryService repositories.Service,
githubRepo repositories.Repository,
- githubOrgRepo github_organizations.Repository,
+ githubOrgRepo github_organizations.RepositoryInterface,
claRepository projects_cla_groups.Repository,
claService project.Service,
) AutoEnableService {
@@ -59,7 +59,7 @@ func NewAutoEnableService(repositoryService repositories.Service,
type autoEnableServiceProvider struct {
repositoryService repositories.Service
githubRepo repositories.Repository
- githubOrgRepo github_organizations.Repository
+ githubOrgRepo github_organizations.RepositoryInterface
claRepository projects_cla_groups.Repository
claService project.Service
}
diff --git a/cla-backend-go/v2/dynamo_events/service.go b/cla-backend-go/v2/dynamo_events/service.go
index 7fcef6ef3..896167d40 100644
--- a/cla-backend-go/v2/dynamo_events/service.go
+++ b/cla-backend-go/v2/dynamo_events/service.go
@@ -58,7 +58,7 @@ type service struct {
eventsRepo claevent.Repository
projectRepo project.ProjectRepository
projectService project.Service
- githubOrgService github_organizations.Service
+ githubOrgService github_organizations.ServiceInterface
repositoryService repositories.Service
gerritService gerrits.Service
autoEnableService *autoEnableServiceProvider
@@ -80,7 +80,7 @@ func NewService(stage string,
eventsRepo claevent.Repository,
projectRepo project.ProjectRepository,
projService project.Service,
- githubOrgService github_organizations.Service,
+ githubOrgService github_organizations.ServiceInterface,
repositoryService repositories.Service,
gerritService gerrits.Service,
claManagerRequestsRepo cla_manager.IRepository,
diff --git a/cla-backend-go/v2/dynamo_events/signatures.go b/cla-backend-go/v2/dynamo_events/signatures.go
index a7faff862..6fe85c609 100644
--- a/cla-backend-go/v2/dynamo_events/signatures.go
+++ b/cla-backend-go/v2/dynamo_events/signatures.go
@@ -227,14 +227,15 @@ func (s *service) SignatureSignedEvent(event events.DynamoDBEventRecord) error {
}
} else {
for _, projectCLAGroup := range projectCLAGroups {
+ pcg := projectCLAGroup // make a copy of the loop variable to use in the closure, avoids the loopclosure: loop variable projectCLAGroup captured by func literal lint error
eg.Go(func() error {
// Remove any roles that were previously assigned for cla-manager-designee
log.WithFields(f).Debugf("removing existing %s role for project: '%s' (%s) and company: '%s' (%s)",
- utils.CLADesigneeRole, projectCLAGroup.ProjectName, projectCLAGroup.ProjectSFID, companyModel.CompanyName, companyModel.CompanyExternalID)
- err = s.removeCLAPermissionsByProjectOrganizationRole(ctx, projectCLAGroup.ProjectSFID, companyModel.CompanyExternalID, []string{utils.CLADesigneeRole})
+ utils.CLADesigneeRole, pcg.ProjectName, pcg.ProjectSFID, companyModel.CompanyName, companyModel.CompanyExternalID)
+ err = s.removeCLAPermissionsByProjectOrganizationRole(ctx, pcg.ProjectSFID, companyModel.CompanyExternalID, []string{utils.CLADesigneeRole})
if err != nil {
log.WithFields(f).Warnf("failed to remove %s roles for project: '%s' (%s) and company: '%s' (%s), error: %+v",
- utils.CLADesigneeRole, projectCLAGroup.ProjectName, projectCLAGroup.ProjectSFID, companyModel.CompanyName, companyModel.CompanyExternalID, err)
+ utils.CLADesigneeRole, pcg.ProjectName, pcg.ProjectSFID, companyModel.CompanyName, companyModel.CompanyExternalID, err)
return err
}
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index 19b12d989..a44c0398e 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -37,7 +37,7 @@ type Service interface {
type eventHandlerService struct {
githubRepo repositories.Repository
- githubOrgRepo v1GithubOrg.Repository
+ githubOrgRepo v1GithubOrg.RepositoryInterface
eventService events.Service
autoEnableService dynamo_events.AutoEnableService
emailService emails.Service
@@ -46,7 +46,7 @@ type eventHandlerService struct {
// NewService creates a new instance of the Event Handler Service
func NewService(githubRepo repositories.Repository,
- githubOrgRepo v1GithubOrg.Repository,
+ githubOrgRepo v1GithubOrg.RepositoryInterface,
eventService events.Service,
autoEnableService dynamo_events.AutoEnableService,
emailService emails.Service) Service {
@@ -55,7 +55,7 @@ func NewService(githubRepo repositories.Repository,
}
func newService(githubRepo repositories.Repository,
- githubOrgRepo v1GithubOrg.Repository,
+ githubOrgRepo v1GithubOrg.RepositoryInterface,
eventService events.Service,
autoEnableService dynamo_events.AutoEnableService,
emailService emails.Service,
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index d70513a83..4a7d9e553 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -46,14 +46,14 @@ type Service interface {
}
type service struct {
- repo v1GithubOrg.Repository
+ repo v1GithubOrg.RepositoryInterface
ghRepository v1Repositories.Repository
- ghService v1GithubOrg.Service
+ ghService v1GithubOrg.ServiceInterface
projectsCLAGroupService projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo v1GithubOrg.Repository, ghRepository v1Repositories.Repository, projectsCLAGroupService projects_cla_groups.Repository, ghService v1GithubOrg.Service) Service {
+func NewService(repo v1GithubOrg.RepositoryInterface, ghRepository v1Repositories.Repository, projectsCLAGroupService projects_cla_groups.Repository, ghService v1GithubOrg.ServiceInterface) Service {
return service{
repo: repo,
ghRepository: ghRepository,
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 07a5010ee..2d315fb9a 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -40,7 +40,7 @@ import (
)
// Configure setups handlers on api with service
-func Configure(api *operations.EasyclaAPI, claGroupService project.Service, projectRepo project.ProjectRepository, companyService company.IService, v1SignatureService signatureService.SignatureService, sessionStore *dynastore.Store, eventsService events.Service, v2service Service, projectClaGroupsRepo projects_cla_groups.Repository) { //nolint
+func Configure(api *operations.EasyclaAPI, claGroupService project.Service, projectRepo project.ProjectRepository, companyService company.IService, v1SignatureService signatureService.SignatureService, sessionStore *dynastore.Store, eventsService events.Service, v2service ServiceInterface, projectClaGroupsRepo projects_cla_groups.Repository) { //nolint
const problemLoadingCLAGroupByID = "problem loading cla group by ID"
const iclaNotSupportedForCLAGroup = "individual contribution is not supported for this project"
@@ -1370,7 +1370,7 @@ func isUserHaveAccessOfSignedSignaturePDF(ctx context.Context, authUser *auth.Us
}
// Corporate signature...we can check the company details
- if signature.SignatureType == CclaSignatureType {
+ if signature.SignatureType == utils.SignatureTypeCCLA {
comp, err := companyService.GetCompany(ctx, signature.SignatureReferenceID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("failed to load company record using signature reference id: %s", signature.SignatureReferenceID)
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 1984e5ed7..08507fc6a 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -34,30 +34,17 @@ import (
// constants
const (
- // used when we want to query all data from dependent service.
- HugePageSize = int64(10000)
- CclaSignatureType = "ccla"
- ClaSignatureType = "cla"
+ // HugePageSize constant for querying signatures
+ HugePageSize = int64(10000)
)
-// errors
var (
+ // ErrZipNotPresent error
ErrZipNotPresent = errors.New("zip file not present")
)
-type service struct {
- v1ProjectService project.Service
- v1CompanyService company.IService
- v1SignatureService signatures.SignatureService
- v1SignatureRepo signatures.SignatureRepository
- usersService users.Service
- projectsClaGroupsRepo projects_cla_groups.Repository
- s3 *s3.S3
- signaturesBucket string
-}
-
-// Service contains method of v2 signature service
-type Service interface {
+// ServiceInterface contains method of v2 signature service
+type ServiceInterface interface {
GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error)
GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error)
@@ -70,6 +57,17 @@ type Service interface {
InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error
}
+type service struct {
+ v1ProjectService project.Service
+ v1CompanyService company.IService
+ v1SignatureService signatures.SignatureService
+ v1SignatureRepo signatures.SignatureRepository
+ usersService users.Service
+ projectsClaGroupsRepo projects_cla_groups.Repository
+ s3 *s3.S3
+ signaturesBucket string
+}
+
// NewService creates instance of v2 signature service
func NewService(awsSession *session.Session, signaturesBucketName string, v1ProjectService project.Service,
v1CompanyService company.IService,
@@ -206,14 +204,14 @@ func (s service) GetSignedDocument(ctx context.Context, signatureID string) (*mo
if err != nil {
return nil, err
}
- if sig.SignatureType == ClaSignatureType && sig.CompanyName != "" {
+ if sig.SignatureType == utils.SignatureTypeCLA && sig.CompanyName != "" {
return nil, errors.New("bad request. employee signature does not have signed document")
}
var url string
switch sig.SignatureType {
- case ClaSignatureType:
+ case utils.SignatureTypeCLA:
url = utils.SignedCLAFilename(sig.ProjectID, "icla", sig.SignatureReferenceID, sig.SignatureID)
- case CclaSignatureType:
+ case utils.SignatureTypeCCLA:
url = utils.SignedCLAFilename(sig.ProjectID, "ccla", sig.SignatureReferenceID, sig.SignatureID)
}
signedURL, err := utils.GetDownloadLink(url)
diff --git a/cla-backend-go/v2/template/handlers.go b/cla-backend-go/v2/template/handlers.go
index 9f305052b..d854234e0 100644
--- a/cla-backend-go/v2/template/handlers.go
+++ b/cla-backend-go/v2/template/handlers.go
@@ -29,7 +29,7 @@ import (
)
// Configure API call
-func Configure(api *operations.EasyclaAPI, service v1Template.Service, v1ProjectClaGroupService v1ProjectsCLAGroups.Service, eventsService v1Events.Service) {
+func Configure(api *operations.EasyclaAPI, service v1Template.ServiceInterface, v1ProjectClaGroupService v1ProjectsCLAGroups.Service, eventsService v1Events.Service) {
// Retrieve a list of available templates
api.TemplateGetTemplatesHandler = template.GetTemplatesHandlerFunc(func(params template.GetTemplatesParams, user *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
From a40e5f5b020c067b4196928eabd7ace64e32efb5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:43:50 -0700
Subject: [PATCH 0324/1276] Bump set-getter from 0.1.0 to 0.1.1 in
/cla-backend-go (#3004)
Bumps [set-getter](https://github.com/doowb/set-getter) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/doowb/set-getter/releases)
- [Commits](https://github.com/doowb/set-getter/commits/0.1.1)
---
updated-dependencies:
- dependency-name: set-getter
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index a94e9c28c..c19b7b764 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -4384,9 +4384,9 @@ set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
From 75f34b20094c82a395e13c0ab1c97a63adfb6b0d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:44:10 -0700
Subject: [PATCH 0325/1276] Bump set-getter from 0.1.0 to 0.1.1 in
/cla-frontend-project-console (#3003)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-project-console/yarn.lock b/cla-frontend-project-console/yarn.lock
index c334447b7..e032b9b67 100644
--- a/cla-frontend-project-console/yarn.lock
+++ b/cla-frontend-project-console/yarn.lock
@@ -5625,9 +5625,9 @@ set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
From 8322bea8d22b709aec545b97abb87a12e10a0e38 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:45:23 -0700
Subject: [PATCH 0326/1276] Bump set-getter from 0.1.0 to 0.1.1 in
/cla-frontend-contributor-console (#2999)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 71ba0f0e7..72f50ceb6 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -5713,9 +5713,9 @@ set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
From b04f7e104d24f7c3830c0a8e261b63fc9e283f88 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:46:07 -0700
Subject: [PATCH 0327/1276] Bump set-getter from 0.1.0 to 0.1.1 in
/cla-frontend-corporate-console (#3002)
Bumps [set-getter](https://github.com/doowb/set-getter) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/doowb/set-getter/releases)
- [Commits](https://github.com/doowb/set-getter/commits/0.1.1)
---
updated-dependencies:
- dependency-name: set-getter
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 1fa89e32d..3d08370c1 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -5669,9 +5669,9 @@ set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
From 28815aacb4a76cab51aa6aea859e8895186dbd37 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:46:32 -0700
Subject: [PATCH 0328/1276] Bump set-getter from 0.1.0 to 0.1.1 in /cla-backend
(#3000)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 9e8e865fb..bb921ef4b 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -5452,9 +5452,9 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
From 6b0f525aa1a5d0f3b197d54ca2a3fea45a98f01b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:46:58 -0700
Subject: [PATCH 0329/1276] Bump color-string from 1.5.4 to 1.5.5 in
/cla-backend (#3011)
Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/Qix-/color-string/releases)
- [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.5.5)
---
updated-dependencies:
- dependency-name: color-string
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index bb921ef4b..31b8c1bf3 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -1754,9 +1754,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
- version "1.5.4"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
+ integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
From cabe01a50d95a11f7d5e70b80dc5d7ca1673afe7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:47:27 -0700
Subject: [PATCH 0330/1276] Bump color-string from 1.5.4 to 1.5.5 in
/cla-frontend-corporate-console (#3012)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 3d08370c1..8c8b1f913 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -1624,9 +1624,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
- version "1.5.4"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
+ integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
From 880d096f4079df445b009804f5244dd55559fa65 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:47:55 -0700
Subject: [PATCH 0331/1276] Bump color-string from 1.5.4 to 1.5.5 in
/cla-frontend-contributor-console (#3010)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 72f50ceb6..d7e1b8ee0 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -1637,9 +1637,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
- version "1.5.4"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
+ integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
From 0ef7a8ebd94dddf6a74cc60d5f46a069d7708f6c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:48:37 -0700
Subject: [PATCH 0332/1276] Bump color-string from 1.5.4 to 1.5.5 (#3009)
Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/Qix-/color-string/releases)
- [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.5.5)
---
updated-dependencies:
- dependency-name: color-string
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index a72f22243..d323fbb34 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1267,9 +1267,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
- version "1.5.4"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
+ integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
From eeb7f53accf37083ac3c374619e6d339eae9e275 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Jun 2021 12:49:31 -0700
Subject: [PATCH 0333/1276] Bump color-string from 1.5.4 to 1.5.5 in
/cla-backend-go (#3008)
Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/Qix-/color-string/releases)
- [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Qix-/color-string/compare/1.5.4...1.5.5)
---
updated-dependencies:
- dependency-name: color-string
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index c19b7b764..678865e5d 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -1376,9 +1376,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
- version "1.5.4"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
+ integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
From 2f821a1957f57688c275ac8758ea0388a0cf9984 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Fri, 25 Jun 2021 18:03:38 +0300
Subject: [PATCH 0334/1276] removing the signature_acl default argument set
which was causing issues (#3013)
---
cla-backend/cla/models/dynamo_models.py | 40 ++++++++++++++++++++++---
1 file changed, 36 insertions(+), 4 deletions(-)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index c1d2d342f..221106e6e 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -2272,7 +2272,7 @@ class Meta:
signature_return_url = UnicodeAttribute(null=True)
signature_callback_url = UnicodeAttribute(null=True)
signature_user_ccla_company_id = UnicodeAttribute(null=True)
- signature_acl = UnicodeSetAttribute(default=set())
+ signature_acl = UnicodeSetAttribute()
signature_project_index = ProjectSignatureIndex()
signature_reference_index = ReferenceSignatureIndex()
signature_envelope_id = UnicodeAttribute(null=True)
@@ -2538,7 +2538,7 @@ def get_signature_user_ccla_company_id(self):
return self.model.signature_user_ccla_company_id
def get_signature_acl(self):
- return self.model.signature_acl
+ return self.model.signature_acl or set()
def get_signature_return_url_type(self):
# Refers to either Gerrit or GitHub
@@ -2716,11 +2716,15 @@ def set_signature_project_external_id(self, signature_project_external_id):
self.model.signature_project_external_id = signature_project_external_id
def add_signature_acl(self, username):
+ if not self.model.signature_acl:
+ self.model.signature_acl = set()
self.model.signature_acl.add(username)
def remove_signature_acl(self, username):
- if username in self.model.signature_acl:
- self.model.signature_acl.remove(username)
+ current_acl = self.model.signature_acl or set()
+ if username not in current_acl:
+ return
+ self.model.signature_acl.remove(username)
def set_user_email(self, user_email):
self.model.user_email = user_email
@@ -2939,6 +2943,34 @@ def get_employee_signature_by_company_project(self, company_id, project_id, user
"Why do we have more than one employee signature for this user? - Will return the first one only.")
return signatures[0]
+ def get_employee_signature_by_company_project_list(self, company_id, project_id, user_id) -> Optional[List[Signature]]:
+ """
+ Returns the employee signature for the specified user associated with
+ the project/company. Returns None if no employee signature exists for
+ this set of query parameters.
+ """
+ signature_attributes = {
+ "signature_signed": True,
+ "signature_approved": True,
+ "signature_type": 'cla',
+ "signature_reference_type": 'user',
+ "signature_project_id": project_id,
+ "signature_user_ccla_company_id": company_id
+ }
+ filter_condition = create_filter(signature_attributes, SignatureModel)
+ signature_generator = self.model.signature_reference_index.query(
+ user_id, filter_condition=filter_condition
+ )
+ signatures = []
+ for signature_model in signature_generator:
+ signature = Signature()
+ signature.model = signature_model
+ signatures.append(signature)
+ # No employee signatures were found that were signed/approved
+ if len(signatures) == 0:
+ return None
+ return signatures
+
def get_employee_signatures_by_company_project_model(self, company_id, project_id) -> List[Signature]:
signature_attributes = {
"signature_signed": True,
From 71b7108e25c57b6509bf532276db52b45ca46cc3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 25 Jun 2021 14:33:44 -0700
Subject: [PATCH 0335/1276] Updated Parent/Child/GrandChild Tree Logic (#3014)
- Added additional checks for nested tree
- Added string function to printout the project tree
Signed-off-by: David Deal
---
cla-backend-go/.golangci.yaml | 9 +-
cla-backend-go/Makefile | 2 +-
.../service_discovery/service_discovery.go | 93 ----------
cla-backend-go/v2/cla_groups/helpers.go | 168 +++++++++---------
cla-backend-go/v2/project-service/client.go | 100 +++++++++--
cla-backend-go/v2/signatures/service.go | 41 +++--
6 files changed, 208 insertions(+), 205 deletions(-)
delete mode 100644 cla-backend-go/service_discovery/service_discovery.go
diff --git a/cla-backend-go/.golangci.yaml b/cla-backend-go/.golangci.yaml
index 29ee03471..ad4f90bca 100644
--- a/cla-backend-go/.golangci.yaml
+++ b/cla-backend-go/.golangci.yaml
@@ -34,6 +34,7 @@ linters-settings:
check-blank: true
govet:
check-shadowing: true
+ fieldalignment: true
revive:
min-confidence: 0
dupl:
@@ -41,10 +42,7 @@ linters-settings:
goconst:
min-len: 2
min-occurrences: 2
- maligned:
- # print struct with more effective memory layout or not, false by default
- suggest-new: true
-
+
linters:
disable-all: true
enable:
@@ -67,8 +65,9 @@ linters:
- unparam
- unused
- nakedret
- - maligned
+ #- maligned # The repository of the linter has been archived by the owner. Replaced by govet 'fieldalignment'.
#- dupl
+ - bodyclose
issues:
exclude-use-default: false
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 468f39d57..852fe6fe4 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -19,7 +19,7 @@ LDFLAGS=-ldflags "-s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X m
BUILD_TAGS=-tags aws_lambda
LINT_TOOL=$(shell go env GOPATH)/bin/golangci-lint
-LINT_VERSION=v1.37.0
+LINT_VERSION=v1.41.1
SWAGGER_TOOL_VERSION=v0.24.0
GO_PKGS=$(shell go list ./... | grep -v /vendor/ | grep -v /node_modules/)
GO_FILES=$(shell find . -type f -name '*.go' -not -path './vendor/*')
diff --git a/cla-backend-go/service_discovery/service_discovery.go b/cla-backend-go/service_discovery/service_discovery.go
deleted file mode 100644
index 0eddb94d3..000000000
--- a/cla-backend-go/service_discovery/service_discovery.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-package service_discovery
-
-import (
- "github.com/communitybridge/easycla/cla-backend-go/company"
- "github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/project"
- "github.com/communitybridge/easycla/cla-backend-go/signatures"
- "github.com/communitybridge/easycla/cla-backend-go/users"
- v2CompanyService "github.com/communitybridge/easycla/cla-backend-go/v2/company"
- v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project"
-)
-
-// ServiceDiscovery interface
-type ServiceDiscovery interface {
- SetEventService(eventService events.Service)
- GetEventService() events.Service
- SetUserService(userService users.Service)
- GetUserService() users.Service
- SetV1CompanyService(companyService company.IService)
- GetV1CompanyService() company.IService
- SetV2CompanyService(companyService v2CompanyService.Service)
- GetV2CompanyService() v2CompanyService.Service
- GetV1ProjectService() project.Service
- GetV2ProjectService() v2ProjectService.Service
- GetV1SignatureService() signatures.SignatureService
-}
-
-type service struct {
- eventService events.Service
- userService users.Service
- v1CompanyService company.IService
- v2CompanyService v2CompanyService.Service
- v1ProjectService project.Service
- v2ProjectService v2ProjectService.Service
- v1SignatureService signatures.SignatureService
-}
-
-// NewServiceDiscovery creates a new whitelist service
-func NewServiceDiscovery() ServiceDiscovery {
- return service{}
-}
-
-// GetEventService sets the event service reference
-func (s service) SetEventService(eventService events.Service) {
- s.eventService = eventService
-}
-
-// GetEventService returns a reference to the the event service
-func (s service) GetEventService() events.Service {
- return s.eventService
-}
-
-// SetUserService sets the user service reference
-func (s service) SetUserService(userService users.Service) {
- s.userService = userService
-}
-
-// GetUserService returns a reference to the the user service
-func (s service) GetUserService() users.Service {
- return s.userService
-}
-
-// SetV1CompanyService sets the v1 company service reference
-func (s service) SetV1CompanyService(companyService company.IService) {
- s.v1CompanyService = companyService
-}
-
-// GetV1CompanyService returns a reference to the the v1 company service
-func (s service) GetV1CompanyService() company.IService {
- return s.v1CompanyService
-}
-
-// SetV2CompanyService sets the v1 company service reference
-func (s service) SetV2CompanyService(companyService v2CompanyService.Service) {
- s.v2CompanyService = companyService
-}
-
-// GetV2CompanyService returns a reference to the the v2 company service
-func (s service) GetV2CompanyService() v2CompanyService.Service {
- return s.v2CompanyService
-}
-
-// GetV1ProjectService returns a reference to the the v1 project service
-func (s service) GetV1ProjectService() project.Service { return s.v1ProjectService }
-
-// GetV2ProjectService returns a reference to the the v2 project service
-func (s service) GetV2ProjectService() v2ProjectService.Service { return s.v2ProjectService }
-
-// GetV1SignatureService returns a reference to the the v1 signature service
-func (s service) GetV1SignatureService() signatures.SignatureService { return s.v1SignatureService }
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 1d35947d4..f426104a1 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -229,11 +229,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
}
// build Tree that tracks parent and child projects
- projectTree, err := buildProjectTree(foundationProjectSummary)
- if err != nil {
- log.WithFields(f).Warnf("unable to process project summary :%+v ", foundationProjectSummary)
- return err
- }
+ projectTree := buildProjectNode(foundationProjectSummary)
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
@@ -253,22 +249,18 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
return err
}
- if projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
+ if projectTree != nil && projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
foundationProjectDetails.Parent, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
-
}
// Check to see if all the provided enrolled projects are part of this foundation
-
- exists := projectsExist(projectTree, projectSFIDList)
-
- if !exists {
- log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", projectTree)
- return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the SF foundation", projectTree)
+ if !allProjectsExistInTree(projectTree, projectSFIDList) {
+ log.WithFields(f).Warnf("validation failure - one or more provided projects are not under the project tree: %+v", projectTree.String())
+ return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the parent", projectTree.String())
}
// check if projects are not already enabled
@@ -335,11 +327,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
}
// build Tree that tracks parent and child projects
- projectTree, err := buildProjectTree(foundationProjectSummary)
- if err != nil {
- log.WithFields(f).Warnf("unable to process project summary :%+v ", foundationProjectSummary)
- return err
- }
+ projectTree := buildProjectNode(foundationProjectSummary)
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
@@ -364,18 +352,10 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
}
- // Comment out the below as we want to support stand-alone projects
- /* if len(foundationProjectDetails.Projects) == 0 {
- log.WithFields(f).Warn("validation failure - project does not have any subprojects")
- return fmt.Errorf("bad request: invalid input to enroll projects. project does not have any subprojects")
- } */
-
// Check to see if all the provided enrolled projects are part of this foundation
- exists := projectsExist(projectTree, projectSFIDList)
-
- if !exists {
- log.WithFields(f).Warnf("validation failure - provided projects are not under the SF foundation: %+v", projectTree)
- return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the SF foundation", projectTree)
+ if !allProjectsExistInTree(projectTree, projectSFIDList) {
+ log.WithFields(f).Warnf("validation failure - one or more provided projects are not under the project tree: %+v", projectTree.String())
+ return fmt.Errorf("bad request: invalid project_sfid: %+v. One or more provided projects are not under the parent", projectTree.String())
}
// check if projects are already enrolled/enabled
@@ -732,85 +712,88 @@ func getUniqueCLAGroupIDs(projectCLAGroupMappings []*projects_cla_groups.Project
return keys
}
-// buildTree helper function that builds tree based on nessted Projects
-func buildProjectTree(projectSummaryList []*v2ProjectServiceModels.ProjectSummary) (*ProjectNode, error) {
- f := logrus.Fields{
- "functionName": "v2.cla_groups.helpers.buildTree",
+func buildProjectNode(projectSummaryList []*v2ProjectServiceModels.ProjectSummary) *ProjectNode {
+ root := &ProjectNode{
+ ID: "",
+ Name: "",
+ Children: nil,
}
- log.WithFields(f).Debugf("Building project summary tree for : %+v ...", projectSummaryList)
- var root ProjectNode
+ parentSFID := ""
+ parentName := ""
+ for _, projectSummaryEntry := range projectSummaryList {
+ // Get ParentProject
+ parentProjectModel, err := v2ProjectService.GetClient().GetParentProjectModel(projectSummaryEntry.ID)
- if len(projectSummaryList) == 0 {
- msg := "project summary list is empty"
- log.WithFields(f).Debugf(msg)
- return nil, errors.New(msg)
- }
- projectSummary := projectSummaryList[0]
- // Get ParentProject ID
- parentProjectID, err := v2ProjectService.GetClient().GetParentProject(projectSummary.ID)
- if err != nil {
- log.WithFields(f).Debugf("unable to get parent project for : %s ", projectSummary.ID)
- }
+ if parentSFID == "" && err == nil && parentProjectModel != nil {
+ // Update our root node
+ root.Parent = nil
+ root.ID = parentProjectModel.ID
+ root.Name = parentProjectModel.Name
- // Use Parent as root for projects to help in validation checks for enrolling / unenrolling
- root = ProjectNode{
- Parent: nil,
- ID: parentProjectID,
- Children: []*ProjectNode{{
- ID: projectSummary.ID,
- Name: projectSummary.Name,
- }},
- }
+ // Save the parentSFID
+ parentSFID = parentProjectModel.ID
+ parentName = parentProjectModel.Name
+ }
- // Aggregate projects
- for _, summary := range projectSummaryList {
- for _, child := range root.Children {
- child.addChildren(summary)
+ if parentSFID != "" && err == nil && parentProjectModel != nil && parentSFID != parentProjectModel.ID {
+ //We have different parents !!!
+ log.Warnf("current parent Name: %s ID: %s does not match other parent Name: %s, parent ID: %s", parentName, parentSFID, parentProjectModel.Name, parentProjectModel.ID)
}
+
+ root.Children = append(root.Children, getLeafNodeFromProjectSFID(projectSummaryEntry.ID, parentName, parentSFID))
}
- return &root, nil
+ return root
}
-func (n *ProjectNode) addChildren(summary *v2ProjectServiceModels.ProjectSummary) {
- f := logrus.Fields{
- "functionName": "v2.cla_groups.helpers.addChidlren",
+func getLeafNodeFromProjectSFID(projectSFID, parentName, parentSFID string) *ProjectNode {
+
+ // Get ParentProject
+ projectModel, err := v2ProjectService.GetClient().GetProject(projectSFID)
+ if err != nil {
+ return nil
}
- log.WithFields(f).Debugf("Agrregating children for %+v ...", summary)
- for _, subProject := range summary.Projects {
- child := &ProjectNode{
- Parent: n,
- ID: subProject.ID,
- Name: subProject.Name,
- }
- n.Children = append(n.Children, child)
- log.WithFields(f).Debugf("added child : %+v ", child)
+ node := &ProjectNode{
+ ID: projectModel.ID,
+ Name: projectModel.Name,
+ Parent: &ProjectNode{
+ ID: parentName,
+ Name: parentSFID,
+ },
+ }
+
+ // For this node, collect the list of child nodes...
+ for _, childNode := range projectModel.Projects {
+ node.Children = append(node.Children, getLeafNodeFromProjectSFID(childNode.ID, projectModel.Name, projectModel.ID))
+
}
+
+ return node
}
// findByID searches for given projectSFID recursively using DFS algorithm
func findByID(node *ProjectNode, projectSFID string) *ProjectNode {
- f := logrus.Fields{
- "functionName": "v2.cla_groups.helpers.fundByID",
+ if node == nil {
+ return nil
}
- log.WithFields(f).Debugf("searching for :%s ...", projectSFID)
if node.ID == projectSFID {
return node
}
- if len(node.Children) > 0 {
- for _, child := range node.Children {
- findByID(child, projectSFID)
+ for _, child := range node.Children {
+ foundNode := findByID(child, projectSFID)
+ if foundNode != nil {
+ return foundNode
}
}
return nil
}
-// projectsExist searches for given list of projects in foundation items
-func projectsExist(node *ProjectNode, projectSFIDs []string) bool {
+// allProjectsExistInTree searches for given list of projects in foundation items
+func allProjectsExistInTree(node *ProjectNode, projectSFIDs []string) bool {
for _, projectSFID := range projectSFIDs {
found := findByID(node, projectSFID)
if found == nil {
@@ -819,3 +802,28 @@ func projectsExist(node *ProjectNode, projectSFIDs []string) bool {
}
return true
}
+
+func (n *ProjectNode) String() string {
+ if n == nil {
+ return ""
+ }
+ msg := fmt.Sprintf("projectSFID: '%s', projectName: '%s'\n", n.ID, n.Name)
+
+ if n.Parent == nil {
+ msg = fmt.Sprintf("%s 'parentProjectSFID':'%s','parentProjectName':'%s'\n", msg, "", "")
+ } else {
+ msg = fmt.Sprintf("%s 'parentProjectSFID':'%s','parentProjectName':'%s'\n", msg, n.Parent.ID, n.Parent.Name)
+ }
+
+ if len(n.Children) == 0 {
+ msg = fmt.Sprintf("%s children: no children\n", msg)
+ } else {
+ for _, child := range n.Children {
+ if child != nil {
+ msg = fmt.Sprintf("%s 'child': %s\n", msg, child.String())
+ }
+ }
+ }
+
+ return msg
+}
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 28bb7f54f..72a6f1c12 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -38,8 +38,8 @@ type Client struct {
var (
projectServiceClient *Client
// Short term cache - only for the lifetime of this lambda
- projectServiceModes = make(map[string]*models.ProjectOutputDetailed)
- apiGWHost string
+ projectServiceModels = make(map[string]*models.ProjectOutputDetailed)
+ apiGWHost string
)
// InitClient initializes the user_service client
@@ -78,12 +78,12 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
}
// Lookup in cache first
- existingModel, exists := projectServiceModes[projectSFID]
+ existingModel, exists := projectServiceModels[projectSFID]
if exists {
- log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModes))
+ log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
return existingModel, nil
}
- log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModes))
+ log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModels))
tok, err := token.GetToken()
if err != nil {
@@ -100,8 +100,8 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
}
// Update our cache for next time
- projectServiceModes[projectSFID] = projectModel
- log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModes))
+ projectServiceModels[projectSFID] = projectModel
+ log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
return projectModel, nil
}
@@ -142,12 +142,12 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Lookup in cache first
- existingModel, exists := projectServiceModes[projectSFID]
+ existingModel, exists := projectServiceModels[projectSFID]
if exists {
- log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModes))
+ log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
return existingModel.Parent, nil
}
- log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModes))
+ log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModels))
log.WithFields(f).Debug("looking up projectModel in SF by projectSFID")
projectModel, err := pmm.GetProject(projectSFID)
@@ -157,8 +157,8 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
}
// Update our cache for next time
- projectServiceModes[projectSFID] = projectModel
- log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModes))
+ projectServiceModels[projectSFID] = projectModel
+ log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
// Do they have a parent?
if projectModel.Parent == "" || (projectModel.Foundation != nil &&
@@ -171,6 +171,82 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
return projectModel.Parent, nil
}
+// GetParentProjectModel returns the parent project model if there is a parent, otherwise returns nil
+func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOutputDetailed, error) {
+ f := logrus.Fields{
+ "functionName": "v2.project-service.client.GetParentProjectModel",
+ "projectSFID": projectSFID,
+ "apiGWHost": apiGWHost,
+ }
+
+ // Lookup in cache first
+ var exists bool
+ var existingModel *models.ProjectOutputDetailed
+ var existingParentModel *models.ProjectOutputDetailed
+
+ // Current project in the cache?
+ existingModel, exists = projectServiceModels[projectSFID]
+ if exists {
+ log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
+
+ // Parent in the cache?
+ existingParentModel, exists = projectServiceModels[existingModel.Parent]
+ if exists {
+ return existingParentModel, nil
+ }
+
+ // Parent project not in the cache - lookup
+ parentProjectModel, err := pmm.GetProject(existingModel.Parent)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", existingModel.Parent)
+ return nil, err
+ }
+
+ // Update our cache for next time
+ projectServiceModels[existingModel.Parent] = parentProjectModel
+ log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+
+ return parentProjectModel, nil
+ }
+
+ log.WithFields(f).Debugf("cache miss - looking up projectModel in projectSFID: %s", projectSFID)
+ projectModel, err := pmm.GetProject(projectSFID)
+ if err != nil {
+ log.WithFields(f).Warnf("unable to lookup projectModel in projectModel service by projectSFID, error: %+v", err)
+ return nil, err
+ }
+
+ // Update our cache for next time
+ projectServiceModels[projectSFID] = projectModel
+ log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+
+ // Do they have a parent?
+ if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
+ log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
+ return nil, nil
+ }
+
+ // Parent in the cache?
+ existingParentModel, exists = projectServiceModels[projectModel.Parent]
+ if exists {
+ return existingParentModel, nil
+ }
+
+ // Parent project not in the cache - lookup
+ parentProjectModel, err := pmm.GetProject(projectModel.Parent)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", projectModel.Parent)
+ return nil, err
+ }
+
+ // Update our cache for next time
+ projectServiceModels[existingModel.Parent] = parentProjectModel
+ log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+
+ return parentProjectModel, nil
+}
+
// IsTheLinuxFoundation returns true if the specified project SFID is the The Linux Foundation project
func (pmm *Client) IsTheLinuxFoundation(projectSFID string) (bool, error) {
f := logrus.Fields{
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 08507fc6a..fc57adb58 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -57,7 +57,8 @@ type ServiceInterface interface {
InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error
}
-type service struct {
+// Service structure/model
+type Service struct {
v1ProjectService project.Service
v1CompanyService company.IService
v1SignatureService signatures.SignatureService
@@ -72,8 +73,8 @@ type service struct {
func NewService(awsSession *session.Session, signaturesBucketName string, v1ProjectService project.Service,
v1CompanyService company.IService,
v1SignatureService signatures.SignatureService,
- pcgRepo projects_cla_groups.Repository, v1SignatureRepo signatures.SignatureRepository, usersService users.Service) *service {
- return &service{
+ pcgRepo projects_cla_groups.Repository, v1SignatureRepo signatures.SignatureRepository, usersService users.Service) *Service {
+ return &Service{
v1ProjectService: v1ProjectService,
v1CompanyService: v1CompanyService,
v1SignatureService: v1SignatureService,
@@ -85,7 +86,8 @@ func NewService(awsSession *session.Session, signaturesBucketName string, v1Proj
}
}
-func (s *service) GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error) {
+// GetProjectCompanySignatures return the signatures for the specified project and company information
+func (s *Service) GetProjectCompanySignatures(ctx context.Context, companyID, companySFID, projectSFID string) (*models.Signatures, error) {
pm, err := s.projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
if err != nil {
return nil, err
@@ -106,6 +108,7 @@ func (s *service) GetProjectCompanySignatures(ctx context.Context, companyID, co
return v2SignaturesReplaceCompanyID(resp, companyID, companySFID)
}
+// eclaSigCsvLine returns a single ECLA signature CSV line
func eclaSigCsvLine(sig *v1Models.CorporateContributor) string {
var dateTime string
t, err := utils.ParseDateTime(sig.Timestamp)
@@ -118,7 +121,8 @@ func eclaSigCsvLine(sig *v1Models.CorporateContributor) string {
return fmt.Sprintf("\n%s,%s,%s,%s,\"%s\"", sig.GithubID, sig.LinuxFoundationID, sig.Name, sig.Email, dateTime)
}
-func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error) {
+// GetClaGroupCorporateContributorsCsv returns the CLA Group corporate contributors as a CSV
+func (s *Service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGroupID string, companyID string) ([]byte, error) {
var b bytes.Buffer
result, err := s.v1SignatureService.GetClaGroupCorporateContributors(ctx, claGroupID, &companyID, nil)
if err != nil {
@@ -136,7 +140,8 @@ func (s service) GetClaGroupCorporateContributorsCsv(ctx context.Context, claGro
return b.Bytes(), nil
}
-func (s service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
+// GetProjectIclaSignaturesCsv returns the ICLA signatures as a CSV file for the specified CLA Group
+func (s *Service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
var b bytes.Buffer
result, err := s.v1SignatureService.GetClaGroupICLASignatures(ctx, claGroupID, nil, nil, nil, 0, "")
if err != nil {
@@ -149,7 +154,8 @@ func (s service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID str
return b.Bytes(), nil
}
-func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
+// GetProjectCclaSignaturesCsv returns the ICLA signatures as a CSV file for the specified CLA Group and search term filters
+func (s *Service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
f := logrus.Fields{
"functionName": "GetProjectCclaSignaturesCsv",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -173,7 +179,8 @@ func (s service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID str
return b.Bytes(), nil
}
-func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
+// GetProjectIclaSignatures returns the ICLA signatures for the specified CLA Group and search term filters
+func (s *Service) GetProjectIclaSignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
f := logrus.Fields{
"functionName": "v2.signatures.service.GetProjectIclaSignatures",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -199,7 +206,8 @@ func (s service) GetProjectIclaSignatures(ctx context.Context, claGroupID string
return &out, nil
}
-func (s service) GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error) {
+// GetSignedDocument returns the signed document for the specified signature ID
+func (s *Service) GetSignedDocument(ctx context.Context, signatureID string) (*models.SignedDocument, error) {
sig, err := s.v1SignatureService.GetSignature(ctx, signatureID)
if err != nil {
return nil, err
@@ -224,7 +232,8 @@ func (s service) GetSignedDocument(ctx context.Context, signatureID string) (*mo
}, nil
}
-func (s service) GetSignedCclaZipPdf(claGroupID string) (*models.URLObject, error) {
+// GetSignedCclaZipPdf returns the signed CCLA Zip PDF reference
+func (s *Service) GetSignedCclaZipPdf(claGroupID string) (*models.URLObject, error) {
url := utils.SignedClaGroupZipFilename(claGroupID, CCLA)
ok, err := s.IsZipPresentOnS3(url)
if err != nil {
@@ -242,7 +251,8 @@ func (s service) GetSignedCclaZipPdf(claGroupID string) (*models.URLObject, erro
}, nil
}
-func (s service) GetSignedIclaZipPdf(claGroupID string) (*models.URLObject, error) {
+// GetSignedIclaZipPdf returns the signed ICLA Zip PDF reference
+func (s *Service) GetSignedIclaZipPdf(claGroupID string) (*models.URLObject, error) {
url := utils.SignedClaGroupZipFilename(claGroupID, ICLA)
ok, err := s.IsZipPresentOnS3(url)
if err != nil {
@@ -260,7 +270,8 @@ func (s service) GetSignedIclaZipPdf(claGroupID string) (*models.URLObject, erro
}, nil
}
-func (s service) IsZipPresentOnS3(zipFilePath string) (bool, error) {
+// IsZipPresentOnS3 returns true if the specified file is present in S3
+func (s *Service) IsZipPresentOnS3(zipFilePath string) (bool, error) {
_, err := s.s3.GetObject(&s3.GetObjectInput{
Bucket: aws.String(s.signaturesBucket),
Key: aws.String(zipFilePath),
@@ -275,7 +286,8 @@ func (s service) IsZipPresentOnS3(zipFilePath string) (bool, error) {
return true, nil
}
-func (s service) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID string, searchTerm *string) (*models.CorporateContributorList, error) {
+// GetClaGroupCorporateContributors returns the list of corporate contributors for the specified CLA Group and company
+func (s *Service) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID string, searchTerm *string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
"functionName": "GetClaGroupCorporateContributors",
"claGroupID": claGroupID,
@@ -301,7 +313,8 @@ func (s service) GetClaGroupCorporateContributors(ctx context.Context, claGroupI
return &resp, nil
}
-func (s service) InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error {
+// InvalidateICLA invalidates the specified signature record using the supplied parameters
+func (s *Service) InvalidateICLA(ctx context.Context, claGroupID string, userID string, authUser *auth.User, eventsService events.Service, eventArgs *events.LogEventArgs) error {
f := logrus.Fields{
"functionName": "v2.signatures.service.InvalidateICLA",
"claGroupID": claGroupID,
From 693c73c9249d8081be72d7a6eebbfac95979dae0 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 25 Jun 2021 16:17:00 -0700
Subject: [PATCH 0336/1276] Updated golang CI/CD Version (#3015)
---
.circleci/config.yml | 4 +-
cla-backend-go/Makefile | 9 ++--
cla-backend-go/swagger/cla.v1.yaml | 14 ++++--
cla-backend-go/swagger/cla.v2.yaml | 79 +++++-------------------------
4 files changed, 29 insertions(+), 77 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index d70015f06..93a29ae55 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -198,7 +198,7 @@ jobs:
buildGoBackend: &buildGoBackendAnchor
docker:
- - image: circleci/golang:1.15.6
+ - image: circleci/golang:1.16.5
working_directory: /go/src/github.com/communitybridge/easycla/
steps:
- checkout
@@ -888,7 +888,7 @@ jobs:
functionalTestsGo: &functionalTestsGo
docker:
- - image: circleci/golang:1.15.6
+ - image: circleci/golang:1.16.5
steps:
- attach_workspace:
at: ~/
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 852fe6fe4..a9e8110d6 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -116,7 +116,8 @@ swagger-build-project-service:
swagger -q generate client \
--copyright-file=copyright-header.txt \
-t v2/project-service \
- -f swagger/project-service.yaml
+ -f swagger/project-service.yaml \
+ --skip-validation # needed, currently seeing: body.default.Filename in body must be of type string: "null", and definitions.artifact-upload-init-request.default.Filename in body must be of type string: "null" issues, notified PS team
swagger-build-organization-service:
@echo
@@ -127,7 +128,8 @@ swagger-build-organization-service:
swagger -q generate client \
--copyright-file=copyright-header.txt \
-t v2/organization-service \
- -f swagger/organization-service.yaml
+ -f swagger/organization-service.yaml \
+ --skip-validation # needed, currently seeing: - username in query must be of type string: "null"
swagger-build-user-service:
@echo
@@ -138,7 +140,8 @@ swagger-build-user-service:
swagger -q generate client \
--copyright-file=copyright-header.txt \
-t v2/user-service \
- -f swagger/user-service.yaml
+ -f swagger/user-service.yaml \
+ --skip-validation # needed, many validation errors
swagger-build-acs-service:
@echo
diff --git a/cla-backend-go/swagger/cla.v1.yaml b/cla-backend-go/swagger/cla.v1.yaml
index e9c8836e1..dd822172b 100644
--- a/cla-backend-go/swagger/cla.v1.yaml
+++ b/cla-backend-go/swagger/cla.v1.yaml
@@ -65,16 +65,14 @@ paths:
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/health'
- '503':
- description: ''
- schema:
- $ref: '#/definitions/health'
'400':
$ref: '#/responses/invalid-request'
'401':
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '503':
+ $ref: '#/responses/service-unavailable'
tags:
- health
@@ -3180,6 +3178,14 @@ responses:
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/error-response'
+ service-unavailable:
+ description: 'Service unavailable'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/error-response'
conflict:
description: The request could not be completed due to a conflict with the current state of the target resource.
headers:
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index dbf93745e..e370f0929 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -57,16 +57,14 @@ paths:
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/health'
- '503':
- description: 'Service unavailable'
- schema:
- $ref: '#/definitions/health'
'400':
$ref: '#/responses/invalid-request'
'401':
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '503':
+ $ref: '#/responses/service-unavailable'
tags:
- health
@@ -3708,6 +3706,14 @@ responses:
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/error-response'
+ service-unavailable:
+ description: Service unavailable
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/error-response'
conflict:
description: Duplicate Resource
headers:
@@ -3838,13 +3844,6 @@ parameters:
in: query
type: string
pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
- signingEntityName:
- name: signingEntityName
- description: The signing entity name of a Company (Salesforce and EasyCLA)
- in: query
- type: string
- required: true
- pattern: '[^<>]*' # allow everything except greater than and less than symbols
companyID:
name: companyID
description: The internal company ID representing signing entity name instance (EasyCLA)
@@ -3935,12 +3934,6 @@ parameters:
pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
minLength: 5
maxLength: 255
- companySFID:
- name: companySFID
- description: salesforce id of the company
- in: query
- type: string
- pattern: '^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$' # see: https://stackoverflow.com/questions/9742913/validating-a-salesforce-id
gerritHost:
name: gerritHost
description: host of the gerrit server
@@ -4112,9 +4105,6 @@ definitions:
template-pdfs:
$ref: './common/template-pdfs.yaml'
- github-organizations:
- $ref: './common/github-organizations.yaml'
-
github-organization:
$ref: './common/github-organization.yaml'
@@ -4375,7 +4365,7 @@ definitions:
Employees:
type: string
description: The number of employees of the company
- example: "500 - 4999, 10000+"
+ example: "500 - 4999"
pattern: '^([\w\d\s\-\,\.\/]+){2,}$'
signingEntityNames:
type: array
@@ -4527,17 +4517,6 @@ definitions:
companySFID:
type: string
- contributors:
- type: object
- title: contributors
- description: List of contributor roles for a user
- properties:
- list:
- type: array
- items:
- $ref:
- '#/definitions/contributor'
-
contributor:
type: object
title: Contributor
@@ -4569,42 +4548,6 @@ definitions:
example: 'contributor'
x-omitempty: false
- company-owner:
- type: object
- title: Company Owner
- description: Company Owner
- properties:
- lf_username:
- type: string
- description: 'the LF username'
- x-omitempty: false
- example: 'username'
- name:
- type: string
- description: 'name of the user'
- example: 'john'
- x-omitempty: false
- user_sfid:
- type: string
- description: 'the user SalesForce ID'
- x-omitempty: false
- email:
- $ref: './common/properties/email.yaml'
- x-omitempty: false
- type:
- type: string
- x-omitempty: false
- example: 'contact'
- assigned_on:
- type: string
- x-omitempty: false
- company_sfid:
- type: string
- description: 'the Organization SalesForce ID'
- x-omitempty: false
- example: 'abc134234adsdf43'
-
-
cla-manager-designee:
type: object
title: Company CLA Designee
From d21a918d47f9b24cfdf1c4d96ce4dd5f4035f976 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 25 Jun 2021 17:21:22 -0700
Subject: [PATCH 0337/1276] Resolved PCC-1158 - Event Log Created When Template
Updated (#3016)
---
cla-backend-go/events/event_data.go | 399 +++++++++++++------------
cla-backend-go/template/repository.go | 21 ++
cla-backend-go/template/service.go | 17 ++
cla-backend-go/v2/template/handlers.go | 19 +-
4 files changed, 264 insertions(+), 192 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index ec1be67d1..e0fefa7c1 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -1,6 +1,6 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT
-//nolint
+
package events
import (
@@ -10,105 +10,114 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
+const (
+ // noReason constant value
+ noReason = "No reason"
+)
+
// EventData returns event data string which is used for event logging and containsPII field
type EventData interface {
GetEventDetailsString(args *LogEventArgs) (eventData string, containsPII bool)
GetEventSummaryString(args *LogEventArgs) (eventData string, containsPII bool)
}
+// CLAGroupEnrolledProjectData event data model
type CLAGroupEnrolledProjectData struct {
}
+// CLAGroupUnenrolledProjectData event data model
type CLAGroupUnenrolledProjectData struct {
}
+// ProjectServiceCLAEnabledData event data model
type ProjectServiceCLAEnabledData struct {
}
+// ProjectServiceCLADisabledData event data model
type ProjectServiceCLADisabledData struct {
}
-// RepositoryAddedEventData . . .
+// RepositoryAddedEventData event data model
type RepositoryAddedEventData struct {
RepositoryName string
}
-// RepositoryDisabledEventData . . .
+// RepositoryDisabledEventData event data model
type RepositoryDisabledEventData struct {
RepositoryName string
}
-// RepositoryRenamedEventData . . .
+// RepositoryRenamedEventData event data model
type RepositoryRenamedEventData struct {
NewRepositoryName string
OldRepositoryName string
}
-// RepositoryTransferredEventData . . .
+// RepositoryTransferredEventData event data model
type RepositoryTransferredEventData struct {
RepositoryName string
OldGithubOrgName string
NewGithubOrgName string
}
-// RepositoryUpdatedEventData . . .
+// RepositoryUpdatedEventData event data model
type RepositoryUpdatedEventData struct {
RepositoryName string
}
-// RepositoryBranchProtectionAddedEventData . . .
+// RepositoryBranchProtectionAddedEventData event data model
type RepositoryBranchProtectionAddedEventData struct {
RepositoryName string
}
-// RepositoryBranchProtectionDisabledEventData . . .
+// RepositoryBranchProtectionDisabledEventData event data model
type RepositoryBranchProtectionDisabledEventData struct {
RepositoryName string
}
-// RepositoryBranchProtectionUpdatedEventData . . .
+// RepositoryBranchProtectionUpdatedEventData event data model
type RepositoryBranchProtectionUpdatedEventData struct {
RepositoryName string
}
-// GerritProjectDeletedEventData . . .
+// GerritProjectDeletedEventData event data model
type GerritProjectDeletedEventData struct {
DeletedCount int
}
-// GerritAddedEventData . . .
+// GerritAddedEventData data model
type GerritAddedEventData struct {
GerritRepositoryName string
}
-// GerritDeletedEventData . . .
+// GerritDeletedEventData data model
type GerritDeletedEventData struct {
GerritRepositoryName string
}
-// GerritUserAddedEventData . . .
+// GerritUserAddedEventData data model
type GerritUserAddedEventData struct {
Username string
GroupName string
}
-// GerritUserRemovedEventData . . .
+// GerritUserRemovedEventData data model
type GerritUserRemovedEventData struct {
Username string
GroupName string
}
-// GitHubProjectDeletedEventData . . .
+// GitHubProjectDeletedEventData data model
type GitHubProjectDeletedEventData struct {
DeletedCount int
}
-// SignatureProjectInvalidatedEventData . . .
+// SignatureProjectInvalidatedEventData data model
type SignatureProjectInvalidatedEventData struct {
InvalidatedCount int
}
-//SignatureInvalidatedApprovalRejectionEventData . . .
+//SignatureInvalidatedApprovalRejectionEventData data model
type SignatureInvalidatedApprovalRejectionEventData struct {
GHUsername string
Email string
@@ -117,47 +126,49 @@ type SignatureInvalidatedApprovalRejectionEventData struct {
CLAGroupID string
}
-// UserCreatedEventData . . .
+// UserCreatedEventData data model
type UserCreatedEventData struct{}
-// UserDeletedEventData . . .
+// UserDeletedEventData data model
type UserDeletedEventData struct {
DeletedUserID string
}
-// UserUpdatedEventData . . .
+// UserUpdatedEventData data model
type UserUpdatedEventData struct{}
-// CompanyACLRequestAddedEventData . . .
+// CompanyACLRequestAddedEventData data model
type CompanyACLRequestAddedEventData struct {
UserName string
UserID string
UserEmail string
}
-// CompanyACLRequestApprovedEventData . . .
+// CompanyACLRequestApprovedEventData data model
type CompanyACLRequestApprovedEventData struct {
UserName string
UserID string
UserEmail string
}
-// CompanyACLRequestDeniedEventData . . .
+// CompanyACLRequestDeniedEventData data model
type CompanyACLRequestDeniedEventData struct {
UserName string
UserID string
UserEmail string
}
-// CompanyACLUserAddedEventData . . .
+// CompanyACLUserAddedEventData data model
type CompanyACLUserAddedEventData struct {
UserLFID string
}
-// CLATemplateCreatedEventData . . .
-type CLATemplateCreatedEventData struct{}
+// CLATemplateCreatedEventData data model
+type CLATemplateCreatedEventData struct {
+ TemplateName string
+}
-// GitHubOrganizationAddedEventData . . .
+// GitHubOrganizationAddedEventData data model
type GitHubOrganizationAddedEventData struct {
GitHubOrganizationName string
AutoEnabled bool
@@ -165,34 +176,34 @@ type GitHubOrganizationAddedEventData struct {
BranchProtectionEnabled bool
}
-// GitHubOrganizationDeletedEventData . . .
+// GitHubOrganizationDeletedEventData data model
type GitHubOrganizationDeletedEventData struct {
GitHubOrganizationName string
}
-// GitHubOrganizationUpdatedEventData . . .
+// GitHubOrganizationUpdatedEventData data model
type GitHubOrganizationUpdatedEventData struct {
GitHubOrganizationName string
AutoEnabled bool
AutoEnabledClaGroupID string
}
-// CCLAApprovalListRequestCreatedEventData . . .
+// CCLAApprovalListRequestCreatedEventData data model
type CCLAApprovalListRequestCreatedEventData struct {
RequestID string
}
-// CCLAApprovalListRequestApprovedEventData . . .
+// CCLAApprovalListRequestApprovedEventData data model
type CCLAApprovalListRequestApprovedEventData struct {
RequestID string
}
-// CCLAApprovalListRequestRejectedEventData . . .
+// CCLAApprovalListRequestRejectedEventData data model
type CCLAApprovalListRequestRejectedEventData struct {
RequestID string
}
-// CLAManagerCreatedEventData . . .
+// CLAManagerCreatedEventData data model
type CLAManagerCreatedEventData struct {
CompanyName string
ProjectName string
@@ -201,7 +212,7 @@ type CLAManagerCreatedEventData struct {
UserLFID string
}
-// CLAManagerDeletedEventData . . .
+// CLAManagerDeletedEventData data model
type CLAManagerDeletedEventData struct {
CompanyName string
ProjectName string
@@ -210,7 +221,7 @@ type CLAManagerDeletedEventData struct {
UserLFID string
}
-// CLAManagerRequestCreatedEventData . . .
+// CLAManagerRequestCreatedEventData data model
type CLAManagerRequestCreatedEventData struct {
RequestID string
CompanyName string
@@ -220,7 +231,7 @@ type CLAManagerRequestCreatedEventData struct {
UserLFID string
}
-// CLAManagerRequestApprovedEventData . . .
+// CLAManagerRequestApprovedEventData data model
type CLAManagerRequestApprovedEventData struct {
RequestID string
CompanyName string
@@ -231,7 +242,7 @@ type CLAManagerRequestApprovedEventData struct {
ManagerEmail string
}
-// CLAManagerRequestDeniedEventData . . .
+// CLAManagerRequestDeniedEventData data model
type CLAManagerRequestDeniedEventData struct {
RequestID string
CompanyName string
@@ -242,7 +253,7 @@ type CLAManagerRequestDeniedEventData struct {
ManagerEmail string
}
-// CLAManagerRequestDeletedEventData . . .
+// CLAManagerRequestDeletedEventData data model
type CLAManagerRequestDeletedEventData struct {
RequestID string
CompanyName string
@@ -253,71 +264,71 @@ type CLAManagerRequestDeletedEventData struct {
ManagerEmail string
}
-// CLAApprovalListAddEmailData . . .
+// CLAApprovalListAddEmailData data model
type CLAApprovalListAddEmailData struct {
ApprovalListEmail string
}
-// CLAApprovalListRemoveEmailData . . .
+// CLAApprovalListRemoveEmailData data model
type CLAApprovalListRemoveEmailData struct {
ApprovalListEmail string
}
-// CLAApprovalListAddDomainData . . .
+// CLAApprovalListAddDomainData data model
type CLAApprovalListAddDomainData struct {
ApprovalListDomain string
}
-// CLAApprovalListRemoveDomainData . . .
+// CLAApprovalListRemoveDomainData data model
type CLAApprovalListRemoveDomainData struct {
ApprovalListDomain string
}
-// CLAApprovalListAddGitHubUsernameData . . .
+// CLAApprovalListAddGitHubUsernameData data model
type CLAApprovalListAddGitHubUsernameData struct {
ApprovalListGitHubUsername string
}
-// CLAApprovalListRemoveGitHubUsernameData . . .
+// CLAApprovalListRemoveGitHubUsernameData data model
type CLAApprovalListRemoveGitHubUsernameData struct {
ApprovalListGitHubUsername string
}
-// CLAApprovalListAddGitHubOrgData . . .
+// CLAApprovalListAddGitHubOrgData data model
type CLAApprovalListAddGitHubOrgData struct {
ApprovalListGitHubOrg string
}
-// CLAApprovalListRemoveGitHubOrgData . . .
+// CLAApprovalListRemoveGitHubOrgData data model
type CLAApprovalListRemoveGitHubOrgData struct {
ApprovalListGitHubOrg string
}
-// ApprovalListGitHubOrganizationAddedEventData . . .
+// ApprovalListGitHubOrganizationAddedEventData data model
type ApprovalListGitHubOrganizationAddedEventData struct {
GitHubOrganizationName string
}
-// ApprovalListGitHubOrganizationDeletedEventData . . .
+// ApprovalListGitHubOrganizationDeletedEventData data model
type ApprovalListGitHubOrganizationDeletedEventData struct {
GitHubOrganizationName string
}
-// ClaManagerAccessRequestAddedEventData . . .
+// ClaManagerAccessRequestAddedEventData data model
type ClaManagerAccessRequestAddedEventData struct {
ProjectName string
CompanyName string
}
-// ClaManagerAccessRequestDeletedEventData . . .
+// ClaManagerAccessRequestDeletedEventData data model
type ClaManagerAccessRequestDeletedEventData struct {
RequestID string
}
-// CLAGroupCreatedEventData . . .
+// CLAGroupCreatedEventData data model
type CLAGroupCreatedEventData struct{}
-// CLAGroupUpdatedEventData . . .
+// CLAGroupUpdatedEventData data model
type CLAGroupUpdatedEventData struct {
NewClaGroupName string
NewClaGroupDescription string
@@ -325,34 +336,34 @@ type CLAGroupUpdatedEventData struct {
OldClaGroupDescription string
}
-// CLAGroupDeletedEventData . . .
+// CLAGroupDeletedEventData data model
type CLAGroupDeletedEventData struct{}
-// ContributorNotifyCompanyAdminData . . .
+// ContributorNotifyCompanyAdminData data model
type ContributorNotifyCompanyAdminData struct {
AdminName string
AdminEmail string
}
-// ContributorNotifyCLADesignee . . .
+// ContributorNotifyCLADesignee data model
type ContributorNotifyCLADesignee struct {
DesigneeName string
DesigneeEmail string
}
-// ContributorAssignCLADesignee . . .
+// ContributorAssignCLADesignee data model
type ContributorAssignCLADesignee struct {
DesigneeName string
DesigneeEmail string
}
-// UserConvertToContactData . . .
+// UserConvertToContactData data model
type UserConvertToContactData struct {
UserName string
UserEmail string
}
-// AssignRoleScopeData . . .
+// AssignRoleScopeData data model
type AssignRoleScopeData struct {
Role string
Scope string
@@ -360,7 +371,7 @@ type AssignRoleScopeData struct {
UserEmail string
}
-// ClaManagerRoleCreatedData . . .
+// ClaManagerRoleCreatedData data model
type ClaManagerRoleCreatedData struct {
Role string
Scope string
@@ -368,7 +379,7 @@ type ClaManagerRoleCreatedData struct {
UserEmail string
}
-// ClaManagerRoleDeletedData . . .
+// ClaManagerRoleDeletedData data model
type ClaManagerRoleDeletedData struct {
Role string
Scope string
@@ -376,7 +387,7 @@ type ClaManagerRoleDeletedData struct {
UserEmail string
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupEnrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The project %s (%s) was enrolled into the CLA Group %s (%s)", args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID)
if args.UserName != "" {
@@ -386,7 +397,7 @@ func (ed *CLAGroupEnrolledProjectData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupUnenrolledProjectData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The project %s (%s) was unenrolled from the CLA Group %s (%s)", args.ProjectName, args.ProjectID, args.CLAGroupName, args.CLAGroupID)
if args.UserName != "" {
@@ -396,7 +407,7 @@ func (ed *CLAGroupUnenrolledProjectData) GetEventDetailsString(args *LogEventArg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ProjectServiceCLAEnabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Service for the project %s (%s) was enabled", args.ProjectName, args.ProjectID)
if args.UserName != "" {
@@ -406,7 +417,7 @@ func (ed *ProjectServiceCLAEnabledData) GetEventDetailsString(args *LogEventArgs
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ProjectServiceCLADisabledData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Service for the project %s (%s) was disabled", args.ProjectName, args.ProjectID)
if args.UserName != "" {
@@ -416,7 +427,7 @@ func (ed *ProjectServiceCLADisabledData) GetEventDetailsString(args *LogEventArg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository: %s was added for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -426,7 +437,7 @@ func (ed *RepositoryAddedEventData) GetEventDetailsString(args *LogEventArgs) (s
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -436,7 +447,7 @@ func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryRenamedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository renamed from %s to %s for the project %s", ed.OldRepositoryName, ed.NewRepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -446,7 +457,7 @@ func (ed *RepositoryRenamedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryTransferredEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository : %s transferred from %s to %s Github Organization for the project %s", ed.RepositoryName, ed.OldGithubOrgName, ed.NewGithubOrgName, args.ProjectName)
if args.UserName != "" {
@@ -456,7 +467,7 @@ func (ed *RepositoryTransferredEventData) GetEventDetailsString(args *LogEventAr
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was updated for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -466,7 +477,7 @@ func (ed *RepositoryUpdatedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryBranchProtectionAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was added for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -476,7 +487,7 @@ func (ed *RepositoryBranchProtectionAddedEventData) GetEventDetailsString(args *
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was disabled for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -486,7 +497,7 @@ func (ed *RepositoryBranchProtectionDisabledEventData) GetEventDetailsString(arg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was updated for the project %s", ed.RepositoryName, args.ProjectName)
if args.UserName != "" {
@@ -496,7 +507,7 @@ func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventDetailsString(args
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *UserCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User was added : %+v", args.UserModel)
if args.UserName != "" {
@@ -506,7 +517,7 @@ func (ed *UserCreatedEventData) GetEventDetailsString(args *LogEventArgs) (strin
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *UserUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User details updated: %+v", *args.UserModel)
if args.UserName != "" {
@@ -516,7 +527,7 @@ func (ed *UserUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (strin
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *UserDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User ID: %s was deleted", ed.DeletedUserID)
if args.UserName != "" {
@@ -526,7 +537,7 @@ func (ed *UserDeletedEventData) GetEventDetailsString(args *LogEventArgs) (strin
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CompanyACLRequestAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s added pending invite with ID: %s and Email: %s for Company: %s",
ed.UserName, ed.UserID, ed.UserEmail, args.CompanyName)
@@ -537,7 +548,7 @@ func (ed *CompanyACLRequestAddedEventData) GetEventDetailsString(args *LogEventA
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CompanyACLRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Access Aproved for User: %s, ID: %s, Email: %s Company Group: %s",
ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
@@ -548,7 +559,7 @@ func (ed *CompanyACLRequestApprovedEventData) GetEventDetailsString(args *LogEve
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CompanyACLRequestDeniedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Access Denied for User: %s, ID: %s, Email: %s Company Group: %s.",
ed.UserName, args.CompanyName, ed.UserID, ed.UserEmail)
@@ -559,7 +570,7 @@ func (ed *CompanyACLRequestDeniedEventData) GetEventDetailsString(args *LogEvent
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User with LF Username: %s added to the ACL for Company: %s",
args.LFUser.Name, args.CompanyName)
@@ -570,9 +581,18 @@ func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLATemplateCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("PDF Templates created for Project: %s", args.ProjectName)
+ data := "A CLA Group template was created or updated" // nolint
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if ed.TemplateName != "" {
+ data = data + fmt.Sprintf(" using template: %s", ed.TemplateName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
@@ -580,7 +600,7 @@ func (ed *CLATemplateCreatedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization: %s was added with auto-enabled: %t, with branch protection enabled: %t",
ed.GitHubOrganizationName, ed.AutoEnabled, ed.BranchProtectionEnabled)
@@ -594,7 +614,7 @@ func (ed *GitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEvent
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization: %s was deleted ", ed.GitHubOrganizationName)
if args.UserName != "" {
@@ -604,7 +624,7 @@ func (ed *GitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEve
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization:%s was updated with auto-enabled: %t",
ed.GitHubOrganizationName, ed.AutoEnabled)
@@ -618,21 +638,21 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEve
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s approved a CCLA Approval Request for Project: %s and Company: %s with Request ID: %s.",
args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CCLAApprovalListRequestRejectedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s rejected a CCLA Approval Request for Project: %s, Company: %s with Request ID: %s.",
args.UserName, args.ProjectName, args.CompanyName, ed.RequestID)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s, LFID: %s, Email: %s added CLA Manager Request: %s for Company: %s, Project: %s.",
ed.UserName, ed.UserLFID, ed.UserEmail, ed.RequestID, ed.CompanyName, ed.ProjectName)
@@ -643,7 +663,7 @@ func (ed *CLAManagerRequestCreatedEventData) GetEventDetailsString(args *LogEven
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user: %s LFID: %s, email: %s was added as CLA Manager", ed.UserName, ed.UserLFID, ed.UserEmail)
if args.CLAGroupName != "" {
@@ -665,7 +685,7 @@ func (ed *CLAManagerCreatedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user: %s LFID: %s, email: %s was removed as CLA Manager", ed.UserName, ed.UserLFID, ed.UserEmail)
if args.CLAGroupName != "" {
@@ -687,7 +707,7 @@ func (ed *CLAManagerDeletedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager Request: %s was approved for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
@@ -698,7 +718,7 @@ func (ed *CLAManagerRequestApprovedEventData) GetEventDetailsString(args *LogEve
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerRequestDeniedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager Request: %s was denied for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
@@ -709,7 +729,7 @@ func (ed *CLAManagerRequestDeniedEventData) GetEventDetailsString(args *LogEvent
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAManagerRequestDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Manager Request: %s was deleted for User %s, Email: %s by Manager: %s, Email: %s for Company: %s, Project: %s",
ed.RequestID, ed.UserName, ed.UserEmail, ed.ManagerName, ed.ManagerEmail, ed.CompanyName, ed.ProjectName)
@@ -720,7 +740,7 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventDetailsString(args *LogEven
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address %s was added to the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
@@ -742,7 +762,7 @@ func (ed *CLAApprovalListAddEmailData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address %s was removed from the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
@@ -764,7 +784,7 @@ func (ed *CLAApprovalListRemoveEmailData) GetEventDetailsString(args *LogEventAr
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address domain %s was added to the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
@@ -786,7 +806,7 @@ func (ed *CLAApprovalListAddDomainData) GetEventDetailsString(args *LogEventArgs
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address domain %s was removed from the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
@@ -808,7 +828,7 @@ func (ed *CLAApprovalListRemoveDomainData) GetEventDetailsString(args *LogEventA
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub username %s was added to the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
@@ -830,7 +850,7 @@ func (ed *CLAApprovalListAddGitHubUsernameData) GetEventDetailsString(args *LogE
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub username %s was removed from the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
@@ -852,7 +872,7 @@ func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventDetailsString(args *L
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was added to the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
@@ -874,7 +894,7 @@ func (ed *CLAApprovalListAddGitHubOrgData) GetEventDetailsString(args *LogEventA
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was removed from the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
@@ -896,7 +916,7 @@ func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEve
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CCLA Approval Request was created for the Project: %s, Company: %s with Request ID: %s",
args.ProjectName, args.CompanyName, ed.RequestID)
@@ -907,7 +927,7 @@ func (ed *CCLAApprovalListRequestCreatedEventData) GetEventDetailsString(args *L
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub Organization: %s was added to the approval list for the Company %s, Project: %s",
ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
@@ -918,7 +938,7 @@ func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventDetailsString(ar
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub Organization: %s was removed from the approval list for the Company: %s, Project: %s",
ed.GitHubOrganizationName, args.CompanyName, args.ProjectName)
@@ -929,21 +949,21 @@ func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventDetailsString(
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ClaManagerAccessRequestAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s has requested to be CLA Manager for Company %s, Project: %s.",
args.UserName, ed.CompanyName, ed.ProjectName)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ClaManagerAccessRequestDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s has deleted CLA Manager Request with ID: %s.",
args.UserName, ed.RequestID)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Group ID: %s, Name: %s was created", args.ProjectID, args.ProjectName)
if args.UserName != "" {
@@ -953,7 +973,7 @@ func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (s
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
var nameUpdated bool
@@ -978,7 +998,7 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("CLA Group ID: %s was deleted", args.ProjectID)
if args.UserName != "" {
@@ -988,7 +1008,7 @@ func (ed *CLAGroupDeletedEventData) GetEventDetailsString(args *LogEventArgs) (s
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GerritProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Gerrit Repositories were deleted due to CLA Group/Project: %s deletion",
ed.DeletedCount, args.ProjectName)
@@ -999,7 +1019,7 @@ func (ed *GerritProjectDeletedEventData) GetEventDetailsString(args *LogEventArg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GerritAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Gerrit Repository: %s was added", ed.GerritRepositoryName)
if args.UserName != "" {
@@ -1009,7 +1029,7 @@ func (ed *GerritAddedEventData) GetEventDetailsString(args *LogEventArgs) (strin
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GerritDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("Gerrit Repository: %s was deleted", ed.GerritRepositoryName)
if args.UserName != "" {
@@ -1019,7 +1039,7 @@ func (ed *GerritDeletedEventData) GetEventDetailsString(args *LogEventArgs) (str
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GerritUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The username %s was add to the gerrit group %s", ed.Username, ed.GroupName)
if args.UserName != "" {
@@ -1029,7 +1049,7 @@ func (ed *GerritUserAddedEventData) GetEventDetailsString(args *LogEventArgs) (s
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GerritUserRemovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The username %s was removed from the gerrit group %s", ed.Username, ed.GroupName)
if args.UserName != "" {
@@ -1039,7 +1059,7 @@ func (ed *GerritUserRemovedEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *GitHubProjectDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub Repositories were deleted due to CLA Group/Project: [%s] deletion",
ed.DeletedCount, args.ProjectName)
@@ -1050,7 +1070,7 @@ func (ed *GitHubProjectDeletedEventData) GetEventDetailsString(args *LogEventArg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *SignatureProjectInvalidatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Signatures were invalidated (approved set to false) due to CLA Group/Project: %s deletion",
ed.InvalidatedCount, args.ProjectName)
@@ -1061,9 +1081,9 @@ func (ed *SignatureProjectInvalidatedEventData) GetEventDetailsString(args *LogE
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- reason := "No reason"
+ reason := noReason
if ed.Email != "" {
reason = fmt.Sprintf("GH Username: %s approval removal ", ed.GHUsername)
} else if ed.GHUsername != "" {
@@ -1077,14 +1097,14 @@ func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventDetailsString(
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ContributorNotifyCompanyAdminData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s notified Company Admin: %s by Email: %s for Company ID: %s, Name: %s.",
args.UserName, ed.AdminName, ed.AdminEmail, args.CompanyName, args.CompanyID)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ContributorNotifyCLADesignee) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s notified CLA Designee: %s by Email: %s for Project Name : %s, ID: %s and Company Name: %s, ID: %s.",
args.UserName, ed.DesigneeName, ed.DesigneeEmail,
@@ -1093,7 +1113,7 @@ func (ed *ContributorNotifyCLADesignee) GetEventDetailsString(args *LogEventArgs
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ContributorAssignCLADesignee) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User Name: %s, Email: %s was assigned as CLA Manager Designee for project Name: %s, ID: %s and Company Name: %s, ID: %s",
ed.DesigneeName, ed.DesigneeEmail,
@@ -1106,14 +1126,14 @@ func (ed *ContributorAssignCLADesignee) GetEventDetailsString(args *LogEventArgs
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *UserConvertToContactData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s was converted to Contact state for Project: %s with ID: %s.",
args.UserName, args.ProjectName, args.ProjectSFID)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
@@ -1135,13 +1155,13 @@ func (ed *AssignRoleScopeData) GetEventDetailsString(args *LogEventArgs) (string
return data, true
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ClaManagerRoleCreatedData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s, Email: %s was added to Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.UserName)
return data, false
}
-// GetEventDetailsString . . .
+// GetEventDetailsString returns the details string for this event
func (ed *ClaManagerRoleDeletedData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s, Email: %s was removed from Role: %s with Scope: %s by: %s.", ed.UserName, ed.UserEmail, ed.Role, ed.Scope, args.UserName)
return data, false
@@ -1149,7 +1169,7 @@ func (ed *ClaManagerRoleDeletedData) GetEventDetailsString(args *LogEventArgs) (
// Event Summary started
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupEnrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The project %s was enrolled into the CLA Group %s", args.ProjectName, args.CLAGroupName)
if args.UserName != "" {
@@ -1159,7 +1179,7 @@ func (ed *CLAGroupEnrolledProjectData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupUnenrolledProjectData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The project %s was unenrolled from the CLA Group %s", args.ProjectName, args.CLAGroupName)
if args.UserName != "" {
@@ -1169,7 +1189,7 @@ func (ed *CLAGroupUnenrolledProjectData) GetEventSummaryString(args *LogEventArg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := "CLA Service was enabled"
if args.CLAGroupName != "" {
@@ -1188,7 +1208,7 @@ func (ed *ProjectServiceCLAEnabledData) GetEventSummaryString(args *LogEventArgs
return data, true
}
-// GetEventSummaryString function for ProjectServiceCLADisabledData
+// GetEventSummaryString returns the summary string for this event
func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := "CLA Service was disabled"
if args.CLAGroupName != "" {
@@ -1207,7 +1227,7 @@ func (ed *ProjectServiceCLADisabledData) GetEventSummaryString(args *LogEventArg
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was added", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1226,7 +1246,7 @@ func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (s
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was deleted", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1245,7 +1265,7 @@ func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryRenamedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository was renamed from %s to %s", ed.OldRepositoryName, ed.NewRepositoryName)
if args.CLAGroupName != "" {
@@ -1264,7 +1284,7 @@ func (ed *RepositoryRenamedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryTransferredEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository : %s was transferred from %s to %s Github Organization", ed.RepositoryName, ed.OldGithubOrgName, ed.NewGithubOrgName)
if args.CLAGroupName != "" {
@@ -1283,7 +1303,7 @@ func (ed *RepositoryTransferredEventData) GetEventSummaryString(args *LogEventAr
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository %s was updated", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1302,7 +1322,7 @@ func (ed *RepositoryUpdatedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the details string for this event
func (ed *RepositoryBranchProtectionAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was added", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1321,7 +1341,7 @@ func (ed *RepositoryBranchProtectionAddedEventData) GetEventSummaryString(args *
return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the details string for this event
func (ed *RepositoryBranchProtectionDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was disabled", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1340,7 +1360,7 @@ func (ed *RepositoryBranchProtectionDisabledEventData) GetEventSummaryString(arg
return data, true
}
-// GetEventDetailsString . . .
+// GetEventSummaryString returns the details string for this event
func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository branch protection %s was updated", ed.RepositoryName)
if args.CLAGroupName != "" {
@@ -1359,24 +1379,24 @@ func (ed *RepositoryBranchProtectionUpdatedEventData) GetEventSummaryString(args
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *UserCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was added with the user details: %+v.", args.UserName, args.UserModel)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *UserUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
return fmt.Sprintf("The user %s was updated with the user details: %+v.", args.UserName, *args.UserModel), true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *UserDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user ID %s was deleted by the user %s.", ed.DeletedUserID, args.UserName)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CompanyACLRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s with ID %s and with the email %s requested a company invitation",
ed.UserName, ed.UserID, ed.UserEmail)
@@ -1393,7 +1413,7 @@ func (ed *CompanyACLRequestAddedEventData) GetEventSummaryString(args *LogEventA
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CompanyACLRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("A company invite was approved for the user %s with the ID of %s with the email %s",
ed.UserName, ed.UserID, ed.UserEmail)
@@ -1413,7 +1433,7 @@ func (ed *CompanyACLRequestApprovedEventData) GetEventSummaryString(args *LogEve
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("A company invite was denied for the user %s with the ID of %s with the email %s",
ed.UserName, ed.UserID, ed.UserEmail)
@@ -1433,19 +1453,22 @@ func (ed *CompanyACLRequestDeniedEventData) GetEventSummaryString(args *LogEvent
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CompanyACLUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user with LF username %s was added to the access list for the company %s by the user %s.",
args.LFUser.Name, args.CompanyName, args.UserName)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLATemplateCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := "The PDF templates were created"
+ data := "A CLA Group template was created or updated" // nolint
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
+ if ed.TemplateName != "" {
+ data = data + fmt.Sprintf(" using template: %s", ed.TemplateName)
+ }
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
@@ -1456,7 +1479,7 @@ func (ed *CLATemplateCreatedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was added with auto-enabled set to %t with branch protection enabled set to %t",
ed.GitHubOrganizationName, ed.AutoEnabled, ed.BranchProtectionEnabled)
@@ -1476,7 +1499,7 @@ func (ed *GitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEvent
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was deleted", ed.GitHubOrganizationName)
if args.CLAGroupName != "" {
@@ -1492,7 +1515,7 @@ func (ed *GitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEve
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("GitHub Organization: %s was updated with auto-enabled: %t",
ed.GitHubOrganizationName, ed.AutoEnabled)
@@ -1512,7 +1535,7 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEve
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s approved a CCLA approval request", args.UserName)
if args.CLAGroupName != "" {
@@ -1528,7 +1551,7 @@ func (ed *CCLAApprovalListRequestApprovedEventData) GetEventSummaryString(args *
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CCLAApprovalListRequestRejectedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s rejected a CCLA approval request", args.UserName)
if args.CLAGroupName != "" {
@@ -1544,7 +1567,7 @@ func (ed *CCLAApprovalListRequestRejectedEventData) GetEventSummaryString(args *
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s added a CLA Manager request", args.UserName)
if args.CLAGroupName != "" {
@@ -1560,7 +1583,7 @@ func (ed *CLAManagerRequestCreatedEventData) GetEventSummaryString(args *LogEven
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was added as CLA Manager", ed.UserName)
if args.CLAGroupName != "" {
@@ -1579,7 +1602,7 @@ func (ed *CLAManagerCreatedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was removed as CLA Manager", ed.UserName)
if args.CLAGroupName != "" {
@@ -1598,7 +1621,7 @@ func (ed *CLAManagerDeletedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Manager request for the user %s was approved by the CLA Manager %s",
ed.UserName, ed.ManagerName)
@@ -1618,7 +1641,7 @@ func (ed *CLAManagerRequestApprovedEventData) GetEventSummaryString(args *LogEve
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerRequestDeniedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Manager request for the user %s was denied by the CLA Manager %s",
ed.UserName, ed.ManagerName)
@@ -1635,7 +1658,7 @@ func (ed *CLAManagerRequestDeniedEventData) GetEventSummaryString(args *LogEvent
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Manager request for the user %s was deleted by the CLA Manager %s",
ed.UserName, ed.ManagerName)
@@ -1652,7 +1675,7 @@ func (ed *CLAManagerRequestDeletedEventData) GetEventSummaryString(args *LogEven
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address %s was added to the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
@@ -1671,7 +1694,7 @@ func (ed *CLAApprovalListAddEmailData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address %s was removed from the approval list", ed.ApprovalListEmail)
if args.CLAGroupName != "" {
@@ -1690,7 +1713,7 @@ func (ed *CLAApprovalListRemoveEmailData) GetEventSummaryString(args *LogEventAr
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address domain %s was added to the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
@@ -1709,7 +1732,7 @@ func (ed *CLAApprovalListAddDomainData) GetEventSummaryString(args *LogEventArgs
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The email address domain %s was removed from the approval list", ed.ApprovalListDomain)
if args.CLAGroupName != "" {
@@ -1728,7 +1751,7 @@ func (ed *CLAApprovalListRemoveDomainData) GetEventSummaryString(args *LogEventA
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub username %s was added to the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
@@ -1747,7 +1770,7 @@ func (ed *CLAApprovalListAddGitHubUsernameData) GetEventSummaryString(args *LogE
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub username %s was removed from the approval list", ed.ApprovalListGitHubUsername)
if args.CLAGroupName != "" {
@@ -1766,7 +1789,7 @@ func (ed *CLAApprovalListRemoveGitHubUsernameData) GetEventSummaryString(args *L
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was added to the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
@@ -1785,7 +1808,7 @@ func (ed *CLAApprovalListAddGitHubOrgData) GetEventSummaryString(args *LogEventA
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub organization %s was removed from the approval list", ed.ApprovalListGitHubOrg)
if args.CLAGroupName != "" {
@@ -1804,7 +1827,7 @@ func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEve
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s created a CCLA Approval Request", args.UserName)
if args.CLAGroupName != "" {
@@ -1820,7 +1843,7 @@ func (ed *CCLAApprovalListRequestCreatedEventData) GetEventSummaryString(args *L
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Manager %s added the GitHub organization %s to the approval list", args.UserName, ed.GitHubOrganizationName)
if args.CLAGroupName != "" {
@@ -1836,7 +1859,7 @@ func (ed *ApprovalListGitHubOrganizationAddedEventData) GetEventSummaryString(ar
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Manager %s removed the GitHub organization %s from the approval list", args.UserName, ed.GitHubOrganizationName)
if args.CLAGroupName != "" {
@@ -1852,7 +1875,7 @@ func (ed *ApprovalListGitHubOrganizationDeletedEventData) GetEventSummaryString(
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ClaManagerAccessRequestAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s has requested to be CLA Manager", args.UserName)
if args.CLAGroupName != "" {
@@ -1868,7 +1891,7 @@ func (ed *ClaManagerAccessRequestAddedEventData) GetEventSummaryString(args *Log
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ClaManagerAccessRequestDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s has deleted a request to be CLA Manager", args.UserName)
if args.CLAGroupName != "" {
@@ -1884,13 +1907,13 @@ func (ed *ClaManagerAccessRequestDeletedEventData) GetEventSummaryString(args *L
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Group %s was created by the user %s.", args.ProjectName, args.UserName)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
var nameUpdated, descriptionUpdated bool
@@ -1918,7 +1941,7 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CLA Group %s was deleted", args.ProjectName)
if args.UserName != "" {
@@ -1928,7 +1951,7 @@ func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (s
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GerritProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d Gerrit repositories were deleted due to CLA Group/Project deletion", ed.DeletedCount)
if args.CLAGroupName != "" {
@@ -1944,7 +1967,7 @@ func (ed *GerritProjectDeletedEventData) GetEventSummaryString(args *LogEventArg
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GerritAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The Gerrit repository %s was added", ed.GerritRepositoryName)
if args.CLAGroupName != "" {
@@ -1960,7 +1983,7 @@ func (ed *GerritAddedEventData) GetEventSummaryString(args *LogEventArgs) (strin
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The Gerrit repository %s was deleted", ed.GerritRepositoryName)
if args.CLAGroupName != "" {
@@ -1976,7 +1999,7 @@ func (ed *GerritDeletedEventData) GetEventSummaryString(args *LogEventArgs) (str
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GerritUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The username %s was add to the gerrit group %s", ed.Username, ed.GroupName)
if args.CLAGroupName != "" {
@@ -1992,7 +2015,7 @@ func (ed *GerritUserAddedEventData) GetEventSummaryString(args *LogEventArgs) (s
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GerritUserRemovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The username %s was removed from the gerrit group %s", ed.Username, ed.GroupName)
if args.CLAGroupName != "" {
@@ -2008,7 +2031,7 @@ func (ed *GerritUserRemovedEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d GitHub repositories were deleted due to CLA Group/project deletion",
ed.DeletedCount)
@@ -2025,7 +2048,7 @@ func (ed *GitHubProjectDeletedEventData) GetEventSummaryString(args *LogEventArg
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("%d signatures were invalidated (approved set to false) due to CLA Group/Project %s deletion",
ed.InvalidatedCount, args.ProjectName)
@@ -2045,9 +2068,9 @@ func (ed *SignatureProjectInvalidatedEventData) GetEventSummaryString(args *LogE
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- reason := "No reason"
+ reason := noReason
if ed.Email != "" {
reason = fmt.Sprintf("Email: %s approval removal ", ed.Email)
} else if ed.GHUsername != "" {
@@ -2070,7 +2093,7 @@ func (ed *SignatureInvalidatedApprovalRejectionEventData) GetEventSummaryString(
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ContributorNotifyCompanyAdminData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s notified the company admin %s by the email address %s",
args.UserName, ed.AdminName, ed.AdminEmail)
@@ -2087,7 +2110,7 @@ func (ed *ContributorNotifyCompanyAdminData) GetEventSummaryString(args *LogEven
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ContributorNotifyCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s notified the CLA Designee %s by email %s", args.UserName, ed.DesigneeName, ed.DesigneeEmail)
if args.CLAGroupName != "" {
@@ -2103,7 +2126,7 @@ func (ed *ContributorNotifyCLADesignee) GetEventSummaryString(args *LogEventArgs
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s was assigned as CLA Manager Designee", ed.DesigneeName)
if args.CLAGroupName != "" {
@@ -2122,7 +2145,7 @@ func (ed *ContributorAssignCLADesignee) GetEventSummaryString(args *LogEventArgs
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user '%s' with email '%s' was converted to a contact", ed.UserName, ed.UserEmail)
if args.CLAGroupName != "" {
@@ -2141,7 +2164,7 @@ func (ed *UserConvertToContactData) GetEventSummaryString(args *LogEventArgs) (s
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
@@ -2160,7 +2183,7 @@ func (ed *AssignRoleScopeData) GetEventSummaryString(args *LogEventArgs) (string
return data, true
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
@@ -2179,7 +2202,7 @@ func (ed *ClaManagerRoleCreatedData) GetEventSummaryString(args *LogEventArgs) (
return data, false
}
-// GetEventSummaryString . . .
+// GetEventSummaryString returns the summary string for this event
func (ed *ClaManagerRoleDeletedData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user '%s' with email '%s' was added to the role %s", ed.UserName, ed.UserEmail, ed.Role)
if args.CLAGroupName != "" {
diff --git a/cla-backend-go/template/repository.go b/cla-backend-go/template/repository.go
index 99267674f..76c20eaf0 100644
--- a/cla-backend-go/template/repository.go
+++ b/cla-backend-go/template/repository.go
@@ -38,6 +38,7 @@ var (
// RepositoryInterface interface functions
type RepositoryInterface interface {
GetTemplates(ctx context.Context) ([]models.Template, error)
+ GetTemplateName(ctx context.Context, templateID string) (string, error)
GetTemplate(templateID string) (models.Template, error)
CLAGroupTemplateExists(ctx context.Context, templateID string) bool
GetCLAGroup(claGroupID string) (*models.ClaGroup, error)
@@ -129,6 +130,26 @@ func (r Repository) GetTemplates(ctx context.Context) ([]models.Template, error)
return templates, nil
}
+// GetTemplateName returns the template name when provided the template ID
+func (r Repository) GetTemplateName(ctx context.Context, templateID string) (string, error) {
+ f := logrus.Fields{
+ "functionName": "v1.template.repository.GetTemplateName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "templateID": templateID,
+ }
+
+ // For each template...
+ for _, template := range templateMap {
+ // If we have a match
+ if template.ID == templateID {
+ return template.Name, nil
+ }
+ }
+
+ log.WithFields(f).Warnf("unable to locate template with ID: %s", templateID)
+ return "", nil
+}
+
// GetTemplate returns the template based on the template ID
func (r Repository) GetTemplate(templateID string) (models.Template, error) {
template, ok := templateMap[templateID]
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index 4782a5d4e..db79189ca 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -36,6 +36,7 @@ const (
// ServiceInterface interface
type ServiceInterface interface {
GetTemplates(ctx context.Context) ([]models.Template, error)
+ GetTemplateName(ctx context.Context, templateID string) (string, error)
CreateCLAGroupTemplate(ctx context.Context, claGroupID string, claGroupFields *models.CreateClaGroupTemplate) (models.TemplatePdfs, error)
CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error)
GetCLATemplatePreview(ctx context.Context, claGroupID, claType string, watermark bool) ([]byte, error)
@@ -83,6 +84,22 @@ func (s Service) GetTemplates(ctx context.Context) ([]models.Template, error) {
return templates, nil
}
+// GetTemplateName returns the template name when provided the template ID
+func (s Service) GetTemplateName(ctx context.Context, templateID string) (string, error) {
+ f := logrus.Fields{
+ "functionName": "v1.template.service.GetTemplateName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "templateID": templateID,
+ }
+ templateName, err := s.templateRepo.GetTemplateName(ctx, templateID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem loading template by ID: %s", templateID)
+ return "", err
+ }
+
+ return templateName, nil
+}
+
// CreateTemplatePreview returns a PDF using the specified CLA Group field values and template identifier
func (s Service) CreateTemplatePreview(ctx context.Context, claGroupFields *models.CreateClaGroupTemplate, templateFor string) ([]byte, error) {
f := logrus.Fields{
diff --git a/cla-backend-go/v2/template/handlers.go b/cla-backend-go/v2/template/handlers.go
index d854234e0..db572dab0 100644
--- a/cla-backend-go/v2/template/handlers.go
+++ b/cla-backend-go/v2/template/handlers.go
@@ -92,11 +92,22 @@ func Configure(api *operations.EasyclaAPI, service v1Template.ServiceInterface,
return template.NewGetTemplatesBadRequest().WithPayload(errorResponse(reqID, err))
}
+ // Need the template name for the event log
+ templateName, lookupErr := service.GetTemplateName(ctx, input.TemplateID)
+ if lookupErr != nil || templateName == "" {
+ log.WithFields(f).WithError(lookupErr).Warnf("Error looking up template name with ID: %s", input.TemplateID)
+ return template.NewGetTemplatesBadRequest().WithPayload(errorResponse(reqID, err))
+ }
+
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLATemplateCreated,
- ProjectID: params.ClaGroupID,
- LfUsername: authUser.UserName,
- EventData: &events.CLATemplateCreatedEventData{},
+ EventType: events.CLATemplateCreated,
+ ProjectID: params.ClaGroupID,
+ ProjectSFID: projectCLAGroups[0].ProjectSFID,
+ ParentProjectSFID: projectCLAGroups[0].FoundationSFID,
+ LfUsername: authUser.UserName,
+ EventData: &events.CLATemplateCreatedEventData{
+ TemplateName: templateName,
+ },
})
response := &models.TemplatePdfs{}
From 9c6e09b430302b9a1c94e25df7e014a9df4cf646 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 28 Jun 2021 11:36:39 -0700
Subject: [PATCH 0338/1276] Updated Add/Update Template Event Log Message
Content (#3017)
---
cla-backend-go/events/event_data.go | 36 ++++++++++------------
cla-backend-go/template/handlers.go | 41 ++++++++++++++++++++++++--
cla-backend-go/utils/auth_user.go | 3 +-
cla-backend-go/v2/template/handlers.go | 10 +++++++
4 files changed, 65 insertions(+), 25 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index e0fefa7c1..3cbd3f87f 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -166,6 +166,8 @@ type CompanyACLUserAddedEventData struct {
// CLATemplateCreatedEventData data model
type CLATemplateCreatedEventData struct {
TemplateName string
+ OldPOC string
+ NewPOC string
}
// GitHubOrganizationAddedEventData data model
@@ -585,18 +587,25 @@ func (ed *CompanyACLUserAddedEventData) GetEventDetailsString(args *LogEventArgs
func (ed *CLATemplateCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := "A CLA Group template was created or updated" // nolint
if args.CLAGroupName != "" {
- data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ data = fmt.Sprintf("%s for the CLA Group %s", data, args.CLAGroupName)
}
if ed.TemplateName != "" {
- data = data + fmt.Sprintf(" using template: %s", ed.TemplateName)
+ data = fmt.Sprintf("%s using template: %s", data, ed.TemplateName)
}
if args.ProjectName != "" {
- data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ data = fmt.Sprintf("%s for the project %s", data, args.ProjectName)
}
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = fmt.Sprintf("%s by the user %s", data, args.UserName)
}
- data = data + "."
+ data = fmt.Sprintf("%s.", data)
+
+ if ed.OldPOC != "" && ed.NewPOC != "" {
+ data = fmt.Sprintf("%s The point of contact email was changed from %s to %s.", data, ed.OldPOC, ed.NewPOC)
+ } else if ed.NewPOC != "" {
+ data = fmt.Sprintf("%s The point of contact email was set to %s.", data, ed.NewPOC)
+ }
+
return data, true
}
@@ -1462,21 +1471,8 @@ func (ed *CompanyACLUserAddedEventData) GetEventSummaryString(args *LogEventArgs
// GetEventSummaryString returns the summary string for this event
func (ed *CLATemplateCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := "A CLA Group template was created or updated" // nolint
- if args.CLAGroupName != "" {
- data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
- }
- if ed.TemplateName != "" {
- data = data + fmt.Sprintf(" using template: %s", ed.TemplateName)
- }
- if args.ProjectName != "" {
- data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
- }
- if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
- }
- data = data + "."
- return data, true
+ // Same output as the details
+ return ed.GetEventDetailsString(args)
}
// GetEventSummaryString returns the summary string for this event
diff --git a/cla-backend-go/template/handlers.go b/cla-backend-go/template/handlers.go
index 4bb758510..f3409386a 100644
--- a/cla-backend-go/template/handlers.go
+++ b/cla-backend-go/template/handlers.go
@@ -4,13 +4,18 @@
package template
import (
+ "context"
+ "fmt"
+
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gen/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/template"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/runtime/middleware"
+ "github.com/sirupsen/logrus"
)
// Configure API call
@@ -26,17 +31,47 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventsService e
})
api.TemplateCreateCLAGroupTemplateHandler = template.CreateCLAGroupTemplateHandlerFunc(func(params template.CreateCLAGroupTemplateParams, claUser *user.CLAUser) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "v2.signatures.handlers.SignaturesGetProjectSignaturesHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": params.ClaGroupID,
+ "templateID": params.Body.TemplateID,
+ }
+
pdfUrls, err := service.CreateCLAGroupTemplate(params.HTTPRequest.Context(), params.ClaGroupID, ¶ms.Body)
if err != nil {
- log.Warnf("Error generating PDFs from provided templates, error: %v", err)
- return template.NewGetTemplatesBadRequest().WithPayload(errorResponse(err))
+ msg := fmt.Sprintf("Error generating PDFs from provided templates, error: %v", err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return template.NewGetTemplatesBadRequest().WithPayload(utils.ToV1ErrorResponse(utils.ErrorResponseBadRequestWithError(reqID, msg, err)))
+ }
+
+ // Need the template name for the event log
+ templateName, lookupErr := service.GetTemplateName(ctx, params.Body.TemplateID)
+ if lookupErr != nil || templateName == "" {
+ msg := fmt.Sprintf("Error looking up template name with ID: %s", params.Body.TemplateID)
+ log.WithFields(f).WithError(lookupErr).Warn(msg)
+ return template.NewGetTemplatesBadRequest().WithPayload(utils.ToV1ErrorResponse(utils.ErrorResponseBadRequestWithError(reqID, msg, lookupErr)))
+ }
+
+ // Grab the new POC value from the request
+ newPOCValue := ""
+ for _, field := range params.Body.MetaFields {
+ if field.TemplateVariable == "CONTACT_EMAIL" {
+ newPOCValue = field.Value
+ break
+ }
}
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLATemplateCreated,
ProjectID: params.ClaGroupID,
UserID: claUser.UserID,
- EventData: &events.CLATemplateCreatedEventData{},
+ EventData: &events.CLATemplateCreatedEventData{
+ TemplateName: templateName,
+ NewPOC: newPOCValue,
+ },
})
return template.NewCreateCLAGroupTemplateOK().WithPayload(&pdfUrls)
diff --git a/cla-backend-go/utils/auth_user.go b/cla-backend-go/utils/auth_user.go
index 33af5fcaf..72bce1224 100644
--- a/cla-backend-go/utils/auth_user.go
+++ b/cla-backend-go/utils/auth_user.go
@@ -29,7 +29,6 @@ func SetAuthUserProperties(authUser *auth.User, xUserName *string, xEmail *strin
tracingEnabled, conversionErr := strconv.ParseBool(os.Getenv("USER_AUTH_TRACING"))
if conversionErr == nil && tracingEnabled {
- log.WithFields(f).Debugf("Auth User: %+v", authUser)
+ log.WithFields(f).Debugf("set authuser x-username: %s and x-email: %s from Auth User model: %+v", authUser.UserName, authUser.Email, authUser)
}
- log.WithFields(f).Debugf("set authuser x-username:%s and x-email:%s", authUser.UserName, authUser.Email)
}
diff --git a/cla-backend-go/v2/template/handlers.go b/cla-backend-go/v2/template/handlers.go
index db572dab0..8292e6cd7 100644
--- a/cla-backend-go/v2/template/handlers.go
+++ b/cla-backend-go/v2/template/handlers.go
@@ -99,6 +99,15 @@ func Configure(api *operations.EasyclaAPI, service v1Template.ServiceInterface,
return template.NewGetTemplatesBadRequest().WithPayload(errorResponse(reqID, err))
}
+ // Grab the new POC value from the request
+ newPOCValue := ""
+ for _, field := range input.MetaFields {
+ if field.TemplateVariable == "CONTACT_EMAIL" {
+ newPOCValue = field.Value
+ break
+ }
+ }
+
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLATemplateCreated,
ProjectID: params.ClaGroupID,
@@ -107,6 +116,7 @@ func Configure(api *operations.EasyclaAPI, service v1Template.ServiceInterface,
LfUsername: authUser.UserName,
EventData: &events.CLATemplateCreatedEventData{
TemplateName: templateName,
+ NewPOC: newPOCValue,
},
})
From 446fe23744a20e116a907ea39c99aab3080446f5 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 28 Jun 2021 16:22:49 -0700
Subject: [PATCH 0339/1276] Cleaned Up/Optimized Signature Query (#3019)
---
cla-backend-go/signatures/repository.go | 81 ++++++++++++-------------
cla-backend-go/swagger/cla.v2.yaml | 9 ++-
2 files changed, 46 insertions(+), 44 deletions(-)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 03af483fb..b0f9359aa 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -747,7 +747,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
// Always sort by date
indexName := SignatureProjectDateIDIndex
- realPageSize := int64(100)
+ realPageSize := int64(1000)
if params.PageSize != nil && *params.PageSize > 0 {
realPageSize = *params.PageSize
}
@@ -759,61 +759,38 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
var filter expression.ConditionBuilder
var filterAdded = false
- if params.ClaType != nil {
- if strings.ToLower(*params.ClaType) == utils.ClaTypeICLA {
+ if params.ClaType != nil || params.SignatureType != nil {
+ switch getCLATypeFromParams(params) {
+ case utils.ClaTypeICLA:
log.WithFields(f).Debugf("adding ICLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
- } else if strings.ToLower(*params.ClaType) == utils.ClaTypeECLA {
+ case utils.ClaTypeECLA:
log.WithFields(f).Debugf("adding ECLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: exists", utils.SignatureTypeCLA, utils.SignatureReferenceTypeUser)
filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeUser)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeExists(), &filterAdded)
- } else if strings.ToLower(*params.ClaType) == utils.ClaTypeCCLA {
+ case utils.ClaTypeCCLA:
log.WithFields(f).Debugf("adding CCLA filters: signature_type: %s, signature_reference_type: %s, signature_user_ccla_company_id: not exists", utils.SignatureTypeCCLA, utils.SignatureReferenceTypeCompany)
filter = addAndCondition(filter, expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCCLA)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_reference_type").Equal(expression.Value(utils.SignatureReferenceTypeCompany)), &filterAdded)
filter = addAndCondition(filter, expression.Name("signature_user_ccla_company_id").AttributeNotExists(), &filterAdded)
}
- } else {
- if params.SearchField != nil {
- log.WithFields(f).Debugf("adding filters: signature_type: %s", expression.Value(params.SearchField))
- searchFieldExpression := expression.Name("signature_reference_type").Equal(expression.Value(params.SearchField))
- filter = addAndCondition(filter, searchFieldExpression, &filterAdded)
- }
-
- if params.SignatureType != nil {
- if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" && (params.FullMatch != nil && !*params.FullMatch) {
- log.WithFields(f).Debugf("adding filters: signature_type: %s", strings.ToLower(utils.StringValue(params.SignatureType)))
- indexName = SignatureProjectIDTypeIndex
- condition = condition.And(expression.Key("signature_type").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SignatureType)))))
- } else {
- log.WithFields(f).Debugf("adding filters: signature_type: %s", utils.StringValue(params.SignatureType))
- signatureTypeExpression := expression.Name("signature_type").Equal(expression.Value(utils.StringValue(params.SignatureType)))
- filter = addAndCondition(filter, signatureTypeExpression, &filterAdded)
- }
- if *params.SignatureType == utils.ClaTypeCCLA {
- log.WithFields(f).Debug("adding filters: signature_reference_id: exists, signature_user_ccla_company_id: not exists")
- signatureReferenceIDExpression := expression.Name("signature_reference_id").AttributeExists()
- signatureUserCclaCompanyIDExpression := expression.Name("signature_user_ccla_company_id").AttributeNotExists()
- filter = addAndCondition(filter, signatureReferenceIDExpression, &filterAdded)
- filter = addAndCondition(filter, signatureUserCclaCompanyIDExpression, &filterAdded)
- }
- }
+ }
- if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
- if *params.FullMatch {
- indexName = SignatureReferenceSearchIndex
- log.WithFields(f).Debugf("adding filter signature_reference_name_lower: %s", strings.ToLower(utils.StringValue(params.SearchTerm)))
- condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SearchTerm)))))
- } else {
- log.WithFields(f).Debugf("adding filters signature_reference_name_lower: %s or user_email: %s", strings.ToLower(utils.StringValue(params.SearchTerm)), strings.ToLower(utils.StringValue(params.SearchTerm)))
- searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
- Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
- filter = addAndCondition(filter, searchTermExpression, &filterAdded)
- }
- }
+ if params.SearchTerm != nil && utils.StringValue(params.SearchTerm) != "" {
+ //if *params.FullMatch {
+ // indexName = SignatureReferenceSearchIndex
+ // log.WithFields(f).Debugf("adding filter signature_reference_name_lower: %s", strings.ToLower(utils.StringValue(params.SearchTerm)))
+ // condition = condition.And(expression.Key("signature_reference_name_lower").Equal(expression.Value(strings.ToLower(utils.StringValue(params.SearchTerm)))))
+ //} // else {
+ log.WithFields(f).Debugf("adding filters signature_reference_name_lower: %s or user_email: %s", strings.ToLower(utils.StringValue(params.SearchTerm)), strings.ToLower(utils.StringValue(params.SearchTerm)))
+ searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))).
+ Or(expression.Name("user_email").Contains(strings.ToLower(utils.StringValue(params.SearchTerm))))
+ filter = addAndCondition(filter, searchTermExpression, &filterAdded)
+ //}
}
if params.Approved != nil {
@@ -821,6 +798,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(params.Approved))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
+
if params.Signed != nil {
log.WithFields(f).Debugf("adding signature_signed: %t filter", aws.BoolValue(params.Signed))
searchTermExpression := expression.Name("signature_signed").Equal(expression.Value(params.Signed))
@@ -857,6 +835,7 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
IndexName: aws.String(indexName), // Name of a secondary index to scan
}
f["indexName"] = indexName
+ log.WithFields(f).Debugf("queryInput: %+v", queryInput)
if params.NextKey != nil {
queryInput.ExclusiveStartKey, err = decodeNextKey(*params.NextKey)
@@ -941,6 +920,24 @@ func (repo repository) GetProjectSignatures(ctx context.Context, params signatur
}, nil
}
+// getCLATypeFromParams helper function to combine the new CLA Type parameter and the old legacy signature type parameter - returns one of the values from utils.ClaTypeICLA, utils.ClaTypeECLA, utils.ClaTypeCCLA or empty string if nothing matches
+func getCLATypeFromParams(params signatures.GetProjectSignaturesParams) string {
+ if params.ClaType != nil {
+ return strings.ToLower(*params.ClaType)
+ } else if params.SignatureType != nil {
+ // ICLA -> CLAType == icla, SignatureType == cla
+ // ECLA -> CLAType == ecla, SignatureType == ecla
+ // CCLA -> CLAType == ccla, SignatureType == ccla
+ if strings.ToLower(*params.SignatureType) == "cla" {
+ return utils.ClaTypeICLA
+ }
+
+ return strings.ToLower(*params.SignatureType)
+ }
+
+ return ""
+}
+
// CreateProjectSummaryReport generates a project summary report based on the specified input
func (repo repository) CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error) { // nolint
f := logrus.Fields{
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index e370f0929..06a4ac8b5 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3817,10 +3817,15 @@ parameters:
pattern: '^\w+$'
signatureType:
name: signatureType
+ description: >
+ CLA Type query parameter - allows the caller to specify either individual, employee or corporate signature, valid options:
+ * `icla` - for individual contributor signature records (individuals not associated with a corporation)
+ * `ecla` - for employee contributor signature records (acknowledgements from corporate contributors)
+ * `ccla` - for corporate contributor signature records (created by CLA Signatories and managed by CLA Managers)
in: query
type: string
required: false
- enum: [ ccla,cla ]
+ enum: [ccla,ecla,cla]
claType:
name: claType
description: >
@@ -3831,7 +3836,7 @@ parameters:
in: query
type: string
required: false
- enum: [ icla,ecla,ccla ]
+ enum: [ccla,ecla,icla]
templateCLAType:
name: claType
in: query
From ae5955d220f9672a2fcb67206a306602b0f059d2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 1 Jul 2021 12:43:55 -0700
Subject: [PATCH 0340/1276] Resolved Issues Raised by Project Service Swagger
Model Updates (#3025)
- project.Parent and project.EntityName was changed to optional, making
the generated type to change from string to *string. Updated logic that
reviews this field and dereferences the value as needed.
Signed-off-by: David Deal
---
cla-backend-go/events/service.go | 10 +++---
cla-backend-go/tests/project_helpers_test.go | 22 ++++++-------
cla-backend-go/utils/conversion.go | 5 +++
cla-backend-go/utils/project_helpers.go | 4 +--
cla-backend-go/v2/cla_groups/handlers.go | 8 ++---
cla-backend-go/v2/cla_groups/helpers.go | 13 ++++----
cla-backend-go/v2/cla_groups/service.go | 2 +-
.../v2/github_organizations/service.go | 6 ++--
cla-backend-go/v2/project-service/client.go | 32 +++++++++----------
cla-backend-go/v2/project/handlers.go | 12 +++----
cla-backend-go/v2/repositories/service.go | 6 ++--
cla-backend-go/v2/sign/service.go | 2 +-
12 files changed, 63 insertions(+), 59 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 2eb9fadf9..7a492bc98 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -255,11 +255,11 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
args.ProjectName = project.Name
// Try to load and set the parent information
- if project.Parent != "" {
- log.WithFields(f).Debugf("loading salesforce project parent by ID: %s...", project.Parent)
- parentProject, parentProjectErr := project_service.GetClient().GetProject(project.Parent)
+ if utils.StringValue(project.Parent) != "" {
+ log.WithFields(f).Debugf("loading salesforce project parent by ID: %s...", utils.StringValue(project.Parent))
+ parentProject, parentProjectErr := project_service.GetClient().GetProject(utils.StringValue(project.Parent))
if parentProjectErr != nil || parentProject == nil {
- log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", project.Parent)
+ log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", utils.StringValue(project.Parent))
return nil
}
var parentProjectName, parentProjectID string
@@ -271,7 +271,7 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
parentProjectID = project.ID
}
log.WithFields(f).Debugf("loaded salesforce project by parent ID: %s - resulting in ID: %s with name: %s",
- project.Parent, parentProjectID, parentProjectName)
+ utils.StringValue(project.Parent), parentProjectID, parentProjectName)
args.ParentProjectSFID = parentProjectID
args.ParentProjectName = parentProjectName
} else if project.Foundation != nil {
diff --git a/cla-backend-go/tests/project_helpers_test.go b/cla-backend-go/tests/project_helpers_test.go
index 4c7bfe472..eb45c05ad 100644
--- a/cla-backend-go/tests/project_helpers_test.go
+++ b/cla-backend-go/tests/project_helpers_test.go
@@ -20,14 +20,14 @@ const (
func TestIsProjectHasRootParentNoParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = ""
+ project.Parent = utils.StringRef("")
project.Foundation = nil
assert.True(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - Empty Parent")
}
func TestIsProjectHasRootParentLF(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -38,7 +38,7 @@ func TestIsProjectHasRootParentLF(t *testing.T) {
func TestIsProjectHasRootParentLFProjectsLLC(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -49,7 +49,7 @@ func TestIsProjectHasRootParentLFProjectsLLC(t *testing.T) {
func TestIsProjectHasRootParentNonLF(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -60,7 +60,7 @@ func TestIsProjectHasRootParentNonLF(t *testing.T) {
func TestIsStandaloneProject(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = ""
+ project.Parent = utils.StringRef("")
project.Foundation = nil
project.Projects = []*models.ProjectOutput{}
assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with No Parent with No Children")
@@ -68,7 +68,7 @@ func TestIsStandaloneProject(t *testing.T) {
func TestLFParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -80,7 +80,7 @@ func TestLFParent(t *testing.T) {
func TestLFProjectsLLCParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -92,7 +92,7 @@ func TestLFProjectsLLCParent(t *testing.T) {
func TestLFParentWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -104,7 +104,7 @@ func TestLFParentWithChildren(t *testing.T) {
func TestLFProjectsLLCParentWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -140,7 +140,7 @@ func TestLFProjectsLLCParentWithChildren(t *testing.T) {
func TestIsProjectHaveChildrenNoChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = nil
project.Projects = []*models.ProjectOutput{}
assert.False(t, utils.IsProjectHaveChildren(project), "Project has no children")
@@ -148,7 +148,7 @@ func TestIsProjectHaveChildrenNoChildren(t *testing.T) {
func TestIsProjectHaveChildrenWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = testProjectParentID
+ project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = nil
child := &models.ProjectOutput{
ProjectCommon: models.ProjectCommon{},
diff --git a/cla-backend-go/utils/conversion.go b/cla-backend-go/utils/conversion.go
index e2a0f3d32..e51c740c1 100644
--- a/cla-backend-go/utils/conversion.go
+++ b/cla-backend-go/utils/conversion.go
@@ -11,6 +11,11 @@ func StringValue(input *string) string {
return *input
}
+// StringRef function convert string to string reference
+func StringRef(input string) *string {
+ return &input
+}
+
// Int64Value function convert int64 pointer to string
func Int64Value(input *int64) int64 {
if input == nil {
diff --git a/cla-backend-go/utils/project_helpers.go b/cla-backend-go/utils/project_helpers.go
index 3457fc55d..6cffde169 100644
--- a/cla-backend-go/utils/project_helpers.go
+++ b/cla-backend-go/utils/project_helpers.go
@@ -7,13 +7,13 @@ import "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/mod
// IsProjectHasRootParent determines if the a given project has a root parent. A root parent is a parent that is empty parent or the parent is TLF or LFProjects
func IsProjectHasRootParent(project *models.ProjectOutputDetailed) bool {
- return project.Parent == "" || (project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
+ return StringValue(project.Parent) == "" || (project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
}
// IsStandaloneProject determines if a given project is a standalone project. A standalone project is a project with no parent or the parent is TLF/LFProjects and does not have any children
func IsStandaloneProject(project *models.ProjectOutputDetailed) bool {
// standalone: No parent or parent is TLF/LFProjects....and no children
- return (project.Parent == "" ||
+ return (StringValue(project.Parent) == "" ||
(project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))) &&
len(project.Projects) == 0
}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 288044ae9..a1b0110b4 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -306,15 +306,15 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
var parentProject *v2ProjectServiceModels.ProjectOutputDetailed
// Handle the ONAP edge case
- if project.Parent != "" {
- parentProject, projectErr = psc.GetProject(project.Parent)
+ if utils.StringValue(project.Parent) != "" {
+ parentProject, projectErr = psc.GetProject(utils.StringValue(project.Parent))
if parentProject == nil || projectErr != nil {
- msg := fmt.Sprintf("Failed to get parent: %s", project.Parent)
+ msg := fmt.Sprintf("Failed to get parent: %s", utils.StringValue(project.Parent))
log.WithFields(f).Warnf(msg)
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
}
- if (project.Parent != "" && !utils.IsProjectCategory(project, parentProject)) || (utils.IsProjectHasRootParent(project) && project.ProjectType == utils.ProjectTypeProjectGroup) {
+ if (utils.StringValue(project.Parent) != "" && !utils.IsProjectCategory(project, parentProject)) || (utils.IsProjectHasRootParent(project) && project.ProjectType == utils.ProjectTypeProjectGroup) {
msg := fmt.Sprintf("Unable to enroll salesforce foundation project: %s in project level cla-group.", projectSFID)
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index f426104a1..cf27d3867 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -159,7 +159,7 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
+ isLFParent, err := psc.IsTheLinuxFoundation(utils.StringValue(foundationProjectDetails.Parent))
if err != nil {
log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return false, err
@@ -168,7 +168,7 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
// If the foundation details in the platform project service indicates that this foundation has no parent or no
// children/sub-project... (stand alone project situation)
log.WithFields(f).Debug("checking to see if we have a standalone project...")
- if (foundationProjectDetails.Parent == "" || isLFParent) && len(foundationProjectDetails.Projects) == 0 {
+ if (utils.StringValue(foundationProjectDetails.Parent) == "" || isLFParent) && len(foundationProjectDetails.Projects) == 0 {
log.WithFields(f).Debug("we have a standalone project...")
// Did the user actually pass in any projects? If none - add the foundation ID to the list and return to
// indicate it is a "standalone project"
@@ -251,7 +251,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
if projectTree != nil && projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- foundationProjectDetails.Parent, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
+ utils.StringValue(foundationProjectDetails.Parent), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
@@ -331,7 +331,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(foundationProjectDetails.Parent)
+ isLFParent, err := psc.IsTheLinuxFoundation(utils.StringValue(foundationProjectDetails.Parent))
if err != nil {
log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return err
@@ -343,13 +343,12 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
- if foundationProjectDetails.Parent != "" && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
+ if utils.StringValue(foundationProjectDetails.Parent) != "" && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- foundationProjectDetails.Parent, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
+ utils.StringValue(foundationProjectDetails.Parent), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
-
}
// Check to see if all the provided enrolled projects are part of this foundation
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 87d436c4f..701fe6637 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -386,7 +386,7 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
var parentDetails *v2ProjectServiceModels.ProjectOutputDetailed
var parentDetailErr error
- if sfProjectModelDetails.Parent != "" {
+ if utils.StringValue(sfProjectModelDetails.Parent) != "" {
var parentSFID string
// Use utility function that considers TLF and LF Projects, LLC
parentSFID, parentDetailErr = v2ProjectService.GetClient().GetParentProject(projectOrFoundationSFID)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 4a7d9e553..28981b4af 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -102,7 +102,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
if utils.IsProjectHasRootParent(projectServiceRecord) {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = projectServiceRecord.Parent
+ parentProjectSFID = utils.StringValue(projectServiceRecord.Parent)
}
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
@@ -290,11 +290,11 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.Foundation != nil &&
+ if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = project.Parent
+ parentProjectSFID = utils.StringValue(project.Parent)
}
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 72a6f1c12..9d25f680a 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -145,7 +145,7 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
existingModel, exists := projectServiceModels[projectSFID]
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
- return existingModel.Parent, nil
+ return utils.StringValue(existingModel.Parent), nil
}
log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModels))
@@ -161,14 +161,14 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ if utils.StringValue(projectModel.Parent) == "" || (projectModel.Foundation != nil &&
(projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return projectSFID, nil
}
- log.WithFields(f).Debugf("returning parent projectSFID: %s", projectModel.Parent)
- return projectModel.Parent, nil
+ log.WithFields(f).Debugf("returning parent projectSFID: %s", utils.StringValue(projectModel.Parent))
+ return utils.StringValue(projectModel.Parent), nil
}
// GetParentProjectModel returns the parent project model if there is a parent, otherwise returns nil
@@ -190,20 +190,20 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
// Parent in the cache?
- existingParentModel, exists = projectServiceModels[existingModel.Parent]
+ existingParentModel, exists = projectServiceModels[utils.StringValue(existingModel.Parent)]
if exists {
return existingParentModel, nil
}
// Parent project not in the cache - lookup
- parentProjectModel, err := pmm.GetProject(existingModel.Parent)
+ parentProjectModel, err := pmm.GetProject(utils.StringValue(existingModel.Parent))
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", existingModel.Parent)
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", utils.StringValue(existingModel.Parent))
return nil, err
}
// Update our cache for next time
- projectServiceModels[existingModel.Parent] = parentProjectModel
+ projectServiceModels[utils.StringValue(existingModel.Parent)] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
return parentProjectModel, nil
@@ -221,27 +221,27 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
// Do they have a parent?
- if projectModel.Parent == "" || (projectModel.Foundation != nil &&
+ if utils.StringValue(projectModel.Parent) == "" || (projectModel.Foundation != nil &&
(projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return nil, nil
}
// Parent in the cache?
- existingParentModel, exists = projectServiceModels[projectModel.Parent]
+ existingParentModel, exists = projectServiceModels[utils.StringValue(projectModel.Parent)]
if exists {
return existingParentModel, nil
}
// Parent project not in the cache - lookup
- parentProjectModel, err := pmm.GetProject(projectModel.Parent)
+ parentProjectModel, err := pmm.GetProject(utils.StringValue(projectModel.Parent))
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", projectModel.Parent)
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", utils.StringValue(projectModel.Parent))
return nil, err
}
// Update our cache for next time
- projectServiceModels[existingModel.Parent] = parentProjectModel
+ projectServiceModels[utils.StringValue(existingModel.Parent)] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
return parentProjectModel, nil
@@ -286,13 +286,13 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
return false, err
}
- if projectModel.Parent == "" {
+ if utils.StringValue(projectModel.Parent) == "" {
return false, nil
}
- parentProjectModel, err := pmm.GetProject(projectModel.Parent)
+ parentProjectModel, err := pmm.GetProject(utils.StringValue(projectModel.Parent))
if err != nil {
- log.WithFields(f).Warnf("unable to lookup parent project by ID: %s error: %+v", projectModel.Parent, err)
+ log.WithFields(f).Warnf("unable to lookup parent project by ID: %s error: %+v", utils.StringValue(projectModel.Parent), err)
return false, err
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index a02d48683..78c0455bf 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -316,10 +316,10 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
// Lookup the parent info, if it's available
var parentName string
- if sfProject.Parent != "" {
- sfParentProject, err := psc.GetProject(sfProject.Parent)
+ if utils.StringValue(sfProject.Parent) != "" {
+ sfParentProject, err := psc.GetProject(utils.StringValue(sfProject.Parent))
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to load parant project by ID: %s", sfProject.Parent)
+ log.WithFields(f).WithError(err).Warnf("unable to load parant project by ID: %s", utils.StringValue(sfProject.Parent))
}
if sfParentProject != nil {
@@ -334,18 +334,18 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetailed, parentName string) *models.SfProjectSummary {
return &models.SfProjectSummary{
- EntityName: sfProject.EntityName,
+ EntityName: utils.StringValue(sfProject.EntityName),
EntityType: sfProject.EntityType,
Funding: sfProject.Funding,
ID: sfProject.ID,
LfSupported: sfProject.LFSponsored,
Name: sfProject.Name,
- ParentID: sfProject.Parent,
+ ParentID: utils.StringValue(sfProject.Parent),
ParentName: parentName,
Slug: sfProject.Slug,
Status: sfProject.Status,
Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (sfProject.Parent == "" || (sfProject.Foundation != nil &&
+ IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (utils.StringValue(sfProject.Parent) == "" || (sfProject.Foundation != nil &&
(sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index a47796026..837cce2ca 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -90,11 +90,11 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if project.Parent == "" || (project.Foundation != nil &&
+ if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = project.Parent
+ parentProjectSFID = utils.StringValue(project.Parent)
}
allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, aws.StringValue(input.ClaGroupID))
@@ -252,7 +252,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
return nil, err
}
f["projectName"] = projectModel.Name
- if projectModel.Parent != "" {
+ if utils.StringValue(projectModel.Parent) != "" {
f["projectParentSFID"] = projectModel.Parent
}
log.WithFields(f).Debug("loaded project from the project service")
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 683299365..6019dc427 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,7 +173,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if project.Parent == "" || (project.Foundation != nil &&
+ if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
(project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(ctx, utils.StringValue(input.ProjectSfid))
From 66a0be847e26b9b430e299a1069513fbe73bf72b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 1 Jul 2021 14:49:52 -0700
Subject: [PATCH 0341/1276] Resolved/Updated Error Handling for Missing Project
to CLA Group Mapping (#3026)
- Updated error handling for the project to CLA group mapping query
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/service.go | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 701fe6637..91520848e 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -646,7 +646,6 @@ func (s *service) appendCLAGroupsForProject(ctx context.Context, f logrus.Fields
// Since this is a project and not a foundation, we'll want to set he parent foundation ID and name (which is
// our parent in this case)
var foundationID, foundationName string
- log.WithFields(f).Debug("found 'project' in platform project service.")
if sfProjectModelDetails.ProjectOutput.Foundation != nil {
foundationID = sfProjectModelDetails.ProjectOutput.Foundation.ID
foundationName = sfProjectModelDetails.ProjectOutput.Foundation.Name
@@ -658,18 +657,17 @@ func (s *service) appendCLAGroupsForProject(ctx context.Context, f logrus.Fields
log.WithFields(f).Debugf("no parent - using project as foundation ID: %s and name: %s", foundationID, foundationName)
}
- log.WithFields(f).Debug("locating CLA Group mapping...")
+ log.WithFields(f).Debugf("locating CLA Group mapping using projectOrFoundationSFID: '%s'...", projectOrFoundationSFID)
projectCLAGroup, lookupErr := s.projectsClaGroupsRepo.GetClaGroupIDForProject(ctx, projectOrFoundationSFID)
- if lookupErr != nil {
- log.WithFields(f).Warnf("problem locating CLA group by project id, error: %+v", lookupErr)
+ if lookupErr != nil || projectCLAGroup == nil || projectCLAGroup.ClaGroupID == "" {
+ log.WithFields(f).WithError(lookupErr).Warnf("problem locating CLA group by project id: '%s'", projectOrFoundationSFID)
return "", "", &utils.ProjectCLAGroupMappingNotFound{ProjectSFID: projectOrFoundationSFID, Err: lookupErr}
}
- log.WithFields(f).Debugf("loading CLA Group by ID: %s", projectCLAGroup.ClaGroupID)
+ log.WithFields(f).Debugf("loading CLA Group by ID: '%s' - %+v", projectCLAGroup.ClaGroupID, projectCLAGroup)
v1ClaGroupsByProject, claGroupLoadErr := s.v1ProjectService.GetCLAGroupByID(ctx, projectCLAGroup.ClaGroupID)
- //v1ClaGroupsByProject, prjerr := s.v1ProjectService.GetClaGroupByProjectSFID(projectOrFoundationSFID, DontLoadDetails)
if claGroupLoadErr != nil {
- log.WithFields(f).Warnf("problem loading CLA group by id, error: %+v", claGroupLoadErr)
+ log.WithFields(f).Warnf("problem loading CLA group by id: '%s', error: %+v", projectCLAGroup.ClaGroupID, claGroupLoadErr)
return "", "", &utils.CLAGroupNotFound{CLAGroupID: projectCLAGroup.ClaGroupID, Err: claGroupLoadErr}
}
From 36b9c616a4b1be6b2a4238f260e1378bc7fafdbe Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 1 Jul 2021 16:02:56 -0700
Subject: [PATCH 0342/1276] Updated Build to Generate v1 APIs/Models to the v1
folder (#3027)
---
cla-backend-go/Makefile | 15 +++++++++------
cla-backend-go/approval_list/handlers.go | 6 +++---
cla-backend-go/approval_list/helpers.go | 2 +-
cla-backend-go/approval_list/repository.go | 2 +-
cla-backend-go/approval_list/service.go | 2 +-
cla-backend-go/cla_manager/handlers.go | 8 ++++----
cla-backend-go/cla_manager/models.go | 2 +-
cla-backend-go/cla_manager/service.go | 4 ++--
.../functional_tests/cla_manager/cla_manager.go | 2 +-
.../cmd/functional_tests/health/health.go | 2 +-
.../cmd/functional_tests/signatures/signatures.go | 2 +-
cla-backend-go/cmd/server.go | 6 +++---
cla-backend-go/company/handlers.go | 8 ++++----
cla-backend-go/company/models.go | 2 +-
cla-backend-go/company/repository.go | 2 +-
cla-backend-go/company/service.go | 2 +-
cla-backend-go/docs/handlers.go | 4 ++--
cla-backend-go/docs/swagger.go | 2 +-
cla-backend-go/events/event_data.go | 2 +-
cla-backend-go/events/handlers.go | 6 +++---
cla-backend-go/events/mock.go | 4 ++--
cla-backend-go/events/mockrepo.go | 4 ++--
cla-backend-go/events/models.go | 2 +-
cla-backend-go/events/repository.go | 4 ++--
cla-backend-go/events/service.go | 4 ++--
cla-backend-go/gerrits/handlers.go | 6 +++---
cla-backend-go/gerrits/models.go | 2 +-
cla-backend-go/gerrits/repository.go | 2 +-
cla-backend-go/gerrits/service.go | 2 +-
cla-backend-go/github/handlers.go | 4 ++--
cla-backend-go/github_organizations/handlers.go | 6 +++---
cla-backend-go/github_organizations/helpers.go | 2 +-
cla-backend-go/github_organizations/mock.go | 2 +-
cla-backend-go/github_organizations/models.go | 2 +-
cla-backend-go/github_organizations/repository.go | 2 +-
cla-backend-go/github_organizations/service.go | 2 +-
cla-backend-go/health/handlers.go | 6 +++---
cla-backend-go/health/service.go | 2 +-
cla-backend-go/project/errors.go | 2 +-
cla-backend-go/project/handlers.go | 6 +++---
cla-backend-go/project/helpers.go | 2 +-
cla-backend-go/project/repository.go | 4 ++--
cla-backend-go/project/service.go | 4 ++--
cla-backend-go/repositories/handlers.go | 6 +++---
.../repositories/mock/mock_repository.go | 2 +-
cla-backend-go/repositories/mock/mock_service.go | 2 +-
cla-backend-go/repositories/models.go | 2 +-
cla-backend-go/repositories/repository.go | 2 +-
cla-backend-go/repositories/service.go | 2 +-
cla-backend-go/signatures/converters.go | 2 +-
cla-backend-go/signatures/handlers.go | 6 +++---
cla-backend-go/signatures/models.go | 2 +-
cla-backend-go/signatures/repository.go | 4 ++--
cla-backend-go/signatures/service.go | 4 ++--
cla-backend-go/template/handlers.go | 6 +++---
cla-backend-go/template/repository.go | 2 +-
cla-backend-go/template/service.go | 2 +-
cla-backend-go/tests/events_test.go | 2 +-
cla-backend-go/tests/project_test.go | 2 +-
cla-backend-go/tests/repository_test.go | 2 +-
cla-backend-go/userSubscribeLambda/main.go | 2 +-
cla-backend-go/users/handlers.go | 6 +++---
cla-backend-go/users/repository.go | 2 +-
cla-backend-go/users/service.go | 2 +-
cla-backend-go/utils/cla_user.go | 2 +-
cla-backend-go/utils/responses.go | 2 +-
cla-backend-go/utils/signature_utils.go | 2 +-
cla-backend-go/v2/cla_groups/service.go | 4 ++--
cla-backend-go/v2/cla_manager/emails.go | 2 +-
cla-backend-go/v2/cla_manager/service.go | 2 +-
cla-backend-go/v2/company/service.go | 6 +++---
cla-backend-go/v2/dynamo_events/autoenable.go | 2 +-
.../v2/dynamo_events/autoenable_test.go | 2 +-
.../v2/dynamo_events/projects_cla_groups.go | 4 ++--
cla-backend-go/v2/events/converters.go | 2 +-
cla-backend-go/v2/events/csvResponse.go | 2 +-
cla-backend-go/v2/events/handlers.go | 2 +-
cla-backend-go/v2/gerrits/handlers.go | 2 +-
cla-backend-go/v2/github_activity/service.go | 2 +-
cla-backend-go/v2/github_activity/service_test.go | 2 +-
cla-backend-go/v2/github_organizations/service.go | 2 +-
cla-backend-go/v2/project/converters.go | 2 +-
cla-backend-go/v2/project/handlers.go | 2 +-
cla-backend-go/v2/repositories/handlers.go | 2 +-
cla-backend-go/v2/repositories/service.go | 2 +-
cla-backend-go/v2/sign/service.go | 2 +-
cla-backend-go/v2/signatures/converters.go | 2 +-
cla-backend-go/v2/signatures/handlers.go | 4 ++--
cla-backend-go/v2/signatures/service.go | 2 +-
cla-backend-go/v2/template/handlers.go | 2 +-
cla-backend-go/version/handlers.go | 6 +++---
91 files changed, 145 insertions(+), 142 deletions(-)
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index a9e8110d6..406f757a2 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -29,7 +29,9 @@ GO_FILES=$(shell find . -type f -name '*.go' -not -path './vendor/*')
all: all-mac
all-mac: clean swagger deps fmt build-mac build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac test lint
all-linux: clean swagger deps fmt build-linux build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux test lint
+lambdas-mac: build-aws-lambda-mac
build-lambdas-mac: build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-metrics-report-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac
+lambdas: build-lambdas-linux
build-lambdas-linux: build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-metrics-report-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux
generate: swagger
@@ -70,13 +72,17 @@ clean:
functional-tests* metrics-aws-lambda* metrics-report-lambda* \
user-subscribe-lambda* zipbuild-lambda* zipbuilder-scheduler-lambda*
+swagger-clean: clean-swagger
clean-swagger:
@rm -rf gen/
clean-all: clean clean-swagger
@rm -rf vendor/
-swagger: clean-swagger swagger-build swagger-validate
+swagger: clean-swagger swagger-prep swagger-build swagger-validate
+build-swagger: swagger-build
+swagger-build: swagger-build-v1-services swagger-build-v2-services swagger-build-project-service swagger-build-organization-service swagger-build-user-service swagger-build-acs-service
+swagger-validate: swagger-v1-validate swagger-v2-validate
swagger-prep:
@mkdir gen
@@ -89,6 +95,8 @@ swagger-build-v1-services:
-t gen \
-f swagger/cla.v1.compiled.yaml \
--copyright-file=copyright-header.txt \
+ --server-package=v1/restapi \
+ --model-package=v1/models \
--exclude-main \
-A cla \
-P user.CLAUser
@@ -154,11 +162,6 @@ swagger-build-acs-service:
-t v2/acs-service \
-f swagger/acs-service.yaml
-build-swagger: swagger-build
-swagger-build: clean-swagger swagger-prep swagger-build-v1-services swagger-build-v2-services swagger-build-project-service swagger-build-organization-service swagger-build-user-service swagger-build-acs-service
-
-swagger-validate: swagger-v1-validate swagger-v2-validate
-
swagger-v1-validate:
@echo ""
@echo "Validating EasyCLA v1 legacy API specification..."
diff --git a/cla-backend-go/approval_list/handlers.go b/cla-backend-go/approval_list/handlers.go
index c8df6a62d..8dfafddbc 100644
--- a/cla-backend-go/approval_list/handlers.go
+++ b/cla-backend-go/approval_list/handlers.go
@@ -9,9 +9,9 @@ import (
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/company"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/company"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/signatures"
"github.com/communitybridge/easycla/cla-backend-go/user"
diff --git a/cla-backend-go/approval_list/helpers.go b/cla-backend-go/approval_list/helpers.go
index fb53ce6d3..187c39bf7 100644
--- a/cla-backend-go/approval_list/helpers.go
+++ b/cla-backend-go/approval_list/helpers.go
@@ -8,7 +8,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/approval_list/repository.go b/cla-backend-go/approval_list/repository.go
index 6a025bdc7..26218948b 100644
--- a/cla-backend-go/approval_list/repository.go
+++ b/cla-backend-go/approval_list/repository.go
@@ -15,7 +15,7 @@ import (
"github.com/gofrs/uuid"
"github.com/sirupsen/logrus"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/aws/aws-sdk-go/aws"
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index ae9ce32df..21ee64305 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -25,7 +25,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/users"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
)
// errors
diff --git a/cla-backend-go/cla_manager/handlers.go b/cla-backend-go/cla_manager/handlers.go
index 4d16cd222..8bea2b3ce 100644
--- a/cla-backend-go/cla_manager/handlers.go
+++ b/cla-backend-go/cla_manager/handlers.go
@@ -14,18 +14,18 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/emails"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/cla_manager"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/cla_manager"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/aws/aws-sdk-go/aws"
- sigAPI "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ sigAPI "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/signatures"
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/project"
"github.com/communitybridge/easycla/cla-backend-go/user"
diff --git a/cla-backend-go/cla_manager/models.go b/cla-backend-go/cla_manager/models.go
index ff5e3a756..7d9fad8e7 100644
--- a/cla-backend-go/cla_manager/models.go
+++ b/cla-backend-go/cla_manager/models.go
@@ -3,7 +3,7 @@
package cla_manager
-import "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+import "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
// CLAManagerRequests data model
type CLAManagerRequests struct {
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index 58ddbb212..07b18574e 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -16,8 +16,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- sigAPI "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ sigAPI "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/project"
"github.com/communitybridge/easycla/cla-backend-go/signatures"
diff --git a/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go b/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
index fa015d3ab..ec71d9dac 100644
--- a/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
+++ b/cla-backend-go/cmd/functional_tests/cla_manager/cla_manager.go
@@ -9,7 +9,7 @@ import (
"reflect"
"github.com/communitybridge/easycla/cla-backend-go/cmd/functional_tests/test_models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/verdverm/frisby"
)
diff --git a/cla-backend-go/cmd/functional_tests/health/health.go b/cla-backend-go/cmd/functional_tests/health/health.go
index 8fe3d7ec7..e2937a344 100644
--- a/cla-backend-go/cmd/functional_tests/health/health.go
+++ b/cla-backend-go/cmd/functional_tests/health/health.go
@@ -9,7 +9,7 @@ import (
"reflect"
"github.com/communitybridge/easycla/cla-backend-go/cmd/functional_tests/test_models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/verdverm/frisby"
)
diff --git a/cla-backend-go/cmd/functional_tests/signatures/signatures.go b/cla-backend-go/cmd/functional_tests/signatures/signatures.go
index 340324efd..f0e423412 100644
--- a/cla-backend-go/cmd/functional_tests/signatures/signatures.go
+++ b/cla-backend-go/cmd/functional_tests/signatures/signatures.go
@@ -9,7 +9,7 @@ import (
"reflect"
"github.com/communitybridge/easycla/cla-backend-go/cmd/functional_tests/test_models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/verdverm/frisby"
)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 5718fe743..4cf1af8be 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -72,9 +72,9 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/auth"
v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/docraptor"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
v2RestAPI "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi"
v2Ops "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/github"
diff --git a/cla-backend-go/company/handlers.go b/cla-backend-go/company/handlers.go
index f4fc7243a..f7216dd86 100644
--- a/cla-backend-go/company/handlers.go
+++ b/cla-backend-go/company/handlers.go
@@ -12,14 +12,14 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/organization"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/organization"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/users"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/company"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/company"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
orgService "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
diff --git a/cla-backend-go/company/models.go b/cla-backend-go/company/models.go
index 30e9a3e2a..9b4fea85a 100644
--- a/cla-backend-go/company/models.go
+++ b/cla-backend-go/company/models.go
@@ -6,7 +6,7 @@ package company
import (
"context"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/strfmt"
diff --git a/cla-backend-go/company/repository.go b/cla-backend-go/company/repository.go
index d5d8ca32c..ea57f7e6c 100644
--- a/cla-backend-go/company/repository.go
+++ b/cla-backend-go/company/repository.go
@@ -16,7 +16,7 @@ import (
"github.com/go-openapi/strfmt"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
diff --git a/cla-backend-go/company/service.go b/cla-backend-go/company/service.go
index 2d3c69072..ab513e0d4 100644
--- a/cla-backend-go/company/service.go
+++ b/cla-backend-go/company/service.go
@@ -15,7 +15,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
organization_service "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
diff --git a/cla-backend-go/docs/handlers.go b/cla-backend-go/docs/handlers.go
index 4bcdf5cd4..1ccae22d1 100644
--- a/cla-backend-go/docs/handlers.go
+++ b/cla-backend-go/docs/handlers.go
@@ -4,8 +4,8 @@
package docs
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/docs"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/docs"
"github.com/go-openapi/runtime/middleware"
)
diff --git a/cla-backend-go/docs/swagger.go b/cla-backend-go/docs/swagger.go
index d486fa1e9..999b937b8 100644
--- a/cla-backend-go/docs/swagger.go
+++ b/cla-backend-go/docs/swagger.go
@@ -6,7 +6,7 @@ package docs
import (
"net/http"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi"
"github.com/go-openapi/runtime"
)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 3cbd3f87f..70f17f5ce 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -6,7 +6,7 @@ package events
import (
"fmt"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
diff --git a/cla-backend-go/events/handlers.go b/cla-backend-go/events/handlers.go
index 919f0d3a7..5703673e5 100644
--- a/cla-backend-go/events/handlers.go
+++ b/cla-backend-go/events/handlers.go
@@ -4,9 +4,9 @@
package events
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/events/mock.go b/cla-backend-go/events/mock.go
index 91494c65c..882c8358e 100644
--- a/cla-backend-go/events/mock.go
+++ b/cla-backend-go/events/mock.go
@@ -12,8 +12,8 @@ import (
context "context"
reflect "reflect"
- models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- events0 "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ events0 "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
gomock "github.com/golang/mock/gomock"
)
diff --git a/cla-backend-go/events/mockrepo.go b/cla-backend-go/events/mockrepo.go
index 82f9025a8..0ba7ceee1 100644
--- a/cla-backend-go/events/mockrepo.go
+++ b/cla-backend-go/events/mockrepo.go
@@ -12,8 +12,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
"github.com/go-openapi/strfmt"
)
diff --git a/cla-backend-go/events/models.go b/cla-backend-go/events/models.go
index de832080e..993f21e52 100644
--- a/cla-backend-go/events/models.go
+++ b/cla-backend-go/events/models.go
@@ -3,7 +3,7 @@
package events
-import "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+import "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
//IndividualSignedEvent represntative of ICLA signatures
const IndividualSignedEvent = "IndividualSignatureSigned"
diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go
index bfce950a4..bac4ec089 100644
--- a/cla-backend-go/events/repository.go
+++ b/cla-backend-go/events/repository.go
@@ -27,8 +27,8 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
)
// errors
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 7a492bc98..0ad22ba99 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -18,8 +18,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/gerrits/handlers.go b/cla-backend-go/gerrits/handlers.go
index f34cfa2a4..015c0fd8f 100644
--- a/cla-backend-go/gerrits/handlers.go
+++ b/cla-backend-go/gerrits/handlers.go
@@ -8,9 +8,9 @@ import (
"strings"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/gerrits"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/gerrits"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/gerrits/models.go b/cla-backend-go/gerrits/models.go
index 82b309aa1..b31689693 100644
--- a/cla-backend-go/gerrits/models.go
+++ b/cla-backend-go/gerrits/models.go
@@ -4,7 +4,7 @@
package gerrits
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/go-openapi/strfmt"
)
diff --git a/cla-backend-go/gerrits/repository.go b/cla-backend-go/gerrits/repository.go
index e053af72e..d9f4ae410 100644
--- a/cla-backend-go/gerrits/repository.go
+++ b/cla-backend-go/gerrits/repository.go
@@ -23,7 +23,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/gerrits/service.go b/cla-backend-go/gerrits/service.go
index 41606da14..3ca90164a 100644
--- a/cla-backend-go/gerrits/service.go
+++ b/cla-backend-go/gerrits/service.go
@@ -20,7 +20,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
diff --git a/cla-backend-go/github/handlers.go b/cla-backend-go/github/handlers.go
index 6c8217829..24784a1a8 100644
--- a/cla-backend-go/github/handlers.go
+++ b/cla-backend-go/github/handlers.go
@@ -14,8 +14,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/user"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- gh "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/github"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ gh "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/github"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/github_organizations/handlers.go b/cla-backend-go/github_organizations/handlers.go
index 85495681c..4f23a1fb6 100644
--- a/cla-backend-go/github_organizations/handlers.go
+++ b/cla-backend-go/github_organizations/handlers.go
@@ -11,9 +11,9 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/github_organizations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/github"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/utils"
diff --git a/cla-backend-go/github_organizations/helpers.go b/cla-backend-go/github_organizations/helpers.go
index 29906cce9..0157ac0bf 100644
--- a/cla-backend-go/github_organizations/helpers.go
+++ b/cla-backend-go/github_organizations/helpers.go
@@ -9,7 +9,7 @@ import (
netURL "net/url"
"sync"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/github"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index 29c6e5215..01c35588c 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -12,7 +12,7 @@ import (
context "context"
reflect "reflect"
- models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
gomock "github.com/golang/mock/gomock"
)
diff --git a/cla-backend-go/github_organizations/models.go b/cla-backend-go/github_organizations/models.go
index 955ee50ee..db3755115 100644
--- a/cla-backend-go/github_organizations/models.go
+++ b/cla-backend-go/github_organizations/models.go
@@ -3,7 +3,7 @@
package github_organizations
-import "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+import "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
// GithubOrganization is data model for github organizations
type GithubOrganization struct {
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 5f3ab77b1..2f6bc5a3c 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -19,7 +19,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index a4224055b..e001d26b9 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -15,7 +15,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
)
diff --git a/cla-backend-go/health/handlers.go b/cla-backend-go/health/handlers.go
index 9fe1f00b3..feba29b42 100644
--- a/cla-backend-go/health/handlers.go
+++ b/cla-backend-go/health/handlers.go
@@ -4,9 +4,9 @@
package health
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/health"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/health"
"github.com/go-openapi/runtime/middleware"
)
diff --git a/cla-backend-go/health/service.go b/cla-backend-go/health/service.go
index c64f97cba..61177d3a5 100644
--- a/cla-backend-go/health/service.go
+++ b/cla-backend-go/health/service.go
@@ -11,7 +11,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
ini "github.com/communitybridge/easycla/cla-backend-go/init"
)
diff --git a/cla-backend-go/project/errors.go b/cla-backend-go/project/errors.go
index 34979b9b8..f62368699 100644
--- a/cla-backend-go/project/errors.go
+++ b/cla-backend-go/project/errors.go
@@ -4,7 +4,7 @@
package project
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
)
// codedResponse interface
diff --git a/cla-backend-go/project/handlers.go b/cla-backend-go/project/handlers.go
index 7799daa9e..6c92040df 100644
--- a/cla-backend-go/project/handlers.go
+++ b/cla-backend-go/project/handlers.go
@@ -18,9 +18,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/project"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/project"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
diff --git a/cla-backend-go/project/helpers.go b/cla-backend-go/project/helpers.go
index 1269ac8c2..e09f3f4eb 100644
--- a/cla-backend-go/project/helpers.go
+++ b/cla-backend-go/project/helpers.go
@@ -12,7 +12,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 79ee2d552..36f839193 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -17,7 +17,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/project"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/project"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/gofrs/uuid"
@@ -27,7 +27,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index 3e98bca4d..551f5136a 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -16,8 +16,8 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/project"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/project"
"github.com/communitybridge/easycla/cla-backend-go/gerrits"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/repositories/handlers.go b/cla-backend-go/repositories/handlers.go
index a25b81ad7..547579555 100644
--- a/cla-backend-go/repositories/handlers.go
+++ b/cla-backend-go/repositories/handlers.go
@@ -8,9 +8,9 @@ import (
"fmt"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/github_repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/github_repositories"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/repositories/mock/mock_repository.go b/cla-backend-go/repositories/mock/mock_repository.go
index 9791f117e..a41593d36 100644
--- a/cla-backend-go/repositories/mock/mock_repository.go
+++ b/cla-backend-go/repositories/mock/mock_repository.go
@@ -12,7 +12,7 @@ import (
context "context"
reflect "reflect"
- models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
gomock "github.com/golang/mock/gomock"
)
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index e5c1a9ce0..4d6e8067f 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -12,7 +12,7 @@ import (
context "context"
reflect "reflect"
- models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
gomock "github.com/golang/mock/gomock"
)
diff --git a/cla-backend-go/repositories/models.go b/cla-backend-go/repositories/models.go
index da8ec82ee..38398f3a4 100644
--- a/cla-backend-go/repositories/models.go
+++ b/cla-backend-go/repositories/models.go
@@ -3,7 +3,7 @@
package repositories
-import "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+import "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
// RepositoryDBModel represent repositories table
type RepositoryDBModel struct {
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index a769d1c1c..33212de84 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -23,7 +23,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index 4edfbcf59..054e14436 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -12,7 +12,7 @@ import (
"github.com/sirupsen/logrus"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/github"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index 21821850e..f5a730b6f 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -11,7 +11,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index c1589bce2..700a20a44 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -11,9 +11,9 @@ import (
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/github"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index e3d8c6468..7476d1b79 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -4,7 +4,7 @@
package signatures
import (
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
)
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index b0f9359aa..eefa0a026 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -23,7 +23,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
@@ -34,7 +34,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/aws/aws-sdk-go/aws"
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 3408a38a1..69d5bb47a 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -21,11 +21,11 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
githubpkg "github.com/google/go-github/v33/github"
"golang.org/x/oauth2"
)
diff --git a/cla-backend-go/template/handlers.go b/cla-backend-go/template/handlers.go
index f3409386a..d01f67b0f 100644
--- a/cla-backend-go/template/handlers.go
+++ b/cla-backend-go/template/handlers.go
@@ -8,9 +8,9 @@ import (
"fmt"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/template"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/template"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/communitybridge/easycla/cla-backend-go/utils"
diff --git a/cla-backend-go/template/repository.go b/cla-backend-go/template/repository.go
index 76c20eaf0..9dc15efed 100644
--- a/cla-backend-go/template/repository.go
+++ b/cla-backend-go/template/repository.go
@@ -20,7 +20,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
)
var (
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index db79189ca..dd2a040f3 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -20,7 +20,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/docraptor"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
diff --git a/cla-backend-go/tests/events_test.go b/cla-backend-go/tests/events_test.go
index 20a8cd365..02d520a56 100644
--- a/cla-backend-go/tests/events_test.go
+++ b/cla-backend-go/tests/events_test.go
@@ -8,7 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/events"
- eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/events"
+ eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events"
"github.com/stretchr/testify/assert"
)
diff --git a/cla-backend-go/tests/project_test.go b/cla-backend-go/tests/project_test.go
index 927f91e69..289c5a702 100644
--- a/cla-backend-go/tests/project_test.go
+++ b/cla-backend-go/tests/project_test.go
@@ -7,7 +7,7 @@ import (
"context"
"testing"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/project"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/stretchr/testify/assert"
diff --git a/cla-backend-go/tests/repository_test.go b/cla-backend-go/tests/repository_test.go
index 2207c4e95..723fb1029 100644
--- a/cla-backend-go/tests/repository_test.go
+++ b/cla-backend-go/tests/repository_test.go
@@ -7,7 +7,7 @@ package tests
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
ini "github.com/communitybridge/easycla/cla-backend-go/init"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
diff --git a/cla-backend-go/userSubscribeLambda/main.go b/cla-backend-go/userSubscribeLambda/main.go
index dec549704..8d3730785 100644
--- a/cla-backend-go/userSubscribeLambda/main.go
+++ b/cla-backend-go/userSubscribeLambda/main.go
@@ -18,7 +18,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/communitybridge/easycla/cla-backend-go/config"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/token"
"github.com/communitybridge/easycla/cla-backend-go/userSubscribeLambda/cmd"
diff --git a/cla-backend-go/users/handlers.go b/cla-backend-go/users/handlers.go
index fb97b3bb1..ee0b84f28 100644
--- a/cla-backend-go/users/handlers.go
+++ b/cla-backend-go/users/handlers.go
@@ -9,9 +9,9 @@ import (
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/users"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/users"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/user"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 1412b91d4..1d633f3c7 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -21,7 +21,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/aws/aws-sdk-go/aws"
diff --git a/cla-backend-go/users/service.go b/cla-backend-go/users/service.go
index d1e17158f..715519965 100644
--- a/cla-backend-go/users/service.go
+++ b/cla-backend-go/users/service.go
@@ -7,7 +7,7 @@ import (
"errors"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/user"
)
diff --git a/cla-backend-go/utils/cla_user.go b/cla-backend-go/utils/cla_user.go
index 5470d1a90..7b5a462e5 100644
--- a/cla-backend-go/utils/cla_user.go
+++ b/cla-backend-go/utils/cla_user.go
@@ -6,7 +6,7 @@ package utils
import (
"strings"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
)
// GetBestUsername gets best username of CLA User
diff --git a/cla-backend-go/utils/responses.go b/cla-backend-go/utils/responses.go
index c8c699a8c..2ecb9452e 100644
--- a/cla-backend-go/utils/responses.go
+++ b/cla-backend-go/utils/responses.go
@@ -6,7 +6,7 @@ package utils
import (
"fmt"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
)
diff --git a/cla-backend-go/utils/signature_utils.go b/cla-backend-go/utils/signature_utils.go
index c75da7a31..9a643f85f 100644
--- a/cla-backend-go/utils/signature_utils.go
+++ b/cla-backend-go/utils/signature_utils.go
@@ -5,7 +5,7 @@ package utils
import (
"github.com/LF-Engineering/lfx-kit/auth"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/sirupsen/logrus"
)
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 91520848e..6afe2ed9c 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -27,8 +27,8 @@ import (
"github.com/jinzhu/copier"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
v1Project "github.com/communitybridge/easycla/cla-backend-go/project"
diff --git a/cla-backend-go/v2/cla_manager/emails.go b/cla-backend-go/v2/cla_manager/emails.go
index b053fc1a6..96828ac0f 100644
--- a/cla-backend-go/v2/cla_manager/emails.go
+++ b/cla-backend-go/v2/cla_manager/emails.go
@@ -11,7 +11,7 @@ import (
v2AcsService "github.com/communitybridge/easycla/cla-backend-go/v2/acs-service"
"github.com/communitybridge/easycla/cla-backend-go/emails"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index c8e6e5a25..1cd59dbf1 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -29,7 +29,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/client/organizations"
v1ClaManager "github.com/communitybridge/easycla/cla-backend-go/cla_manager"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
v1User "github.com/communitybridge/easycla/cla-backend-go/user"
easyCLAUser "github.com/communitybridge/easycla/cla-backend-go/users"
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 63b0547c3..fbd4bd7db 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -27,9 +27,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- v1ProjectParams "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/project"
- v1SignatureParams "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ v1ProjectParams "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/project"
+ v1SignatureParams "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index a0d9a7dfb..06fa65955 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -14,7 +14,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/project"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
diff --git a/cla-backend-go/v2/dynamo_events/autoenable_test.go b/cla-backend-go/v2/dynamo_events/autoenable_test.go
index b92d6a8f1..061408561 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable_test.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable_test.go
@@ -7,7 +7,7 @@ import (
"fmt"
"testing"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
diff --git a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
index a7b25a612..ce536e51b 100644
--- a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
+++ b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
@@ -10,7 +10,7 @@ import (
"sync"
"github.com/aws/aws-sdk-go/aws"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
organizationService "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service"
userService "github.com/communitybridge/easycla/cla-backend-go/v2/user-service"
@@ -18,7 +18,7 @@ import (
"github.com/aws/aws-lambda-go/events"
claEvents "github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
diff --git a/cla-backend-go/v2/events/converters.go b/cla-backend-go/v2/events/converters.go
index 4f0528de8..868026574 100644
--- a/cla-backend-go/v2/events/converters.go
+++ b/cla-backend-go/v2/events/converters.go
@@ -4,7 +4,7 @@
package events
import (
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/jinzhu/copier"
)
diff --git a/cla-backend-go/v2/events/csvResponse.go b/cla-backend-go/v2/events/csvResponse.go
index e937e68ea..12641fab1 100644
--- a/cla-backend-go/v2/events/csvResponse.go
+++ b/cla-backend-go/v2/events/csvResponse.go
@@ -10,7 +10,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
diff --git a/cla-backend-go/v2/events/handlers.go b/cla-backend-go/v2/events/handlers.go
index a5d88af22..da32630e0 100644
--- a/cla-backend-go/v2/events/handlers.go
+++ b/cla-backend-go/v2/events/handlers.go
@@ -21,7 +21,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
v1Events "github.com/communitybridge/easycla/cla-backend-go/events"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/events"
diff --git a/cla-backend-go/v2/gerrits/handlers.go b/cla-backend-go/v2/gerrits/handlers.go
index 7118eb1a1..c0da4a705 100644
--- a/cla-backend-go/v2/gerrits/handlers.go
+++ b/cla-backend-go/v2/gerrits/handlers.go
@@ -16,7 +16,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gerrits"
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index a44c0398e..5cda76b0b 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -17,7 +17,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/v2/dynamo_events"
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index 1c0a623cb..b667ee0eb 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -9,7 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/events"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/repositories/mock"
"github.com/golang/mock/gomock"
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 28981b4af..9a9ea60d6 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -20,7 +20,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
v1GithubOrg "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
v1Repositories "github.com/communitybridge/easycla/cla-backend-go/repositories"
diff --git a/cla-backend-go/v2/project/converters.go b/cla-backend-go/v2/project/converters.go
index 00248dbef..781ec67f2 100644
--- a/cla-backend-go/v2/project/converters.go
+++ b/cla-backend-go/v2/project/converters.go
@@ -4,7 +4,7 @@
package project
import (
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/jinzhu/copier"
)
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 78c0455bf..b25d6a578 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -19,7 +19,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
- v1ProjectOps "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/project"
+ v1ProjectOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/project"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/project"
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 0d1800f69..bf8ba337d 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -19,7 +19,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/github_repositories"
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 837cce2ca..d7b2474ea 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -21,7 +21,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/github"
"github.com/aws/aws-sdk-go/aws"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index 6019dc427..b2ded0a47 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -28,7 +28,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/company"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/utils"
)
diff --git a/cla-backend-go/v2/signatures/converters.go b/cla-backend-go/v2/signatures/converters.go
index 904735a2e..50b4ecbd4 100644
--- a/cla-backend-go/v2/signatures/converters.go
+++ b/cla-backend-go/v2/signatures/converters.go
@@ -7,7 +7,7 @@ import (
"fmt"
"strings"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 2d315fb9a..71efaa668 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -21,14 +21,14 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/project"
"github.com/communitybridge/easycla/cla-backend-go/company"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
- v1Signatures "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/signatures"
+ v1Signatures "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/signatures"
"github.com/communitybridge/easycla/cla-backend-go/github"
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index fc57adb58..45aa39516 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -22,7 +22,7 @@ import (
"github.com/jinzhu/copier"
"github.com/communitybridge/easycla/cla-backend-go/company"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/project"
diff --git a/cla-backend-go/v2/template/handlers.go b/cla-backend-go/v2/template/handlers.go
index 8292e6cd7..55c995e3b 100644
--- a/cla-backend-go/v2/template/handlers.go
+++ b/cla-backend-go/v2/template/handlers.go
@@ -16,7 +16,7 @@ import (
"github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
v1Events "github.com/communitybridge/easycla/cla-backend-go/events"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/models"
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/template"
diff --git a/cla-backend-go/version/handlers.go b/cla-backend-go/version/handlers.go
index 6ca46609e..fcaf6d1c2 100644
--- a/cla-backend-go/version/handlers.go
+++ b/cla-backend-go/version/handlers.go
@@ -5,9 +5,9 @@ package version
import (
"github.com/aws/aws-sdk-go/aws"
- "github.com/communitybridge/easycla/cla-backend-go/gen/models"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations"
- "github.com/communitybridge/easycla/cla-backend-go/gen/restapi/operations/version"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/version"
"github.com/go-openapi/runtime/middleware"
)
From f6f6a30d46b705f63d957c26072f451fcadb81b9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 1 Jul 2021 17:05:20 -0700
Subject: [PATCH 0343/1276] Updated Add/Delete CLA Group Event Log Entries
(#3028)
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 30 ++++++++++++++++++++----
cla-backend-go/v2/cla_groups/handlers.go | 10 ++++----
cla-backend-go/v2/project/handlers.go | 1 +
3 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 70f17f5ce..eec9bcd3f 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -974,7 +974,13 @@ func (ed *ClaManagerAccessRequestDeletedEventData) GetEventDetailsString(args *L
// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Group ID: %s, Name: %s was created", args.ProjectID, args.ProjectName)
+ data := fmt.Sprintf("The CLA group %s was created", args.CLAGroupName)
+ if args.CLAGroupID != "" {
+ data = data + fmt.Sprintf(" with the CLA group ID %s", args.CLAGroupID)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
@@ -1009,7 +1015,13 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("CLA Group ID: %s was deleted", args.ProjectID)
+ data := fmt.Sprintf("The CLA group %s was deleted", args.CLAGroupName)
+ if args.CLAGroupID != "" {
+ data = data + fmt.Sprintf(" with the CLA group ID %s", args.CLAGroupID)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
@@ -1905,7 +1917,14 @@ func (ed *ClaManagerAccessRequestDeletedEventData) GetEventSummaryString(args *L
// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was created by the user %s.", args.ProjectName, args.UserName)
+ data := fmt.Sprintf("The CLA group %s was created", args.CLAGroupName)
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
return data, true
}
@@ -1939,7 +1958,10 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString returns the summary string for this event
func (ed *CLAGroupDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The CLA Group %s was deleted", args.ProjectName)
+ data := fmt.Sprintf("The CLA group %s was deleted", args.CLAGroupName)
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index a1b0110b4..58eb9607d 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -72,10 +72,12 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
- EventType: events.CLAGroupCreated,
- ProjectID: claGroup.ClaGroupID,
- LfUsername: authUser.UserName,
- EventData: &events.CLAGroupCreatedEventData{},
+ EventType: events.CLAGroupCreated,
+ CLAGroupName: claGroup.ClaGroupName,
+ CLAGroupID: claGroup.ClaGroupID,
+ ParentProjectSFID: utils.StringValue(params.ClaGroupInput.FoundationSfid),
+ LfUsername: authUser.UserName,
+ EventData: &events.CLAGroupCreatedEventData{},
})
return cla_group.NewCreateClaGroupOK().WithXRequestID(reqID).WithPayload(claGroup)
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index b25d6a578..9ab981b5b 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -214,6 +214,7 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLAGroupDeleted,
ClaGroupModel: claGroupModel,
+ ProjectSFID: params.ProjectSfdcID,
LfUsername: authUser.UserName,
EventData: &events.CLAGroupDeletedEventData{},
})
From 6a9978f7ac8c9fee9d04e9cc768266733641240a Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 2 Jul 2021 19:53:32 +0300
Subject: [PATCH 0344/1276] [#LFX-3163] Bug/ Signatory Details (#3029)
---
cla-backend-go/v2/company/service.go | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index fbd4bd7db..34aac92a2 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1467,13 +1467,27 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
log.WithFields(f).Warnf("signature : %s have empty signature_acl", sig.SignatureID)
return
}
- lfUsername := sig.SignatureACL[0].LfUsername
- user, err := usc.GetUserByUsername(lfUsername)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to get user with lf username : %s", lfUsername)
- return
+
+ // get cla manager with cla signatory permission
+ orgClient := orgService.GetClient()
+ for _, lfUser := range sig.SignatureACL {
+ log.WithFields(f).Debugf("")
+ hasScope, hasScopeErr := orgClient.IsUserHaveRoleScope(ctx, utils.CLASignatoryRole, lfUser.LfUsername, v1CompanyModel.CompanyExternalID, cg.ProjectSFID)
+ if hasScopeErr != nil {
+ log.WithFields(f).WithError(hasScopeErr).Warnf("unable to check for %s permissions for user: %s ", utils.CLASignatoryRole, lfUser.LfUsername)
+ continue
+ }
+ if hasScope {
+ log.WithFields(f).Debugf("%s has %s permissions", lfUser.LfUsername, utils.CLASignatoryRole)
+ user, err := usc.GetUserByUsername(lfUser.LfUsername)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to get user with lf username : %s", lfUser.LfUsername)
+ return
+ }
+ signatoryName = user.Name
+ break
+ }
}
- signatoryName = user.Name
}()
cwg.Wait()
From e5bc36780af622047fea4bd3ced3bd64156875f4 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 7 Jul 2021 21:43:30 +0300
Subject: [PATCH 0345/1276] LFX-3163 Feature/ Signatory Name
- Removed cla-manager set for signatory name (active cla-list endpoint)
Signed-off-by: Harold Wanyama
---
cla-backend-go/v2/company/service.go | 25 -------------------------
1 file changed, 25 deletions(-)
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 34aac92a2..31be7ed16 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1462,32 +1462,7 @@ func (s *service) fillActiveCLA(ctx context.Context, wg *sync.WaitGroup, sig *v1
signatoryName = sig.SignatoryName
return
}
- usc := v2UserService.GetClient()
- if len(sig.SignatureACL) == 0 {
- log.WithFields(f).Warnf("signature : %s have empty signature_acl", sig.SignatureID)
- return
- }
- // get cla manager with cla signatory permission
- orgClient := orgService.GetClient()
- for _, lfUser := range sig.SignatureACL {
- log.WithFields(f).Debugf("")
- hasScope, hasScopeErr := orgClient.IsUserHaveRoleScope(ctx, utils.CLASignatoryRole, lfUser.LfUsername, v1CompanyModel.CompanyExternalID, cg.ProjectSFID)
- if hasScopeErr != nil {
- log.WithFields(f).WithError(hasScopeErr).Warnf("unable to check for %s permissions for user: %s ", utils.CLASignatoryRole, lfUser.LfUsername)
- continue
- }
- if hasScope {
- log.WithFields(f).Debugf("%s has %s permissions", lfUser.LfUsername, utils.CLASignatoryRole)
- user, err := usc.GetUserByUsername(lfUser.LfUsername)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to get user with lf username : %s", lfUser.LfUsername)
- return
- }
- signatoryName = user.Name
- break
- }
- }
}()
cwg.Wait()
From 7a3c2625975bd7ed7a310a010144e4d63de3f466 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 8 Jul 2021 07:16:10 -0700
Subject: [PATCH 0346/1276] Bump socket.io-parser from 3.3.1 to 3.3.2 in
/cla-frontend-corporate-console (#3024)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 8c8b1f913..bd7cfae77 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -5846,9 +5846,9 @@ socket.io-client@^2.3.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
- integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
+ integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "~1.3.0"
debug "~3.1.0"
From ade9d5ca0c29126156fc3b2bb7a13df846c75d6f Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 9 Jul 2021 02:02:38 +0300
Subject: [PATCH 0347/1276] Bug/ GH Status Update
- Resolved GH status update for a company affiliation use case
Signed-off-by: Harold Wanyama
---
cla-backend/cla/models/github_models.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index 29b14e9cd..6101367b1 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -844,6 +844,7 @@ def handle_commit_from_user(project, commit_sha, author_info, signed, missing):
f'user: {author_username}, '
f'email {author_email}) is on the approved list, '
'but not affiliated with a company')
+
list_author_info.append(True)
break
missing.append((commit_sha, list_author_info))
From daf5b14c941d696c2e34d64070cf39fb908b8f7f Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Fri, 9 Jul 2021 02:09:36 +0300
Subject: [PATCH 0348/1276] Bug/GH Status Update
- Resolved status update for company affiliatio use case
Signed-off-by: Harold Wanyama
---
cla-backend/cla/utils.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 9c7431d34..6d2a60666 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1001,8 +1001,10 @@ def get_comment_body(repository_type, sign_url, signed, missing):
)
else:
if True in commit_hashes:
+ cla.log.info(f"{fn} filter True in commit hash listing: {commit_hashes}")
+ filtered_commits = [commit_hash for commit_hash in commit_hashes if commit_hash != True]
committers_comment += (
- f"
{author} ({' ,'.join(commit_hashes[:-1])}) "
+ f"
{author} ({' ,'.join(filtered_commits)}) "
+ f"is authorized, but they must confirm their affiliation with their company. "
+ f"Start the authorization process "
+ f" by clicking here, click \"Corporate\","
From 020de654d1abd2233d76cc188538b0c37bdd7cc0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 9 Jul 2021 09:28:52 -0700
Subject: [PATCH 0349/1276] Bump socket.io-parser from 3.3.1 to 3.3.2 in
/cla-backend (#3020)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 31b8c1bf3..b2df201fa 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -2005,10 +2005,10 @@ dayjs@^1.10.3:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.3.tgz#cf3357c8e7f508432826371672ebf376cb7d619b"
integrity sha512-/2fdLN987N8Ki7Id8BUN2nhuiRyxTLumQnSQf9CNncFCyqFsSKb9TNhzRYcC8K8eJSJOKvbvkImo/MKKhNi4iw==
-debug@4, debug@^4.0.1, debug@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
- integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
@@ -2026,13 +2026,6 @@ debug@^3.0.1, debug@^3.1.0, debug@^3.1.1:
dependencies:
ms "^2.1.1"
-debug@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
- integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
- dependencies:
- ms "2.1.2"
-
debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -5589,9 +5582,9 @@ socket.io-client@^2.3.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
- integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
+ integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "~1.3.0"
debug "~3.1.0"
From 209a5c096ad94db060b965e0408ea796464ec363 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 9 Jul 2021 20:42:30 -0700
Subject: [PATCH 0350/1276] Bump socket.io-parser from 3.3.1 to 3.3.2 in
/cla-frontend-contributor-console (#3023)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 23 ++++++++--------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index d7e1b8ee0..5592afa90 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -1879,12 +1879,12 @@ debug@3.1.0, debug@~3.1.0:
dependencies:
ms "2.0.0"
-debug@4, debug@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
- integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
- ms "^2.1.1"
+ ms "2.1.2"
debug@^3.0.1, debug@^3.1.0:
version "3.2.6"
@@ -1893,13 +1893,6 @@ debug@^3.0.1, debug@^3.1.0:
dependencies:
ms "^2.1.1"
-debug@^4.0.1, debug@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
- integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
- dependencies:
- ms "2.1.2"
-
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -5890,9 +5883,9 @@ socket.io-client@^2.3.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
- integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
+ integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "~1.3.0"
debug "~3.1.0"
From 46be35f85e323eeb7a269b96017b0d15986fa31a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 11 Jul 2021 10:59:47 -0700
Subject: [PATCH 0351/1276] Bump socket.io-parser from 3.3.1 to 3.3.2 (#3021)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index d323fbb34..7259e5313 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1456,10 +1456,10 @@ dayjs@^1.9.5:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.9.5.tgz#fd49994ebe71639d2ce9575e97186642dfce9808"
integrity sha512-WULIw7UpW/E0y6VywewpbXAMH3d5cZijEhoHLwM+OMVbk/NtchKS/W+57H/0P1rqU7gHrAArjiRLHCUhgMQl6w==
-debug@4, debug@^4.0.1, debug@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
- integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
@@ -1477,13 +1477,6 @@ debug@^3.1.0:
dependencies:
ms "^2.1.1"
-debug@^4.1.0:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
- integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
- dependencies:
- ms "2.1.2"
-
debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -4278,9 +4271,9 @@ socket.io-client@^2.3.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
- integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
+ integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "~1.3.0"
debug "~3.1.0"
From 332dd4b63d630ccf17ad06a5137e5c7c97bba931 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 11 Jul 2021 21:58:18 -0700
Subject: [PATCH 0352/1276] Bump socket.io-parser from 3.3.1 to 3.3.2 in
/cla-backend-go (#3022)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index 678865e5d..69ac0d3b1 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -1588,10 +1588,10 @@ dayjs@^1.10.3:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.3.tgz#cf3357c8e7f508432826371672ebf376cb7d619b"
integrity sha512-/2fdLN987N8Ki7Id8BUN2nhuiRyxTLumQnSQf9CNncFCyqFsSKb9TNhzRYcC8K8eJSJOKvbvkImo/MKKhNi4iw==
-debug@4, debug@^4.0.1, debug@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
- integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
@@ -1609,13 +1609,6 @@ debug@^3.0.1, debug@^3.1.0:
dependencies:
ms "^2.1.1"
-debug@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
- integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
- dependencies:
- ms "2.1.2"
-
debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -4504,9 +4497,9 @@ socket.io-client@^2.3.0:
to-array "0.1.4"
socket.io-parser@~3.3.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
- integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6"
+ integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==
dependencies:
component-emitter "~1.3.0"
debug "~3.1.0"
From cdcaf5a1ebc808af002644f901cbd8c26b74c1dd Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Wed, 21 Jul 2021 03:41:14 +0300
Subject: [PATCH 0353/1276] [Snyk] Security upgrade networkx from 2.2 to 2.6
(#3034)
---
scripts/audits/requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/audits/requirements.txt b/scripts/audits/requirements.txt
index 7b4a7134b..c876de3bf 100644
--- a/scripts/audits/requirements.txt
+++ b/scripts/audits/requirements.txt
@@ -5,3 +5,4 @@ pylint==2.4.3
pytest==5.3.1
moto==1.3.14
+networkx>=2.6 # not directly required, pinned by Snyk to avoid a vulnerability
From 7509c28a772eb18064c777a517d6cf1311e23475 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 20 Jul 2021 22:34:43 -0500
Subject: [PATCH 0354/1276] Project Service - Use Foundation ID for Parent
(#3037)
- Updated logic to use/leverage the project model foundation ID for the
parent ID rather than using the Parent attribute. Added additional
defensive logic to projects with no parents. Resolved null pointer error
when no parent is present.
- Added helper function for determining if a projec has a parent or not
- Added helper function for getting the parent ID (safe method)
Signed-off-by: David Deal
---
cla-backend-go/events/service.go | 12 +--
cla-backend-go/tests/project_helpers_test.go | 16 +---
cla-backend-go/utils/project_helpers.go | 17 +++-
cla-backend-go/v2/cla_groups/handlers.go | 14 +--
cla-backend-go/v2/cla_groups/helpers.go | 48 ++++++----
cla-backend-go/v2/cla_groups/service.go | 9 +-
cla-backend-go/v2/company/service.go | 5 +-
.../v2/github_organizations/service.go | 13 +--
cla-backend-go/v2/project-service/client.go | 87 ++++++++++---------
cla-backend-go/v2/project/handlers.go | 31 ++++---
cla-backend-go/v2/repositories/service.go | 9 +-
cla-backend-go/v2/sign/service.go | 3 +-
12 files changed, 143 insertions(+), 121 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 0ad22ba99..f3e9241aa 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -255,11 +255,11 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
args.ProjectName = project.Name
// Try to load and set the parent information
- if utils.StringValue(project.Parent) != "" {
- log.WithFields(f).Debugf("loading salesforce project parent by ID: %s...", utils.StringValue(project.Parent))
- parentProject, parentProjectErr := project_service.GetClient().GetProject(utils.StringValue(project.Parent))
+ if project.Foundation != nil && project.Foundation.ID != "" {
+ log.WithFields(f).Debugf("loading salesforce project parent by ID: %s...", project.Foundation.ID)
+ parentProject, parentProjectErr := project_service.GetClient().GetProject(project.Foundation.ID)
if parentProjectErr != nil || parentProject == nil {
- log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", utils.StringValue(project.Parent))
+ log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", project.Foundation.ID)
return nil
}
var parentProjectName, parentProjectID string
@@ -271,7 +271,7 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
parentProjectID = project.ID
}
log.WithFields(f).Debugf("loaded salesforce project by parent ID: %s - resulting in ID: %s with name: %s",
- utils.StringValue(project.Parent), parentProjectID, parentProjectName)
+ project.Foundation.ID, parentProjectID, parentProjectName)
args.ParentProjectSFID = parentProjectID
args.ParentProjectName = parentProjectName
} else if project.Foundation != nil {
@@ -293,7 +293,7 @@ func (s *service) loadLFUser(ctx context.Context, args *LogEventArgs) error {
}
if args == nil {
- return errors.New(("unable to load lf user data - args is nil"))
+ return errors.New("unable to load lf user data - args is nil")
}
if args.LfUsername != "" {
diff --git a/cla-backend-go/tests/project_helpers_test.go b/cla-backend-go/tests/project_helpers_test.go
index eb45c05ad..8985bf3df 100644
--- a/cla-backend-go/tests/project_helpers_test.go
+++ b/cla-backend-go/tests/project_helpers_test.go
@@ -13,21 +13,18 @@ import (
)
const (
- testProjectParentID = "abc1234"
- testProjectID = "def13456"
- testProjectLogo = "testlogurl.com"
+ testProjectID = "def13456"
+ testProjectLogo = "testlogurl.com"
)
func TestIsProjectHasRootParentNoParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef("")
project.Foundation = nil
assert.True(t, utils.IsProjectHasRootParent(project), "Project Has Root Parent - Empty Parent")
}
func TestIsProjectHasRootParentLF(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -38,7 +35,6 @@ func TestIsProjectHasRootParentLF(t *testing.T) {
func TestIsProjectHasRootParentLFProjectsLLC(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -49,7 +45,6 @@ func TestIsProjectHasRootParentLFProjectsLLC(t *testing.T) {
func TestIsProjectHasRootParentNonLF(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -60,7 +55,6 @@ func TestIsProjectHasRootParentNonLF(t *testing.T) {
func TestIsStandaloneProject(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef("")
project.Foundation = nil
project.Projects = []*models.ProjectOutput{}
assert.True(t, utils.IsStandaloneProject(project), "Standalone Project with No Parent with No Children")
@@ -68,7 +62,6 @@ func TestIsStandaloneProject(t *testing.T) {
func TestLFParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -80,7 +73,6 @@ func TestLFParent(t *testing.T) {
func TestLFProjectsLLCParent(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -92,7 +84,6 @@ func TestLFProjectsLLCParent(t *testing.T) {
func TestLFParentWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -104,7 +95,6 @@ func TestLFParentWithChildren(t *testing.T) {
func TestLFProjectsLLCParentWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = &models.Foundation{
ID: testProjectID,
LogoURL: testProjectLogo,
@@ -140,7 +130,6 @@ func TestLFProjectsLLCParentWithChildren(t *testing.T) {
func TestIsProjectHaveChildrenNoChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = nil
project.Projects = []*models.ProjectOutput{}
assert.False(t, utils.IsProjectHaveChildren(project), "Project has no children")
@@ -148,7 +137,6 @@ func TestIsProjectHaveChildrenNoChildren(t *testing.T) {
func TestIsProjectHaveChildrenWithChildren(t *testing.T) {
project := &models.ProjectOutputDetailed{}
- project.Parent = utils.StringRef(testProjectParentID)
project.Foundation = nil
child := &models.ProjectOutput{
ProjectCommon: models.ProjectCommon{},
diff --git a/cla-backend-go/utils/project_helpers.go b/cla-backend-go/utils/project_helpers.go
index 6cffde169..066b4c622 100644
--- a/cla-backend-go/utils/project_helpers.go
+++ b/cla-backend-go/utils/project_helpers.go
@@ -5,15 +5,28 @@ package utils
import "github.com/communitybridge/easycla/cla-backend-go/v2/project-service/models"
+// GetProjectParentSFID returns the project parent SFID if available, otherwise returns empty string
+func GetProjectParentSFID(project *models.ProjectOutputDetailed) string {
+ if project == nil || project.Foundation == nil || project.Foundation.ID == "" {
+ return ""
+ }
+ return project.Foundation.ID
+}
+
+// IsProjectHaveParent returns true if the specified project has a parent
+func IsProjectHaveParent(project *models.ProjectOutputDetailed) bool {
+ return project != nil && project.Foundation != nil && project.Foundation.ID != "" && project.Foundation.Name != ""
+}
+
// IsProjectHasRootParent determines if the a given project has a root parent. A root parent is a parent that is empty parent or the parent is TLF or LFProjects
func IsProjectHasRootParent(project *models.ProjectOutputDetailed) bool {
- return StringValue(project.Parent) == "" || (project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
+ return project.Foundation == nil || (project.Foundation != nil && project.Foundation.ID != "" && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
}
// IsStandaloneProject determines if a given project is a standalone project. A standalone project is a project with no parent or the parent is TLF/LFProjects and does not have any children
func IsStandaloneProject(project *models.ProjectOutputDetailed) bool {
// standalone: No parent or parent is TLF/LFProjects....and no children
- return (StringValue(project.Parent) == "" ||
+ return (project.Foundation == nil ||
(project.Foundation != nil && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))) &&
len(project.Projects) == 0
}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 58eb9607d..bca28d288 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -308,15 +308,15 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
var parentProject *v2ProjectServiceModels.ProjectOutputDetailed
// Handle the ONAP edge case
- if utils.StringValue(project.Parent) != "" {
- parentProject, projectErr = psc.GetProject(utils.StringValue(project.Parent))
+ if utils.IsProjectHaveParent(project) {
+ parentProject, projectErr = psc.GetProject(utils.GetProjectParentSFID(project))
if parentProject == nil || projectErr != nil {
- msg := fmt.Sprintf("Failed to get parent: %s", utils.StringValue(project.Parent))
+ msg := fmt.Sprintf("Failed to get parent: %s", utils.GetProjectParentSFID(project))
log.WithFields(f).Warnf(msg)
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
}
- if (utils.StringValue(project.Parent) != "" && !utils.IsProjectCategory(project, parentProject)) || (utils.IsProjectHasRootParent(project) && project.ProjectType == utils.ProjectTypeProjectGroup) {
+ if (utils.IsProjectHaveParent(project) && !utils.IsProjectCategory(project, parentProject)) || (utils.IsProjectHasRootParent(project) && project.ProjectType == utils.ProjectTypeProjectGroup) {
msg := fmt.Sprintf("Unable to enroll salesforce foundation project: %s in project level cla-group.", projectSFID)
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
@@ -424,9 +424,9 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
log.WithFields(f).Debug("found project - evaluating parent...")
var projectSFIDs []string
// Add the foundation ID, if available
- if project.Foundation != nil && project.Foundation.ID != "" {
- log.WithFields(f).Debugf("parent project - found %s - adding to list of project IDs...", project.Foundation.ID)
- projectSFIDs = append(projectSFIDs, project.Foundation.ID)
+ if utils.IsProjectHaveParent(project) {
+ log.WithFields(f).Debugf("parent project - found %s - adding to list of project IDs...", utils.GetProjectParentSFID(project))
+ projectSFIDs = append(projectSFIDs, utils.GetProjectParentSFID(project))
}
log.WithFields(f).Debug("project - adding to list of project IDs...")
projectSFIDs = append(projectSFIDs, project.ID)
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index cf27d3867..3fc5916d5 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -159,16 +159,19 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(utils.StringValue(foundationProjectDetails.Parent))
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
- return false, err
+ isLFParent := false
+ if utils.IsProjectHaveParent(foundationProjectDetails) {
+ isLFParent, err = psc.IsTheLinuxFoundation(foundationProjectDetails.Foundation.ID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup parent project by SFID: %s", foundationProjectDetails.Foundation.ID)
+ return false, err
+ }
}
// If the foundation details in the platform project service indicates that this foundation has no parent or no
// children/sub-project... (stand alone project situation)
log.WithFields(f).Debug("checking to see if we have a standalone project...")
- if (utils.StringValue(foundationProjectDetails.Parent) == "" || isLFParent) && len(foundationProjectDetails.Projects) == 0 {
+ if isLFParent && len(foundationProjectDetails.Projects) == 0 {
log.WithFields(f).Debug("we have a standalone project...")
// Did the user actually pass in any projects? If none - add the foundation ID to the list and return to
// indicate it is a "standalone project"
@@ -218,15 +221,21 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
// fetch the foundation model details from the platform project service which includes a list of its sub projects
foundationProjectDetails, err := psc.GetProject(foundationSFID)
if err != nil {
- log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
+ log.WithFields(f).WithError(err).Warnf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
return err
}
+ if foundationProjectDetails == nil {
+ return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ }
foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
if err != nil {
- log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
+ log.WithFields(f).WithError(err).Warnf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
return err
}
+ if foundationProjectSummary == nil {
+ return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ }
// build Tree that tracks parent and child projects
projectTree := buildProjectNode(foundationProjectSummary)
@@ -251,7 +260,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
if projectTree != nil && projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- utils.StringValue(foundationProjectDetails.Parent), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
+ foundationProjectDetails.Foundation.ID, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
@@ -316,25 +325,32 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
// fetch the foundation model details from the platform project service which includes a list of its sub projects
foundationProjectDetails, err := psc.GetProject(foundationSFID)
if err != nil {
- log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
return err
}
+ if foundationProjectDetails == nil {
+ return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ }
foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
if err != nil {
- log.WithFields(f).Warnf("validation failure - problem fetching project details from project service, error: %+v", err)
return err
}
+ if foundationProjectSummary == nil {
+ return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ }
// build Tree that tracks parent and child projects
projectTree := buildProjectNode(foundationProjectSummary)
// Is our parent the LF project?
log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent, err := psc.IsTheLinuxFoundation(utils.StringValue(foundationProjectDetails.Parent))
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup %s or %s project", utils.TheLinuxFoundation, utils.LFProjectsLLC)
- return err
+ isLFParent := false
+ if utils.IsProjectHaveParent(foundationProjectDetails) {
+ isLFParent, err = psc.IsTheLinuxFoundation(foundationProjectDetails.Foundation.ID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup parent project by SFID: %s", foundationProjectDetails.Foundation.ID)
+ return err
+ }
}
for _, projectSFID := range projectSFIDList {
@@ -343,9 +359,9 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
- if utils.StringValue(foundationProjectDetails.Parent) != "" && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
+ if !isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- utils.StringValue(foundationProjectDetails.Parent), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
+ foundationProjectDetails.Foundation.ID, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 6afe2ed9c..3ffaffd02 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -386,7 +386,8 @@ func (s *service) ListClaGroupsForFoundationOrProject(ctx context.Context, proje
var parentDetails *v2ProjectServiceModels.ProjectOutputDetailed
var parentDetailErr error
- if utils.StringValue(sfProjectModelDetails.Parent) != "" {
+ // If we have a parent...
+ if utils.IsProjectHaveParent(sfProjectModelDetails) {
var parentSFID string
// Use utility function that considers TLF and LF Projects, LLC
parentSFID, parentDetailErr = v2ProjectService.GetClient().GetParentProject(projectOrFoundationSFID)
@@ -646,9 +647,9 @@ func (s *service) appendCLAGroupsForProject(ctx context.Context, f logrus.Fields
// Since this is a project and not a foundation, we'll want to set he parent foundation ID and name (which is
// our parent in this case)
var foundationID, foundationName string
- if sfProjectModelDetails.ProjectOutput.Foundation != nil {
- foundationID = sfProjectModelDetails.ProjectOutput.Foundation.ID
- foundationName = sfProjectModelDetails.ProjectOutput.Foundation.Name
+ if utils.IsProjectHaveParent(sfProjectModelDetails) {
+ foundationID = sfProjectModelDetails.Foundation.ID
+ foundationName = sfProjectModelDetails.Foundation.Name
log.WithFields(f).Debugf("using parent foundation ID: %s and name: %s", foundationID, foundationName)
} else {
// Project with no parent - must be a standalone - use our ID and Name as the foundation
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 31be7ed16..127403fba 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1144,6 +1144,9 @@ func (s *service) GetCompanyCLAGroupManagers(ctx context.Context, companyID, cla
func v2ProjectToMap(projectDetails *v2ProjectServiceModels.ProjectOutputDetailed) (map[string]*v2ProjectServiceModels.ProjectOutput, error) {
epmap := make(map[string]*v2ProjectServiceModels.ProjectOutput) // key project_sfid
+ if projectDetails == nil {
+ return epmap, nil
+ }
var pr v2ProjectServiceModels.ProjectOutput
err := copier.Copy(&pr, projectDetails)
if err != nil {
@@ -1212,7 +1215,7 @@ func (s *service) getCLAGroupsUnderProjectOrFoundation(ctx context.Context, proj
log.WithFields(f).WithError(err).Warnf("unable to get project IDs for CLA Group: %s", projectMapping.ClaGroupID)
return nil, err
}
- if len(allProjectMapping) > 1 {
+ if len(allProjectMapping) > 1 && projectDetails.Foundation != nil && projectDetails.Foundation.ID != "" {
// reload data in projectDetails for all projects of foundation
projectDetails, err = psc.GetProject(projectDetails.Foundation.ID)
if err != nil {
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 9a9ea60d6..114cd8c3e 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -92,18 +92,19 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
psc := v2ProjectService.GetClient()
log.WithFields(f).Debug("loading project details from the project service...")
- projectServiceRecord, err := psc.GetProject(projectSFID)
+ project, err := psc.GetProject(projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading project details from the project service")
return nil, err
}
var parentProjectSFID string
- if utils.IsProjectHasRootParent(projectServiceRecord) {
+ if !utils.IsProjectHaveParent(project) || utils.IsProjectHasRootParent(project) || utils.GetProjectParentSFID(project) == "" {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = utils.StringValue(projectServiceRecord.Parent)
+ parentProjectSFID = utils.GetProjectParentSFID(project)
}
+
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
@@ -290,12 +291,12 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if !utils.IsProjectHaveParent(project) || utils.IsProjectHasRootParent(project) || utils.GetProjectParentSFID(project) == "" {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = utils.StringValue(project.Parent)
+ parentProjectSFID = utils.GetProjectParentSFID(project)
}
+
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 9d25f680a..55b054923 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -141,34 +141,18 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
"apiGWHost": apiGWHost,
}
- // Lookup in cache first
- existingModel, exists := projectServiceModels[projectSFID]
- if exists {
- log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
- return utils.StringValue(existingModel.Parent), nil
- }
- log.WithFields(f).Debugf("cache miss - cache size: %d", len(projectServiceModels))
-
- log.WithFields(f).Debug("looking up projectModel in SF by projectSFID")
- projectModel, err := pmm.GetProject(projectSFID)
+ // Use our helper function to lookup the parent, if it exists
+ parentModel, err := pmm.GetParentProjectModel(projectSFID)
if err != nil {
- log.WithFields(f).Warnf("unable to lookup projectModel in projectModel service by projectSFID, error: %+v", err)
- return "", err
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
+ return projectSFID, err
}
-
- // Update our cache for next time
- projectServiceModels[projectSFID] = projectModel
- log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
-
- // Do they have a parent?
- if utils.StringValue(projectModel.Parent) == "" || (projectModel.Foundation != nil &&
- (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
- log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
- return projectSFID, nil
+ if parentModel == nil || parentModel.Foundation == nil || parentModel.Foundation.ID == "" {
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
+ return projectSFID, err
}
- log.WithFields(f).Debugf("returning parent projectSFID: %s", utils.StringValue(projectModel.Parent))
- return utils.StringValue(projectModel.Parent), nil
+ return parentModel.Foundation.ID, nil
}
// GetParentProjectModel returns the parent project model if there is a parent, otherwise returns nil
@@ -189,21 +173,30 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
- // Parent in the cache?
- existingParentModel, exists = projectServiceModels[utils.StringValue(existingModel.Parent)]
+ // Does this project they have a parent? projectModel.Parent is deprecated and no longer returned, use project.Foundation.ID/Name attribute instead
+ if existingModel.Foundation != nil && existingModel.Foundation.ID != "" && (existingModel.Foundation.Name == utils.TheLinuxFoundation || existingModel.Foundation.Name == utils.LFProjectsLLC) {
+ log.WithFields(f).Debugf("no parent for projectSFID %s or %s or %s is the parent...", projectSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC)
+ return nil, nil
+ }
+
+ // Grab the parent ID once
+ projectParentSFID := existingModel.Foundation.ID
+
+ // Parent SFID in the cache?
+ existingParentModel, exists = projectServiceModels[projectParentSFID]
if exists {
return existingParentModel, nil
}
// Parent project not in the cache - lookup
- parentProjectModel, err := pmm.GetProject(utils.StringValue(existingModel.Parent))
+ parentProjectModel, err := pmm.GetProject(projectParentSFID)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", utils.StringValue(existingModel.Parent))
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel with projectSFID: '%s'", projectParentSFID)
return nil, err
}
- // Update our cache for next time
- projectServiceModels[utils.StringValue(existingModel.Parent)] = parentProjectModel
+ // Save/Update our cache for next time
+ projectServiceModels[projectParentSFID] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
return parentProjectModel, nil
@@ -215,33 +208,43 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
log.WithFields(f).Warnf("unable to lookup projectModel in projectModel service by projectSFID, error: %+v", err)
return nil, err
}
+ if projectModel == nil {
+ return nil, nil
+ }
- // Update our cache for next time
+ // Save/Update our cache for next time
projectServiceModels[projectSFID] = projectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
- // Do they have a parent?
- if utils.StringValue(projectModel.Parent) == "" || (projectModel.Foundation != nil &&
- (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC)) {
+ // No parent
+ if projectModel.Foundation == nil || projectModel.Foundation.ID == "" {
+ return nil, nil
+ }
+
+ // Do they have a parent? projectModel.Parent is deprecated and no longer returned
+ if projectModel.Foundation != nil && projectModel.Foundation.ID != "" && (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC) {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return nil, nil
}
+ // Grab the parent ID once
+ projectParentSFID := projectModel.Foundation.ID
+
// Parent in the cache?
- existingParentModel, exists = projectServiceModels[utils.StringValue(projectModel.Parent)]
+ existingParentModel, exists = projectServiceModels[projectParentSFID]
if exists {
return existingParentModel, nil
}
// Parent project not in the cache - lookup
- parentProjectModel, err := pmm.GetProject(utils.StringValue(projectModel.Parent))
+ parentProjectModel, err := pmm.GetProject(projectParentSFID)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel projectSFID: '%s'", utils.StringValue(projectModel.Parent))
+ log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel with projectSFID: '%s'", projectParentSFID)
return nil, err
}
- // Update our cache for next time
- projectServiceModels[utils.StringValue(existingModel.Parent)] = parentProjectModel
+ // Save/Update our cache for next time
+ projectServiceModels[projectParentSFID] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
return parentProjectModel, nil
@@ -286,13 +289,13 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
return false, err
}
- if utils.StringValue(projectModel.Parent) == "" {
+ if projectModel.Foundation == nil || projectModel.Foundation.ID == "" {
return false, nil
}
- parentProjectModel, err := pmm.GetProject(utils.StringValue(projectModel.Parent))
+ parentProjectModel, err := pmm.GetProject(projectModel.Foundation.ID)
if err != nil {
- log.WithFields(f).Warnf("unable to lookup parent project by ID: %s error: %+v", utils.StringValue(projectModel.Parent), err)
+ log.WithFields(f).Warnf("unable to lookup parent project by ID: %s error: %+v", projectModel.Foundation.ID, err)
return false, err
}
diff --git a/cla-backend-go/v2/project/handlers.go b/cla-backend-go/v2/project/handlers.go
index 9ab981b5b..b03df617b 100644
--- a/cla-backend-go/v2/project/handlers.go
+++ b/cla-backend-go/v2/project/handlers.go
@@ -317,10 +317,10 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
// Lookup the parent info, if it's available
var parentName string
- if utils.StringValue(sfProject.Parent) != "" {
- sfParentProject, err := psc.GetProject(utils.StringValue(sfProject.Parent))
+ if utils.IsProjectHaveParent(sfProject) {
+ sfParentProject, err := psc.GetProject(utils.GetProjectParentSFID(sfProject))
if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to load parant project by ID: %s", utils.StringValue(sfProject.Parent))
+ log.WithFields(f).WithError(err).Warnf("unable to load parant project by ID: %s", utils.GetProjectParentSFID(sfProject))
}
if sfParentProject != nil {
@@ -335,18 +335,17 @@ func Configure(api *operations.EasyclaAPI, service v1Project.Service, v2Service
func buildSFProjectSummary(sfProject *v2ProjectServiceModels.ProjectOutputDetailed, parentName string) *models.SfProjectSummary {
return &models.SfProjectSummary{
- EntityName: utils.StringValue(sfProject.EntityName),
- EntityType: sfProject.EntityType,
- Funding: sfProject.Funding,
- ID: sfProject.ID,
- LfSupported: sfProject.LFSponsored,
- Name: sfProject.Name,
- ParentID: utils.StringValue(sfProject.Parent),
- ParentName: parentName,
- Slug: sfProject.Slug,
- Status: sfProject.Status,
- Type: sfProject.Type,
- IsStandalone: (sfProject.Type != utils.ProjectTypeProjectGroup) && (utils.StringValue(sfProject.Parent) == "" || (sfProject.Foundation != nil &&
- (sfProject.Foundation.Name == utils.TheLinuxFoundation || sfProject.Foundation.Name == utils.LFProjectsLLC))),
+ EntityName: utils.StringValue(sfProject.EntityName),
+ EntityType: sfProject.EntityType,
+ Funding: sfProject.Funding,
+ ID: sfProject.ID,
+ LfSupported: sfProject.LFSponsored,
+ Name: sfProject.Name,
+ ParentID: utils.GetProjectParentSFID(sfProject),
+ ParentName: parentName,
+ Slug: sfProject.Slug,
+ Status: sfProject.Status,
+ Type: sfProject.Type,
+ IsStandalone: utils.IsStandaloneProject(sfProject),
}
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index d7b2474ea..9e0f95264 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -90,11 +90,10 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
}
var parentProjectSFID string
- if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if !utils.IsProjectHaveParent(project) || utils.IsProjectHasRootParent(project) || utils.GetProjectParentSFID(project) == "" {
parentProjectSFID = projectSFID
} else {
- parentProjectSFID = utils.StringValue(project.Parent)
+ parentProjectSFID = utils.GetProjectParentSFID(project)
}
allMappings, err := s.projectsClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, aws.StringValue(input.ClaGroupID))
@@ -252,8 +251,8 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
return nil, err
}
f["projectName"] = projectModel.Name
- if utils.StringValue(projectModel.Parent) != "" {
- f["projectParentSFID"] = projectModel.Parent
+ if utils.IsProjectHaveParent(projectModel) {
+ f["projectParentSFID"] = utils.GetProjectParentSFID(projectModel)
}
log.WithFields(f).Debug("loaded project from the project service")
enabled := true
diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go
index b2ded0a47..7d671f107 100644
--- a/cla-backend-go/v2/sign/service.go
+++ b/cla-backend-go/v2/sign/service.go
@@ -173,8 +173,7 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri
}
var claGroupID string
- if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ if !utils.IsProjectHaveParent(project) || utils.IsProjectHasRootParent(project) || utils.GetProjectParentSFID(project) == "" {
// this is root project
cgmlist, perr := s.projectClaGroupsRepo.GetProjectsIdsForFoundation(ctx, utils.StringValue(input.ProjectSfid))
if perr != nil {
From a1e23a5feac28a4649a1f81ae30ab8359e6ee840 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Wed, 21 Jul 2021 17:33:26 +0300
Subject: [PATCH 0355/1276] [Snyk] Security upgrade networkx from 2.2 to 2.6
(#3033)
---
scripts/signature-audits/requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/signature-audits/requirements.txt b/scripts/signature-audits/requirements.txt
index b386d24c1..6f8f0e7fa 100644
--- a/scripts/signature-audits/requirements.txt
+++ b/scripts/signature-audits/requirements.txt
@@ -11,3 +11,4 @@ oauthlib==3.1.0
requests==2.22.0
requests-oauthlib==1.2.0
moto==1.3.14
+networkx>=2.6 # not directly required, pinned by Snyk to avoid a vulnerability
From a34614f3fbd63f798ad3ce6865f3de627d259168 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 10:19:46 -0500
Subject: [PATCH 0356/1276] Resolved a number of Node Library Vulnerabilities
(#3038)
---
cla-backend-go/package.json | 4 +-
cla-backend-go/yarn.lock | 70 +------
cla-backend/package.json | 4 +-
cla-backend/yarn.lock | 70 +------
cla-frontend-contributor-console/package.json | 6 +-
cla-frontend-contributor-console/yarn.lock | 182 ++----------------
cla-frontend-corporate-console/package.json | 6 +-
cla-frontend-corporate-console/yarn.lock | 182 ++----------------
cla-frontend-project-console/package.json | 6 +-
cla-frontend-project-console/yarn.lock | 182 ++----------------
package.json | 4 +-
yarn.lock | 70 +------
12 files changed, 90 insertions(+), 696 deletions(-)
diff --git a/cla-backend-go/package.json b/cla-backend-go/package.json
index e854a40bd..d58f86e95 100644
--- a/cla-backend-go/package.json
+++ b/cla-backend-go/package.json
@@ -28,6 +28,8 @@
},
"resolutions": {
"axios": "^0.21.1",
- "ini": "^1.3.7"
+ "ini": "^1.3.7",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index 69ac0d3b1..ffa8b1db2 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -886,11 +886,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-async-limiter@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
- integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-
async@^2.6.1, async@^2.6.2, async@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -1616,11 +1611,6 @@ debug@~3.1.0:
dependencies:
ms "2.0.0"
-decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
@@ -3522,19 +3512,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npmlog@^4.0.1:
version "4.1.2"
@@ -3981,15 +3962,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -4519,13 +4491,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
source-map-support@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -4605,11 +4570,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -5154,24 +5114,10 @@ write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
- integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xml2js@0.4.19:
version "0.4.19"
diff --git a/cla-backend/package.json b/cla-backend/package.json
index 30ea00235..fb13cec78 100644
--- a/cla-backend/package.json
+++ b/cla-backend/package.json
@@ -47,6 +47,8 @@
"resolutions": {
"axios": "^0.21.1",
"ini": "^1.3.7",
- "node-fetch": "^2.6.1"
+ "node-fetch": "^2.6.1",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index b2df201fa..6a2f8a6dc 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -1183,11 +1183,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-async-limiter@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
- integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-
async@^2.6.1, async@^2.6.2, async@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -2038,11 +2033,6 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
-decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
@@ -4354,19 +4344,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npm-run-path@^4.0.0:
version "4.0.1"
@@ -4938,15 +4919,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -5604,13 +5576,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
sorted-array-functions@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5"
@@ -5695,11 +5660,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -6327,24 +6287,10 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
- integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xdg-basedir@^4.0.0:
version "4.0.0"
diff --git a/cla-frontend-contributor-console/package.json b/cla-frontend-contributor-console/package.json
index e55171d2f..e35d008de 100644
--- a/cla-frontend-contributor-console/package.json
+++ b/cla-frontend-contributor-console/package.json
@@ -24,6 +24,10 @@
"resolutions": {
"axios": "^0.21.1",
"bl": "^2.2.1",
- "ini": "^1.3.7"
+ "braces": "^2.3.1",
+ "glob-parent": "^5.1.2",
+ "ini": "^1.3.7",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 5592afa90..3d89c7430 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -1003,11 +1003,6 @@ async-each@^1.0.0:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
-async-limiter@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
- integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-
async@^2.0.0, async@^2.6.1, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -1223,16 +1218,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
-braces@^2.3.1:
+braces@^1.8.2, braces@^2.3.1, braces@^3.0.1, braces@~3.0.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
@@ -1248,13 +1234,6 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@^3.0.1, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@@ -2402,13 +2381,6 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
- dependencies:
- fill-range "^2.1.0"
-
expand-template@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
@@ -2672,17 +2644,6 @@ filesize@^6.1.0:
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==
-fill-range@^2.1.0:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
- integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^3.0.0"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -2693,13 +2654,6 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -2973,17 +2927,10 @@ glob-base@^0.3.0:
glob-parent "^2.0.0"
is-glob "^2.0.0"
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
- dependencies:
- is-glob "^2.0.0"
-
-glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+glob-parent@^2.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
@@ -3594,13 +3541,6 @@ is-natural-number@^4.0.1:
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -3608,21 +3548,11 @@ is-number@^3.0.0:
dependencies:
kind-of "^3.0.2"
-is-number@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
- integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
-
is-number@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-6.0.0.tgz#e6d15ad31fc262887cccf217ae5f9316f81b1995"
integrity sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg==
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
is-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
@@ -4196,11 +4126,6 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-math-random@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
- integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
-
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4549,19 +4474,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npmlog@^4.0.1:
version "4.1.2"
@@ -4935,11 +4851,6 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
- integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
-
prettyoutput@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/prettyoutput/-/prettyoutput-1.2.0.tgz#fef93f2a79c032880cddfb84308e2137e3674b22"
@@ -5139,15 +5050,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -5182,15 +5084,6 @@ ramda@^0.27.1:
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
-randomatic@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
- integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
- dependencies:
- is-number "^4.0.0"
- kind-of "^6.0.0"
- math-random "^1.0.1"
-
randomstring@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/randomstring/-/randomstring-1.1.5.tgz#6df0628f75cbd5932930d9fe3ab4e956a18518c3"
@@ -5365,7 +5258,7 @@ repeat-element@^1.1.2:
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@@ -5921,13 +5814,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
source-map-resolve@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -6045,11 +5931,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-template@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
@@ -6368,13 +6249,6 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -6496,11 +6370,6 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-ultron@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
- integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
-
unbzip2-stream@^1.0.9:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
@@ -6760,33 +6629,10 @@ write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^3.2.0:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
- integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
- dependencies:
- async-limiter "~1.0.0"
- safe-buffer "~5.1.0"
- ultron "~1.1.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
- integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^3.2.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xml2js@0.4.19:
version "0.4.19"
diff --git a/cla-frontend-corporate-console/package.json b/cla-frontend-corporate-console/package.json
index ff305e0af..6ee28808e 100644
--- a/cla-frontend-corporate-console/package.json
+++ b/cla-frontend-corporate-console/package.json
@@ -24,10 +24,14 @@
"resolutions": {
"axios": "^0.21.1",
"bl": "^1.2.3",
+ "braces": "^2.3.1",
"http-proxy": "^1.18.1",
"ini": "^1.3.7",
+ "glob-parent": "^5.1.2",
"kind-of": "^6.0.3",
"lodash": "^4.17.19",
- "minimist": "^1.2.3"
+ "minimist": "^1.2.3",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index bd7cfae77..31ab3dc1e 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -1003,11 +1003,6 @@ async-each@^1.0.0:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
-async-limiter@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
- integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-
async@^2.0.0, async@^2.6.1, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -1218,16 +1213,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
-braces@^2.3.1:
+braces@^1.8.2, braces@^2.3.1, braces@^3.0.1, braces@~3.0.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
@@ -1243,13 +1229,6 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@^3.0.1, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@@ -2389,13 +2368,6 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
- dependencies:
- fill-range "^2.1.0"
-
expand-template@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
@@ -2659,17 +2631,6 @@ filesize@^6.1.0:
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==
-fill-range@^2.1.0:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
- integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^3.0.0"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -2680,13 +2641,6 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -2960,17 +2914,10 @@ glob-base@^0.3.0:
glob-parent "^2.0.0"
is-glob "^2.0.0"
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
- dependencies:
- is-glob "^2.0.0"
-
-glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+glob-parent@^2.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
@@ -3581,13 +3528,6 @@ is-natural-number@^4.0.1:
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -3595,21 +3535,11 @@ is-number@^3.0.0:
dependencies:
kind-of "^3.0.2"
-is-number@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
- integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
-
is-number@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-6.0.0.tgz#e6d15ad31fc262887cccf217ae5f9316f81b1995"
integrity sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg==
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
is-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
@@ -4164,11 +4094,6 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-math-random@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
- integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
-
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4512,19 +4437,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npmlog@^4.0.1:
version "4.1.2"
@@ -4898,11 +4814,6 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
- integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
-
prettyoutput@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/prettyoutput/-/prettyoutput-1.2.0.tgz#fef93f2a79c032880cddfb84308e2137e3674b22"
@@ -5102,15 +5013,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -5145,15 +5047,6 @@ ramda@^0.27.1:
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
-randomatic@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
- integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
- dependencies:
- is-number "^4.0.0"
- kind-of "^6.0.0"
- math-random "^1.0.1"
-
randomstring@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/randomstring/-/randomstring-1.1.5.tgz#6df0628f75cbd5932930d9fe3ab4e956a18518c3"
@@ -5328,7 +5221,7 @@ repeat-element@^1.1.2:
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@@ -5884,13 +5777,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
source-map-resolve@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@@ -6008,11 +5894,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-template@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
@@ -6331,13 +6212,6 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -6454,11 +6328,6 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-ultron@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
- integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
-
unbzip2-stream@^1.0.9:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
@@ -6718,33 +6587,10 @@ write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^3.2.0:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
- integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
- dependencies:
- async-limiter "~1.0.0"
- safe-buffer "~5.1.0"
- ultron "~1.1.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
- integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^3.2.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xml2js@0.4.19:
version "0.4.19"
diff --git a/cla-frontend-project-console/package.json b/cla-frontend-project-console/package.json
index f2d40b656..4668fa626 100644
--- a/cla-frontend-project-console/package.json
+++ b/cla-frontend-project-console/package.json
@@ -24,9 +24,13 @@
"resolutions": {
"axios": "^0.21.1",
"bl": "^1.2.3",
+ "braces": "^2.3.1",
+ "glob-parent": "^5.1.2",
"http-proxy": "^1.18.1",
"ini": "^1.3.7",
"kind-of": "^6.0.3",
- "minimist": "^1.2.3"
+ "minimist": "^1.2.3",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/cla-frontend-project-console/yarn.lock b/cla-frontend-project-console/yarn.lock
index e032b9b67..8eb5f6e8a 100644
--- a/cla-frontend-project-console/yarn.lock
+++ b/cla-frontend-project-console/yarn.lock
@@ -987,11 +987,6 @@ async-each@^1.0.0:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
-async-limiter@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
- integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
-
async@^2.0.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381"
@@ -1214,16 +1209,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
-braces@^2.3.1:
+braces@^1.8.2, braces@^2.3.1, braces@^3.0.1, braces@~3.0.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
@@ -1239,13 +1225,6 @@ braces@^2.3.1:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@^3.0.1, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@@ -2352,13 +2331,6 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
- dependencies:
- fill-range "^2.1.0"
-
expand-template@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
@@ -2617,17 +2589,6 @@ filesize@^6.1.0:
resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==
-fill-range@^2.1.0:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
- integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^3.0.0"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -2638,13 +2599,6 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -2909,17 +2863,10 @@ glob-base@^0.3.0:
glob-parent "^2.0.0"
is-glob "^2.0.0"
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
- dependencies:
- is-glob "^2.0.0"
-
-glob-parent@^5.1.0, glob-parent@~5.1.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
- integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+glob-parent@^2.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
@@ -3538,13 +3485,6 @@ is-natural-number@^4.0.1:
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -3552,21 +3492,11 @@ is-number@^3.0.0:
dependencies:
kind-of "^3.0.2"
-is-number@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
- integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
-
is-number@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-6.0.0.tgz#e6d15ad31fc262887cccf217ae5f9316f81b1995"
integrity sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg==
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
is-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
@@ -4106,11 +4036,6 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-math-random@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
- integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
-
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4504,19 +4429,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npm-bundled@^1.0.1:
version "1.0.6"
@@ -4869,11 +4785,6 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
- integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
-
prettyoutput@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/prettyoutput/-/prettyoutput-1.2.0.tgz#fef93f2a79c032880cddfb84308e2137e3674b22"
@@ -5049,15 +4960,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -5092,15 +4994,6 @@ ramda@^0.27.1:
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9"
integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==
-randomatic@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
- integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
- dependencies:
- is-number "^4.0.0"
- kind-of "^6.0.0"
- math-random "^1.0.1"
-
randomstring@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/randomstring/-/randomstring-1.1.5.tgz#6df0628f75cbd5932930d9fe3ab4e956a18518c3"
@@ -5268,7 +5161,7 @@ repeat-element@^1.1.2:
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@@ -5819,13 +5712,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
source-map-resolve@^0.5.0:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
@@ -5943,11 +5829,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-template@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
@@ -6261,13 +6142,6 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -6377,11 +6251,6 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
-ultron@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
- integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
-
unbzip2-stream@^1.0.9:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
@@ -6636,33 +6505,10 @@ write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^3.2.0:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
- integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==
- dependencies:
- async-limiter "~1.0.0"
- safe-buffer "~5.1.0"
- ultron "~1.1.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
- integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^3.2.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xml2js@0.4.19:
version "0.4.19"
diff --git a/package.json b/package.json
index 6c2600db9..b94106b1b 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,8 @@
},
"resolutions": {
"axios": "^0.21.1",
- "ini": "^1.3.7"
+ "ini": "^1.3.7",
+ "normalize-url": "^4.5.1",
+ "ws": "^7.4.6"
}
}
diff --git a/yarn.lock b/yarn.lock
index 7259e5313..621ce34ee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -785,11 +785,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-async-limiter@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
- integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-
async@^2.6.1, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -1484,11 +1479,6 @@ debug@~3.1.0:
dependencies:
ms "2.0.0"
-decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
@@ -3344,19 +3334,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-normalize-url@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
- integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
- dependencies:
- prepend-http "^2.0.0"
- query-string "^5.0.1"
- sort-keys "^2.0.0"
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
+normalize-url@2.0.1, normalize-url@^4.1.0, normalize-url@^4.5.1:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
+ integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npm-run-path@^2.0.0:
version "2.0.2"
@@ -3806,15 +3787,6 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-query-string@^5.0.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
- integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
- dependencies:
- decode-uri-component "^0.2.0"
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@@ -4293,13 +4265,6 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-sort-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
- integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
- dependencies:
- is-plain-obj "^1.0.0"
-
source-map-support@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@@ -4402,11 +4367,6 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
- integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
-
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -4935,24 +4895,10 @@ write-file-atomic@^3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
-ws@<7.0.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
- dependencies:
- async-limiter "~1.0.0"
-
-ws@^7.2.1, ws@^7.3.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
- integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
-
-ws@~6.1.0:
- version "6.1.4"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
- integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
- dependencies:
- async-limiter "~1.0.0"
+ws@<7.0.0, ws@^7.2.1, ws@^7.3.1, ws@^7.4.6, ws@~6.1.0:
+ version "7.5.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
+ integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
xml2js@0.4.19:
version "0.4.19"
From 1418a128635a9731ad5dbcae91d220f88eb9c3f3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 10:28:05 -0500
Subject: [PATCH 0357/1276] More NodeJS Library Updates (#3039)
---
cla-frontend-contributor-console/package.json | 5 ++++-
cla-frontend-contributor-console/yarn.lock | 21 ++++++++++++-------
cla-frontend-corporate-console/package.json | 4 +++-
cla-frontend-corporate-console/yarn.lock | 16 +++++++-------
cla-frontend-project-console/package.json | 4 +++-
cla-frontend-project-console/yarn.lock | 16 +++++++-------
package.json | 4 +++-
yarn.lock | 13 ++++++++----
8 files changed, 51 insertions(+), 32 deletions(-)
diff --git a/cla-frontend-contributor-console/package.json b/cla-frontend-contributor-console/package.json
index e35d008de..d991e9cae 100644
--- a/cla-frontend-contributor-console/package.json
+++ b/cla-frontend-contributor-console/package.json
@@ -27,7 +27,10 @@
"braces": "^2.3.1",
"glob-parent": "^5.1.2",
"ini": "^1.3.7",
+ "netmask": "^2.0.1",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "trim-newlines": "^3.0.1",
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 3d89c7430..561c1188b 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -4418,10 +4418,10 @@ nested-error-stacks@^2.0.0:
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
-netmask@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
- integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=
+netmask@^1.0.6, netmask@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
+ integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
next-tick@1, next-tick@^1.0.0:
version "1.1.0"
@@ -6289,6 +6289,11 @@ traverse@^0.6.6:
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=
+trim-newlines@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
+ integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
+
trim-repeated@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
@@ -6647,10 +6652,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xregexp@2.0.0:
version "2.0.0"
diff --git a/cla-frontend-corporate-console/package.json b/cla-frontend-corporate-console/package.json
index 6ee28808e..0c4c4f9d5 100644
--- a/cla-frontend-corporate-console/package.json
+++ b/cla-frontend-corporate-console/package.json
@@ -30,8 +30,10 @@
"glob-parent": "^5.1.2",
"kind-of": "^6.0.3",
"lodash": "^4.17.19",
+ "netmask": "^2.0.1",
"minimist": "^1.2.3",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 31ab3dc1e..8064dde49 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -4381,10 +4381,10 @@ nested-error-stacks@^2.0.0:
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
-netmask@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
- integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=
+netmask@^1.0.6, netmask@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
+ integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
next-tick@1, next-tick@^1.0.0:
version "1.1.0"
@@ -6605,10 +6605,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xregexp@2.0.0:
version "2.0.0"
diff --git a/cla-frontend-project-console/package.json b/cla-frontend-project-console/package.json
index 4668fa626..63d5eca0b 100644
--- a/cla-frontend-project-console/package.json
+++ b/cla-frontend-project-console/package.json
@@ -30,7 +30,9 @@
"ini": "^1.3.7",
"kind-of": "^6.0.3",
"minimist": "^1.2.3",
+ "netmask": "^2.0.1",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/cla-frontend-project-console/yarn.lock b/cla-frontend-project-console/yarn.lock
index 8eb5f6e8a..c58c94dd5 100644
--- a/cla-frontend-project-console/yarn.lock
+++ b/cla-frontend-project-console/yarn.lock
@@ -4349,10 +4349,10 @@ nested-error-stacks@^2.0.0:
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
-netmask@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
- integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=
+netmask@^1.0.6, netmask@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
+ integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
next-tick@1, next-tick@^1.0.0:
version "1.1.0"
@@ -6523,10 +6523,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xtend@^4.0.0, xtend@~4.0.0:
version "4.0.2"
diff --git a/package.json b/package.json
index b94106b1b..fda74960c 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,9 @@
"resolutions": {
"axios": "^0.21.1",
"ini": "^1.3.7",
+ "netmask": "^2.0.1",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/yarn.lock b/yarn.lock
index 621ce34ee..97d085948 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3280,6 +3280,11 @@ nested-error-stacks@^2.0.0:
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
+netmask@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
+ integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
+
next-tick@1, next-tick@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
@@ -4913,10 +4918,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xtend@^4.0.0:
version "4.0.2"
From ccdd9a63262cfa4700ed6f596c221af610b4e8bd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 12:23:58 -0500
Subject: [PATCH 0358/1276] Cleaned up Event Log Parent Project Lookup (#3040)
- Cleaned up event log parent project lookup logic
- Updated backend node lib vulnerability - xmlhttprequest-ssl
- CVE-2021-31597 critical severity Vulnerable versions: < 1.6.1
Patched version: 1.6.1 The xmlhttprequest-ssl package before 1.6.1
for Node.js disables SSL certificate validation by default, because
rejectUnauthorized (when the property exists but is undefined) is
considered to be false within the https.request function of Node.js.
In other words, no certificate is ever rejected.
- CVE-2020-28502 high severity Vulnerable versions: < 1.6.2 Patched
version: 1.6.2 This affects the package xmlhttprequest before 1.7.0;
all versions of package xmlhttprequest-ssl. Provided requests are
sent synchronously (async=False on xhr.open), malicious user input
flowing into xhr.send could result in arbitrary code being injected
and run.
- updated contributor console lib vuls:
- ws
- glob-parent
- minimist
- braces
- trim-newlines
Signed-off-by: David Deal
---
cla-backend-go/events/service.go | 17 +--
cla-backend-go/package.json | 3 +-
cla-backend-go/yarn.lock | 8 +-
cla-backend/package.json | 3 +-
cla-backend/yarn.lock | 8 +-
.../src/package.json | 27 ++--
.../src/yarn.lock | 116 ++++--------------
7 files changed, 58 insertions(+), 124 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index f3e9241aa..78cff591f 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -255,13 +255,14 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
args.ProjectName = project.Name
// Try to load and set the parent information
- if project.Foundation != nil && project.Foundation.ID != "" {
- log.WithFields(f).Debugf("loading salesforce project parent by ID: %s...", project.Foundation.ID)
+ if utils.IsProjectHaveParent(project) {
+ log.WithFields(f).Debugf("loading project parent by ID: %s...", project.Foundation.ID)
parentProject, parentProjectErr := project_service.GetClient().GetProject(project.Foundation.ID)
if parentProjectErr != nil || parentProject == nil {
- log.WithFields(f).Warnf("failed to load salesforce project parent by ID: %s", project.Foundation.ID)
+ log.WithFields(f).Warnf("failed to load project parent by ID: %s", project.Foundation.ID)
return nil
}
+
var parentProjectName, parentProjectID string
if !utils.IsProjectHasRootParent(project) {
parentProjectName = parentProject.Name
@@ -270,14 +271,14 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
parentProjectName = project.Name
parentProjectID = project.ID
}
- log.WithFields(f).Debugf("loaded salesforce project by parent ID: %s - resulting in ID: %s with name: %s",
+ log.WithFields(f).Debugf("loaded project by parent ID: %s - resulting in ID: %s with name: %s",
project.Foundation.ID, parentProjectID, parentProjectName)
args.ParentProjectSFID = parentProjectID
args.ParentProjectName = parentProjectName
- } else if project.Foundation != nil {
- // if there's no parent set the foundation as parent project
- args.ParentProjectSFID = project.Foundation.ID
- args.ParentProjectName = project.Foundation.Name
+ } else {
+ // No parent, just use the current project as the parent
+ args.ParentProjectSFID = project.ID
+ args.ParentProjectName = project.Name
}
} else {
log.WithFields(f).Warnf("project sfid %s was not set properly can't set parent project fields in event", args.ProjectSFID)
diff --git a/cla-backend-go/package.json b/cla-backend-go/package.json
index d58f86e95..53989b91e 100644
--- a/cla-backend-go/package.json
+++ b/cla-backend-go/package.json
@@ -30,6 +30,7 @@
"axios": "^0.21.1",
"ini": "^1.3.7",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index ffa8b1db2..d9354dd2e 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -5132,10 +5132,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xtend@^4.0.0:
version "4.0.2"
diff --git a/cla-backend/package.json b/cla-backend/package.json
index fb13cec78..2a7ca59bf 100644
--- a/cla-backend/package.json
+++ b/cla-backend/package.json
@@ -49,6 +49,7 @@
"ini": "^1.3.7",
"node-fetch": "^2.6.1",
"normalize-url": "^4.5.1",
- "ws": "^7.4.6"
+ "ws": "^7.4.6",
+ "xmlhttprequest-ssl": "^1.6.2"
}
}
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 6a2f8a6dc..19bc7ae7d 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -6310,10 +6310,10 @@ xmlbuilder@~9.0.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlhttprequest-ssl@~1.5.4:
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
- integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
+xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6"
+ integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==
xtend@^4.0.0:
version "4.0.2"
diff --git a/cla-frontend-contributor-console/src/package.json b/cla-frontend-contributor-console/src/package.json
index a51c8c7d1..b8d825f9f 100644
--- a/cla-frontend-contributor-console/src/package.json
+++ b/cla-frontend-contributor-console/src/package.json
@@ -1,5 +1,6 @@
{
"name": "cla-frontend-contributor-console",
+ "description": "cla-contributor-console",
"license": "MIT",
"author": "The Linux Foundation",
"private": true,
@@ -58,16 +59,6 @@
"@ionic/app-scripts": "3.2.0",
"typescript": "3.0.1"
},
- "resolutions": {
- "cryptiles": "^4.1.2",
- "hoek": "^4.2.1",
- "ini": "^1.3.7",
- "kind-of": "^6.0.3",
- "mem": "^4.0.0",
- "node-sass": "4.13.1",
- "tunnel-agent": "^0.6.0",
- "yargs-parser": "13.1.2"
- },
"cordovaPlugins": [
"cordova-plugin-whitelist",
"cordova-plugin-statusbar",
@@ -87,5 +78,19 @@
"config": {
"ionic_src_dir": "ionic"
},
- "description": "cla-contributor-console"
+ "resolutions": {
+ "braces": "^2.3.1",
+ "cryptiles": "^4.1.2",
+ "hoek": "^4.2.1",
+ "glob-parent": "^5.1.2",
+ "ini": "^1.3.7",
+ "kind-of": "^6.0.3",
+ "mem": "^4.0.0",
+ "minimist": "^0.2.1",
+ "node-sass": "4.13.1",
+ "trim-newlines": "^3.0.1",
+ "tunnel-agent": "^0.6.0",
+ "ws": "^5.2.3",
+ "yargs-parser": "13.1.2"
+ }
}
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index 1d91d9e19..f40734a66 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -516,15 +516,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
-braces@^2.3.1, braces@^2.3.2:
+braces@^1.8.2, braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
dependencies:
@@ -1378,12 +1370,6 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- dependencies:
- fill-range "^2.1.0"
-
express@^4.16.3:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
@@ -1504,16 +1490,6 @@ filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
-fill-range@^2.1.0:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^3.0.0"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -1682,18 +1658,12 @@ glob-base@^0.3.0:
glob-parent "^2.0.0"
is-glob "^2.0.0"
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- dependencies:
- is-glob "^2.0.0"
-
-glob-parent@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+glob-parent@^2.0.0, glob-parent@^3.1.0, glob-parent@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
- is-glob "^3.1.0"
- path-dirname "^1.0.0"
+ is-glob "^4.0.1"
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
version "7.1.4"
@@ -2033,7 +2003,7 @@ is-extglob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
-is-extglob@^2.1.0, is-extglob@^2.1.1:
+is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -2059,15 +2029,10 @@ is-glob@^2.0.0, is-glob@^2.0.1:
dependencies:
is-extglob "^1.0.0"
-is-glob@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
- dependencies:
- is-extglob "^2.1.0"
-
-is-glob@^4.0.0:
+is-glob@^4.0.0, is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+ integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
@@ -2075,22 +2040,12 @@ is-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
dependencies:
kind-of "^3.0.2"
-is-number@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
-
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -2381,10 +2336,6 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-math-random@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
-
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -2514,15 +2465,10 @@ minimatch@^3.0.4, minimatch@~3.0.2:
dependencies:
brace-expansion "^1.1.7"
-minimist@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
- integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-
-minimist@^1.1.3, minimist@^1.2.0:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
- integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+minimist@0.0.8, minimist@^0.2.1, minimist@^1.1.3, minimist@^1.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.1.tgz#827ba4e7593464e7c221e8c5bed930904ee2c455"
+ integrity sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==
minipass@^2.2.1, minipass@^2.3.5:
version "2.3.5"
@@ -2938,10 +2884,6 @@ path-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
-path-dirname@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
-
path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
@@ -3032,10 +2974,6 @@ postcss@^6.0.17, postcss@^6.0.21:
source-map "^0.6.1"
supports-color "^5.4.0"
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
-
private@~0.1.5:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -3139,14 +3077,6 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
-randomatic@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
- dependencies:
- is-number "^4.0.0"
- kind-of "^6.0.0"
- math-random "^1.0.1"
-
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -3934,9 +3864,10 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
-trim-newlines@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
+trim-newlines@^1.0.0, trim-newlines@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
+ integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
"true-case-path@^1.0.2":
version "1.0.3"
@@ -4056,10 +3987,6 @@ uglifyjs-webpack-plugin@^0.4.6:
uglify-js "^2.8.29"
webpack-sources "^1.0.1"
-ultron@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
-
unfetch@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db"
@@ -4298,13 +4225,12 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-ws@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.2.tgz#96c1d08b3fefda1d5c1e33700d3bfaa9be2d5608"
+ws@3.3.2, ws@^5.2.3:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d"
+ integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==
dependencies:
async-limiter "~1.0.0"
- safe-buffer "~5.1.0"
- ultron "~1.1.0"
xml2js@0.4.19, xml2js@^0.4.19:
version "0.4.19"
From 2cf493fdc991ac10f3289f8f21e4a83c81f1304f Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 16:32:43 -0500
Subject: [PATCH 0359/1276] Resolved Project Service Parent Nil Pointer Issue
(#3043)
---
cla-backend-go/v2/project-service/client.go | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 55b054923..b968d05ec 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -173,8 +173,12 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
+ if !utils.IsProjectHaveParent(existingModel) {
+ return nil, nil
+ }
+
// Does this project they have a parent? projectModel.Parent is deprecated and no longer returned, use project.Foundation.ID/Name attribute instead
- if existingModel.Foundation != nil && existingModel.Foundation.ID != "" && (existingModel.Foundation.Name == utils.TheLinuxFoundation || existingModel.Foundation.Name == utils.LFProjectsLLC) {
+ if existingModel.Foundation.Name == utils.TheLinuxFoundation || existingModel.Foundation.Name == utils.LFProjectsLLC {
log.WithFields(f).Debugf("no parent for projectSFID %s or %s or %s is the parent...", projectSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC)
return nil, nil
}
@@ -217,12 +221,12 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
// No parent
- if projectModel.Foundation == nil || projectModel.Foundation.ID == "" {
+ if !utils.IsProjectHaveParent(existingModel) {
return nil, nil
}
// Do they have a parent? projectModel.Parent is deprecated and no longer returned
- if projectModel.Foundation != nil && projectModel.Foundation.ID != "" && (projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC) {
+ if projectModel.Foundation.Name == utils.TheLinuxFoundation || projectModel.Foundation.Name == utils.LFProjectsLLC {
log.WithFields(f).Debugf("no parent for projectSFID or %s or %s is the parent...", utils.TheLinuxFoundation, utils.LFProjectsLLC)
return nil, nil
}
From 87d9a00df3d42eb5aa34cb4776bc08e58de68384 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 17:24:15 -0500
Subject: [PATCH 0360/1276] Cleaned up Enroll/Unenroll Issue (#3044)
Signed-off-by: David Deal
---
cla-backend-go/events/service.go | 12 ++++++------
cla-backend-go/v2/cla_groups/handlers.go | 14 +++++++-------
cla-backend-go/v2/cla_groups/helpers.go | 6 +++---
cla-backend-go/v2/cla_groups/service.go | 4 ++--
cla-backend-go/v2/project-service/client.go | 6 +++---
5 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index 78cff591f..eb0f2e974 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -256,17 +256,17 @@ func (s *service) loadSFProject(ctx context.Context, args *LogEventArgs) error {
// Try to load and set the parent information
if utils.IsProjectHaveParent(project) {
- log.WithFields(f).Debugf("loading project parent by ID: %s...", project.Foundation.ID)
- parentProject, parentProjectErr := project_service.GetClient().GetProject(project.Foundation.ID)
- if parentProjectErr != nil || parentProject == nil {
- log.WithFields(f).Warnf("failed to load project parent by ID: %s", project.Foundation.ID)
+ log.WithFields(f).Debugf("loading project parent by ID: %s...", utils.GetProjectParentSFID(project))
+ parentProjectModel, parentProjectErr := project_service.GetClient().GetParentProjectModel(project.ID)
+ if parentProjectErr != nil || parentProjectModel == nil {
+ log.WithFields(f).Warnf("failed to load project parent by ID: %s", utils.GetProjectParentSFID(project))
return nil
}
var parentProjectName, parentProjectID string
if !utils.IsProjectHasRootParent(project) {
- parentProjectName = parentProject.Name
- parentProjectID = parentProject.ID
+ parentProjectName = parentProjectModel.Name
+ parentProjectID = parentProjectModel.ID
} else {
parentProjectName = project.Name
parentProjectID = project.ID
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index bca28d288..ec2b124a0 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -270,7 +270,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
"projectSFIDList": strings.Join(params.ProjectSFIDList, ","),
}
- cg, getCLAGroupErr := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, getCLAGroupErr := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
if getCLAGroupErr != nil {
if _, ok := getCLAGroupErr.(*utils.CLAGroupNotFound); ok {
return cla_group.NewEnrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
@@ -285,14 +285,14 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, []string{cg.ProjectExternalID}, projectClaGroupsRepo) {
- msg := fmt.Sprintf("user %s does not have access to enroll projects with project scope of: %s", authUser.UserName, cg.FoundationSFID)
+ if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, []string{claGroupModel.ProjectExternalID}, projectClaGroupsRepo) {
+ msg := fmt.Sprintf("user %s does not have access to enroll projects with project scope of: %s", authUser.UserName, claGroupModel.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewEnrollProjectsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- if !cg.FoundationLevelCLA {
- log.WithFields(f).Debug("locating project by sfid...")
+ if !claGroupModel.FoundationLevelCLA {
+ log.WithFields(f).Debug("cla group is not a foundation level CLA group - locating project by sfid...")
psc := v2ProjectService.GetClient()
for _, projectSFID := range params.ProjectSFIDList {
project, projectErr := psc.GetProject(projectSFID)
@@ -321,16 +321,16 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
}
-
}
// Enroll the project(s) into the CLA Group
enrollCLAGroupErr := service.EnrollProjectsInClaGroup(ctx, &EnrollProjectsModel{
AuthUser: authUser,
CLAGroupID: params.ClaGroupID,
- FoundationSFID: cg.FoundationSFID,
+ FoundationSFID: claGroupModel.FoundationSFID,
ProjectSFIDList: params.ProjectSFIDList,
})
+
if enrollCLAGroupErr != nil {
if strings.Contains(enrollCLAGroupErr.Error(), "bad request") {
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index 3fc5916d5..a18dfdf2d 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -161,9 +161,9 @@ func (s *service) validateClaGroupInput(ctx context.Context, input *models.Creat
log.WithFields(f).Debugf("looking up LF parent project record...")
isLFParent := false
if utils.IsProjectHaveParent(foundationProjectDetails) {
- isLFParent, err = psc.IsTheLinuxFoundation(foundationProjectDetails.Foundation.ID)
+ isLFParent, err = psc.IsTheLinuxFoundation(utils.GetProjectParentSFID(foundationProjectDetails))
if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup parent project by SFID: %s", foundationProjectDetails.Foundation.ID)
+ log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup parent project by SFID: %s", utils.GetProjectParentSFID(foundationProjectDetails))
return false, err
}
}
@@ -361,7 +361,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
if !isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup) {
msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- foundationProjectDetails.Foundation.ID, foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
+ utils.GetProjectParentSFID(foundationProjectDetails), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
log.WithFields(f).Warnf(msg)
return fmt.Errorf(msg)
}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 3ffaffd02..d0617159b 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -1027,7 +1027,7 @@ func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollP
return nil
}
-//func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, claGroupID string, foundationSFID string, projectSFIDList []string) error {
+// UnenrollProjectsInClaGroup un-enrolls the specified projects from the CLA group
func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *UnenrollProjectsModel) error {
f := logrus.Fields{
"functionName": "v2.cla_groups.service.UnenrollProjectsInClaGroup",
@@ -1051,7 +1051,7 @@ func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *Unenr
var wg sync.WaitGroup
wg.Add(2)
- // Separate go routine for unenrolling projects
+ // Separate go routine for un-enrolling projects
go func(c context.Context, authUser *auth.User, claGroupID string, foundationSFID string, projSFIDList []string) {
defer wg.Done()
log.WithFields(f).Debug("unenrolling projects in CLA Group")
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index b968d05ec..662ac4798 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -147,12 +147,12 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
return projectSFID, err
}
- if parentModel == nil || parentModel.Foundation == nil || parentModel.Foundation.ID == "" {
+ if parentModel == nil {
log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
return projectSFID, err
}
- return parentModel.Foundation.ID, nil
+ return parentModel.ID, nil
}
// GetParentProjectModel returns the parent project model if there is a parent, otherwise returns nil
@@ -293,7 +293,7 @@ func (pmm *Client) IsParentTheLinuxFoundation(projectSFID string) (bool, error)
return false, err
}
- if projectModel.Foundation == nil || projectModel.Foundation.ID == "" {
+ if !utils.IsProjectHaveParent(projectModel) {
return false, nil
}
From b0912b3edb976404d67f39cf569622386fb4fa4e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 21 Jul 2021 22:31:16 -0500
Subject: [PATCH 0361/1276] Cleaned up Enroll/Unenroll Issue (#3045)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/handlers.go | 8 +++---
cla-backend-go/v2/cla_groups/helpers.go | 31 ++++-----------------
cla-backend-go/v2/project-service/client.go | 23 ++++++++++++---
3 files changed, 29 insertions(+), 33 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index ec2b124a0..a2a2d7e2f 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -356,7 +356,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
"projectSFIDList": strings.Join(params.ProjectSFIDList, ","),
}
- cg, err := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
+ claGroupModel, err := v1ProjectService.GetCLAGroupByID(ctx, params.ClaGroupID)
if err != nil {
if err, ok := err.(*utils.CLAGroupNotFound); ok {
return cla_group.NewUnenrollProjectsNotFound().WithXRequestID(reqID).WithPayload(
@@ -371,8 +371,8 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
}
// Check permissions
- if !isUserHaveAccessToCLAProject(ctx, authUser, cg.FoundationSFID, []string{cg.ProjectExternalID}, projectClaGroupsRepo) {
- msg := fmt.Sprintf("user %s does not have access to unenroll projects with project scope of: %s", authUser.UserName, cg.FoundationSFID)
+ if !isUserHaveAccessToCLAProject(ctx, authUser, claGroupModel.FoundationSFID, []string{claGroupModel.ProjectExternalID}, projectClaGroupsRepo) {
+ msg := fmt.Sprintf("user %s does not have access to unenroll projects with project scope of: %s", authUser.UserName, claGroupModel.FoundationSFID)
log.WithFields(f).Warn(msg)
return cla_group.NewUnenrollProjectsForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -380,7 +380,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
err = service.UnenrollProjectsInClaGroup(ctx, &UnenrollProjectsModel{
AuthUser: authUser,
CLAGroupID: params.ClaGroupID,
- FoundationSFID: cg.FoundationSFID,
+ FoundationSFID: claGroupModel.FoundationSFID,
ProjectSFIDList: params.ProjectSFIDList,
})
if err != nil {
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index a18dfdf2d..b62e62536 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -315,12 +315,6 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
log.WithFields(f).Warn("validation failure - there should be at least one subproject associated...")
return fmt.Errorf("bad request: there should be at least one subproject associated")
}
- // Comment out the below as we want to support project-level projects
- /* log.WithFields(f).Debug("checking to see if foundation is in project list...")
- if !isFoundationIDInList(foundationSFID, projectSFIDList) {
- log.WithFields(f).Warn("validation failure - unable to unenroll Project Group from CLA Group")
- return fmt.Errorf("bad request: unable to unenroll Project Group from CLA Group")
- } */
// fetch the foundation model details from the platform project service which includes a list of its sub projects
foundationProjectDetails, err := psc.GetProject(foundationSFID)
@@ -339,32 +333,19 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
}
+ // Combine all the projectSFID values and check to see if any are the project root - shouldn't be in the list
+ if psc.IsAnyProjectTheRootParent(append(projectSFIDList, foundationSFID)) {
+ return errors.New("validation failure - one of the input projects is the root Linux Foundation project")
+ }
+
// build Tree that tracks parent and child projects
projectTree := buildProjectNode(foundationProjectSummary)
- // Is our parent the LF project?
- log.WithFields(f).Debugf("looking up LF parent project record...")
- isLFParent := false
- if utils.IsProjectHaveParent(foundationProjectDetails) {
- isLFParent, err = psc.IsTheLinuxFoundation(foundationProjectDetails.Foundation.ID)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - unable to lookup parent project by SFID: %s", foundationProjectDetails.Foundation.ID)
- return err
- }
- }
-
for _, projectSFID := range projectSFIDList {
- projectDetails, projErr := psc.GetProject(projectSFID)
+ _, projErr := psc.GetProject(projectSFID)
if projErr != nil {
return err
}
-
- if !isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup) {
- msg := fmt.Sprintf("input validation failure - foundationSFID: %s , foundationType: %s , projectSFID: %s , projectType: %s ",
- utils.GetProjectParentSFID(foundationProjectDetails), foundationProjectDetails.ProjectType, projectSFID, projectDetails.ProjectType)
- log.WithFields(f).Warnf(msg)
- return fmt.Errorf(msg)
- }
}
// Check to see if all the provided enrolled projects are part of this foundation
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 662ac4798..ee3c5d353 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -184,7 +184,7 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
}
// Grab the parent ID once
- projectParentSFID := existingModel.Foundation.ID
+ projectParentSFID := utils.GetProjectParentSFID(existingModel)
// Parent SFID in the cache?
existingParentModel, exists = projectServiceModels[projectParentSFID]
@@ -221,7 +221,7 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
// No parent
- if !utils.IsProjectHaveParent(existingModel) {
+ if !utils.IsProjectHaveParent(projectModel) {
return nil, nil
}
@@ -232,7 +232,7 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
}
// Grab the parent ID once
- projectParentSFID := projectModel.Foundation.ID
+ projectParentSFID := utils.GetProjectParentSFID(projectModel)
// Parent in the cache?
existingParentModel, exists = projectServiceModels[projectParentSFID]
@@ -262,7 +262,6 @@ func (pmm *Client) IsTheLinuxFoundation(projectSFID string) (bool, error) {
"apiGWHost": apiGWHost,
}
- log.WithFields(f).Debug("querying project...")
projectModel, err := pmm.GetProject(projectSFID)
if err != nil {
log.WithFields(f).Warnf("unable to lookup project by ID: %s error: %+v", projectSFID, err)
@@ -465,3 +464,19 @@ func (pmm *Client) GetSummary(ctx context.Context, projectSFID string) ([]*model
return result.Payload.Data, nil
}
+
+// IsAnyProjectTheRootParent returns true if one or more of the project ID's in the list is one of the root parents, returns false otherwise
+func (pmm *Client) IsAnyProjectTheRootParent(sliceProjectSFID []string) bool {
+ var retVal bool
+
+ // Check each project to see if it is one of the root parents
+ for _, projectSFID := range sliceProjectSFID {
+ // If so, return true, we're done
+ if isTLF, err := pmm.IsTheLinuxFoundation(projectSFID); isTLF && err == nil {
+ retVal = isTLF
+ break
+ }
+ }
+
+ return retVal
+}
From 8854480b7808eecc200ad031b383fb7310164a1e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 22 Jul 2021 13:59:39 -0500
Subject: [PATCH 0362/1276] Added Logic to Prevent Unenroll of All Projects
Under a CLA Group (#3046)
Signed-off-by: David Deal
---
.circleci/config.yml | 16 ++---------
cla-backend-go/events/service.go | 7 -----
cla-backend-go/v2/cla_groups/helpers.go | 38 ++++++++++++++++++-------
cla-backend-go/v2/cla_groups/service.go | 1 -
4 files changed, 29 insertions(+), 33 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 93a29ae55..3ffb4c24c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -242,22 +242,10 @@ jobs:
name: Build
command: |
cd cla-backend-go
- echo "Building AWS Lambda - API..."
- make build-aws-lambda-linux
- echo "Building AWS Metrics Lambda..."
- make build-metrics-lambda-linux
- echo "Building AWS Metrics Report Lambda..."
- make build-metrics-report-lambda
- echo "Building AWS Lambda - DynamoDB Events Handler..."
- make build-dynamo-events-lambda-linux
- echo "Building AWS Lambda - Zip Builder Scheduler..."
- make build-zipbuilder-scheduler-lambda-linux
- echo "Building AWS Lambda - Zip Builder Handler..."
- make build-zipbuilder-lambda-linux
+ echo "Building Lambdas..."
+ make build-lambdas-linux
echo "Building Functional Tests..."
make build-functional-tests-linux
- echo "Building User Subscribe..."
- make build-user-subscribe-lambda-linux
- run:
name: Test
command: |
diff --git a/cla-backend-go/events/service.go b/cla-backend-go/events/service.go
index eb0f2e974..7274fe8e0 100644
--- a/cla-backend-go/events/service.go
+++ b/cla-backend-go/events/service.go
@@ -372,45 +372,38 @@ func (s *service) loadDetails(ctx context.Context, args *LogEventArgs) error {
f := logrus.Fields{
"functionName": "v1.events.service.loadDetails",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "args": fmt.Sprintf("%+v", args),
}
- log.WithFields(f).Debug("loading company details...")
err := s.loadCompany(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load company details...")
return err
}
- log.WithFields(f).Debug("loading SF project details...")
err = s.loadSFProject(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load SF project details...")
return err
}
- log.WithFields(f).Debug("loading CLA Group details...")
err = s.loadCLAGroup(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load CLA Group details...")
return err
}
- log.WithFields(f).Debug("loading LF user details ...")
err = s.loadLFUser(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load LF User details...")
return err
}
- log.WithFields(f).Debug("loading user details...")
err = s.loadUser(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load user details...")
return err
}
- log.WithFields(f).Debug("loading LF user details...")
err = s.loadLFUser(ctx, args)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to load LF user details...")
diff --git a/cla-backend-go/v2/cla_groups/helpers.go b/cla-backend-go/v2/cla_groups/helpers.go
index b62e62536..fb17b75b8 100644
--- a/cla-backend-go/v2/cla_groups/helpers.go
+++ b/cla-backend-go/v2/cla_groups/helpers.go
@@ -214,8 +214,7 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
psc := v2ProjectService.GetClient()
if len(projectSFIDList) == 0 {
- log.WithFields(f).Warn("validation failure - there should be at least one subproject associated...")
- return fmt.Errorf("bad request: there should be at least one subproject associated")
+ return errors.New("validation failure - there should be at least one project provided for the enroll request")
}
// fetch the foundation model details from the platform project service which includes a list of its sub projects
@@ -225,18 +224,23 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
return err
}
if foundationProjectDetails == nil {
- return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ return fmt.Errorf("validation failure - problem fetching project details for project: %s", foundationSFID)
}
foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ log.WithFields(f).WithError(err).Warnf("validation failure - problem fetching project details for project: %s", foundationSFID)
return err
}
if foundationProjectSummary == nil {
return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
}
+ // Combine all the projectSFID values and check to see if any are the project root - shouldn't be in the list
+ if psc.IsAnyProjectTheRootParent(append(projectSFIDList, foundationSFID)) {
+ return errors.New("validation failure - one of the input projects is the root Linux Foundation project")
+ }
+
// build Tree that tracks parent and child projects
projectTree := buildProjectNode(foundationProjectSummary)
@@ -252,10 +256,11 @@ func (s *service) validateEnrollProjectsInput(ctx context.Context, foundationSFI
}
}
+ // Make sure each project exists in the project service
for _, projectSFID := range projectSFIDList {
projectDetails, projErr := psc.GetProject(projectSFID)
if projErr != nil {
- return err
+ return fmt.Errorf("validation failure - unable to lookup project by ID %s due to the error: %+v", projectSFID, err)
}
if projectTree != nil && projectTree.Parent != nil && (!isLFParent && (foundationProjectDetails.ProjectType == utils.ProjectTypeProjectGroup && projectDetails.ProjectType != utils.ProjectTypeProjectGroup)) {
@@ -312,8 +317,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
psc := v2ProjectService.GetClient()
if len(projectSFIDList) == 0 {
- log.WithFields(f).Warn("validation failure - there should be at least one subproject associated...")
- return fmt.Errorf("bad request: there should be at least one subproject associated")
+ return errors.New("validation failure - there should be at least one project provided for the unenroll request")
}
// fetch the foundation model details from the platform project service which includes a list of its sub projects
@@ -322,7 +326,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
if foundationProjectDetails == nil {
- return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ return fmt.Errorf("validation failure - problem fetching project details for project: %s", foundationSFID)
}
foundationProjectSummary, err := psc.GetSummary(ctx, foundationSFID)
@@ -330,7 +334,7 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return err
}
if foundationProjectSummary == nil {
- return fmt.Errorf("validation failure - problem fetching project details from project service for project: %s", foundationSFID)
+ return fmt.Errorf("validation failure - problem fetching project details for project: %s", foundationSFID)
}
// Combine all the projectSFID values and check to see if any are the project root - shouldn't be in the list
@@ -338,13 +342,25 @@ func (s *service) validateUnenrollProjectsInput(ctx context.Context, foundationS
return errors.New("validation failure - one of the input projects is the root Linux Foundation project")
}
+ // Grab the existing list of project CLA Groups associated with this foundation
+ existingProjectCLAGroupModels, err := s.projectsClaGroupsRepo.GetProjectsIdsForFoundation(ctx, foundationSFID)
+ if err != nil {
+ return err
+ }
+ log.WithFields(f).Debugf("before unenroll, we have %d projects associated with the CLA Group - we will be removing %d and will have %d remaining.", len(existingProjectCLAGroupModels), len(projectSFIDList), len(existingProjectCLAGroupModels)-len(projectSFIDList))
+
+ if len(existingProjectCLAGroupModels)-len(projectSFIDList) <= 1 {
+ return fmt.Errorf("validation failure - must have at least one project enrolled in the CLA group under parent: %s with ID: %s", foundationProjectDetails.Name, foundationSFID)
+ }
+
// build Tree that tracks parent and child projects
projectTree := buildProjectNode(foundationProjectSummary)
+ // Make sure each project exists in the project service
for _, projectSFID := range projectSFIDList {
_, projErr := psc.GetProject(projectSFID)
if projErr != nil {
- return err
+ return fmt.Errorf("validation failure - unable to lookup project by ID %s due to the error: %+v", projectSFID, err)
}
}
@@ -567,7 +583,6 @@ func (s *service) DisableCLAService(ctx context.Context, authUser *auth.User, cl
"claGroupID": claGroupID,
}
- log.WithFields(f).Debug("disabling CLA service in platform project service")
// Run this in parallel...
var errorList []error
var wg sync.WaitGroup
@@ -578,6 +593,7 @@ func (s *service) DisableCLAService(ctx context.Context, authUser *auth.User, cl
// Execute as a go routine
go func(psClient *v2ProjectService.Client, claGroupID, projectSFID string) {
defer wg.Done()
+ log.WithFields(f).Debugf("disabling CLA service for project: %s", projectSFID)
disableProjectErr := psClient.DisableCLA(ctx, projectSFID)
if disableProjectErr != nil {
log.WithFields(f).WithError(disableProjectErr).
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index d0617159b..53bbb9557 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -1070,7 +1070,6 @@ func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *Unenr
// Separate go routine for disabling the CLA Service in the project service
go func(c context.Context, claGroupID string, projSFIDList []string) {
defer wg.Done()
- log.WithFields(f).Debug("disabling CLA service in platform project service")
// Note: log entry will be created by disable CLA Service call
errDisableCLA := s.DisableCLAService(c, request.AuthUser, claGroupID, projSFIDList)
if errDisableCLA != nil {
From 21bed8f49605c8cc290ca7cfc00320c983914113 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 22 Jul 2021 15:39:27 -0500
Subject: [PATCH 0363/1276] Added GitLab Approval List Placeholders (#3047)
---
cla-backend-go/signatures/converters.go | 8 +--
cla-backend-go/signatures/dbmodels.go | 10 ++--
cla-backend-go/signatures/handlers.go | 6 +-
cla-backend-go/signatures/models.go | 8 ++-
cla-backend-go/signatures/projections.go | 2 +
cla-backend-go/signatures/repository.go | 55 ++++++++----------
cla-backend-go/signatures/service.go | 48 +++++++--------
cla-backend-go/v2/signatures/handlers.go | 74 ++----------------------
8 files changed, 75 insertions(+), 136 deletions(-)
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index f5a730b6f..e7f2d979b 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -81,10 +81,10 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
ProjectID: dbSignature.SignatureProjectID,
Created: dbSignature.DateCreated,
Modified: dbSignature.DateModified,
- EmailApprovalList: dbSignature.EmailWhitelist,
- DomainApprovalList: dbSignature.DomainWhitelist,
- GithubUsernameApprovalList: dbSignature.GitHubWhitelist,
- GithubOrgApprovalList: dbSignature.GitHubOrgWhitelist,
+ EmailApprovalList: dbSignature.EmailApprovalList,
+ DomainApprovalList: dbSignature.EmailDomainApprovalList,
+ GithubUsernameApprovalList: dbSignature.GitHubUsernameApprovalList,
+ GithubOrgApprovalList: dbSignature.GitHubOrgApprovalList,
UserName: dbSignature.UserName,
UserLFID: dbSignature.UserLFUsername,
UserGHID: dbSignature.UserGithubUsername,
diff --git a/cla-backend-go/signatures/dbmodels.go b/cla-backend-go/signatures/dbmodels.go
index 021fa80c9..00056661d 100644
--- a/cla-backend-go/signatures/dbmodels.go
+++ b/cla-backend-go/signatures/dbmodels.go
@@ -19,10 +19,12 @@ type ItemSignature struct {
SignatureReferenceType string `json:"signature_reference_type"`
SignatureType string `json:"signature_type"`
SignatureUserCompanyID string `json:"signature_user_ccla_company_id"`
- EmailWhitelist []string `json:"email_whitelist"`
- DomainWhitelist []string `json:"domain_whitelist"`
- GitHubWhitelist []string `json:"github_whitelist"`
- GitHubOrgWhitelist []string `json:"github_org_whitelist"`
+ EmailApprovalList []string `json:"email_whitelist"`
+ EmailDomainApprovalList []string `json:"domain_whitelist"`
+ GitHubUsernameApprovalList []string `json:"github_whitelist"`
+ GitHubOrgApprovalList []string `json:"github_org_whitelist"`
+ GitLabUsernameApprovalList []string `json:"gitlab_username_approval_list"`
+ GitLabGroupApprovalList []string `json:"gitlab_group_approval_list"`
SignatureACL []string `json:"signature_acl"`
UserGithubUsername string `json:"user_github_username"`
UserLFUsername string `json:"user_lf_username"`
diff --git a/cla-backend-go/signatures/handlers.go b/cla-backend-go/signatures/handlers.go
index 700a20a44..748f7eb76 100644
--- a/cla-backend-go/signatures/handlers.go
+++ b/cla-backend-go/signatures/handlers.go
@@ -177,7 +177,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
githubAccessToken = ""
}
- ghApprovalList, err := service.GetGithubOrganizationsFromWhitelist(ctx, params.SignatureID, githubAccessToken)
+ ghApprovalList, err := service.GetGithubOrganizationsFromApprovalList(ctx, params.SignatureID, githubAccessToken)
if err != nil {
log.Warnf("error fetching github organization approval list entries v using signature_id: %s, error: %+v",
params.SignatureID, err)
@@ -203,7 +203,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
githubAccessToken = ""
}
- ghApprovalList, err := service.AddGithubOrganizationToWhitelist(ctx, params.SignatureID, params.Body, githubAccessToken)
+ ghApprovalList, err := service.AddGithubOrganizationToApprovalList(ctx, params.SignatureID, params.Body, githubAccessToken)
if err != nil {
log.Warnf("error adding github organization %s using signature_id: %s to the whitelist, error: %+v",
*params.Body.OrganizationID, params.SignatureID, err)
@@ -253,7 +253,7 @@ func Configure(api *operations.ClaAPI, service SignatureService, sessionStore *d
githubAccessToken = ""
}
- ghApprovalList, err := service.DeleteGithubOrganizationFromWhitelist(ctx, params.SignatureID, params.Body, githubAccessToken)
+ ghApprovalList, err := service.DeleteGithubOrganizationFromApprovalList(ctx, params.SignatureID, params.Body, githubAccessToken)
if err != nil {
log.Warnf("error deleting github organization %s using signature_id: %s from the whitelist, error: %+v",
*params.Body.OrganizationID, params.SignatureID, err)
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index 7476d1b79..db5a76043 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -22,7 +22,7 @@ type ApprovalCriteria struct {
GitHubUsername string
}
-//ApprovalList ...
+//ApprovalList data model
type ApprovalList struct {
Criteria string
ApprovalList []string
@@ -31,11 +31,13 @@ type ApprovalList struct {
ClaGroupName string
CompanyID string
Version string
+ EmailApprovals []string
DomainApprovals []string
- GHOrgApprovals []string
GitHubUsernameApprovals []string
- EmailApprovals []string
GHUsernames []string
+ GHOrgApprovals []string
+ GitLabUsernameApprovals []string
+ GitLabGroupApprovals []string
GerritICLAECLAs []string
ICLAs []*models.IclaSignature
ECLAs []*models.Signature
diff --git a/cla-backend-go/signatures/projections.go b/cla-backend-go/signatures/projections.go
index e846ae7f1..6bd143612 100644
--- a/cla-backend-go/signatures/projections.go
+++ b/cla-backend-go/signatures/projections.go
@@ -28,6 +28,8 @@ func buildProjection() expression.ProjectionBuilder {
expression.Name("domain_whitelist"),
expression.Name("github_whitelist"),
expression.Name("github_org_whitelist"),
+ expression.Name("gitlab_username_approval_list"), // added for GitLab support
+ expression.Name("gitlab_project_approval_list"), // added for GitLab support
expression.Name("user_github_username"),
expression.Name("user_lf_username"),
expression.Name("user_name"),
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index eefa0a026..d7dafd59f 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -60,11 +60,11 @@ const (
BigPageSize = 200
)
-// SignatureRepository interface defines the functions for the github whitelist service
+// SignatureRepository interface defines the functions for the the signature repository
type SignatureRepository interface {
- GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string) ([]models.GithubOrg, error)
- AddGithubOrganizationToWhitelist(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
- DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
+ GetGithubOrganizationsFromApprovalList(ctx context.Context, signatureID string) ([]models.GithubOrg, error)
+ AddGithubOrganizationToApprovalList(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
+ DeleteGithubOrganizationFromApprovalList(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
InvalidateProjectRecord(ctx context.Context, signatureID, note string) error
GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
@@ -113,7 +113,7 @@ type repository struct {
signatureTableName string
}
-// NewRepository creates a new instance of the whitelist service
+// NewRepository creates a new instance of the signature repository service
func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.RepositoryInterface, gerritService gerrits.Service) SignatureRepository {
return repository{
stage: stage,
@@ -128,10 +128,10 @@ func NewRepository(awsSession *session.Session, stage string, companyRepo compan
}
}
-// GetGithubOrganizationsFromWhitelist returns a list of GH organizations stored in the whitelist
-func (repo repository) GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string) ([]models.GithubOrg, error) {
+// GetGithubOrganizationsFromApprovalList returns a list of GH organizations stored in the approval list
+func (repo repository) GetGithubOrganizationsFromApprovalList(ctx context.Context, signatureID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "v1.signatures.repository.GetGitHubOrganizationsFromWhitelist",
+ "functionName": "v1.signatures.repository.GetGithubOrganizationsFromApprovalList",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
}
@@ -146,7 +146,7 @@ func (repo repository) GetGithubOrganizationsFromWhitelist(ctx context.Context,
})
if err != nil {
- log.WithFields(f).Warnf("Error retrieving GH organization whitelist for signatureID: %s, error: %v", signatureID, err)
+ log.WithFields(f).Warnf("Error retrieving GH organization approval list for signatureID: %s, error: %v", signatureID, err)
return nil, err
}
@@ -172,16 +172,16 @@ func (repo repository) GetGithubOrganizationsFromWhitelist(ctx context.Context,
return orgs, nil
}
-// AddGithubOrganizationToWhitelist adds the specified GH organization to the whitelist
-func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
+// AddGithubOrganizationToApprovalList adds the specified GH organization to the approval list
+func (repo repository) AddGithubOrganizationToApprovalList(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "v1.signatures.repository.AddGitHubOrganizationToWhitelist",
+ "functionName": "v1.signatures.repository.AddGithubOrganizationToApprovalList",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"GitHubOrganizationID": GitHubOrganizationID,
}
// get item from dynamoDB table
- log.WithFields(f).Debugf("querying database for GitHub organization whitelist using signatureID: %s", signatureID)
+ log.WithFields(f).Debugf("querying database for GitHub organization approval list using signatureID: %s", signatureID)
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(repo.signatureTableName),
@@ -193,7 +193,7 @@ func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, sig
})
if err != nil {
- log.WithFields(f).Warnf("Error retrieving GitHub organization whitelist for signatureID: %s and GH Org: %s, error: %v",
+ log.WithFields(f).Warnf("Error retrieving GitHub organization approval list for signatureID: %s and GH Org: %s, error: %v",
signatureID, GitHubOrganizationID, err)
return nil, err
}
@@ -255,7 +255,7 @@ func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, sig
updatedItemFromMap, ok := updatedValues.Attributes["github_org_whitelist"]
if !ok {
- msg := fmt.Sprintf("unable to fetch updated whitelist organization values for "+
+ msg := fmt.Sprintf("unable to fetch updated github organization approval list values for "+
"organization id: %s for signature: %s - list is empty - returning empty list",
GitHubOrganizationID, signatureID)
log.WithFields(f).Debugf(msg)
@@ -265,10 +265,10 @@ func (repo repository) AddGithubOrganizationToWhitelist(ctx context.Context, sig
return buildResponse(updatedItemFromMap.L), nil
}
-// DeleteGithubOrganizationFromWhitelist removes the specified GH organization from the whitelist
-func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
+// DeleteGithubOrganizationFromApprovalList removes the specified GH organization from the approval list
+func (repo repository) DeleteGithubOrganizationFromApprovalList(ctx context.Context, signatureID, GitHubOrganizationID string) ([]models.GithubOrg, error) {
f := logrus.Fields{
- "functionName": "v1.signatures.repository.DeleteGitHubOrganizationFromWhitelist",
+ "functionName": "v1.signatures.repository.DeleteGithubOrganizationFromApprovalList",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": signatureID,
"GitHubOrganizationID": GitHubOrganizationID,
@@ -284,14 +284,14 @@ func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context
})
if err != nil {
- log.WithFields(f).Warnf("error retrieving GH organization whitelist for signatureID: %s and GH Org: %s, error: %v",
+ log.WithFields(f).Warnf("error retrieving GH organization approval list for signatureID: %s and GH Org: %s, error: %v",
signatureID, GitHubOrganizationID, err)
return nil, err
}
itemFromMap, ok := result.Item["github_org_whitelist"]
if !ok {
- log.WithFields(f).Warnf("unable to remove whitelist organization: %s for signature: %s - list is empty",
+ log.WithFields(f).Warnf("unable to remove github organization approval list entry: %s for signature: %s - list is empty",
GitHubOrganizationID, signatureID)
return nil, errors.New("no github_org_whitelist column")
}
@@ -311,7 +311,7 @@ func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context
// ValidationException: ExpressionAttributeValues contains invalid value: Supplied AttributeValue
// is empty, must contain exactly one of the supported data types for the key)
- log.WithFields(f).Debugf("clearing out github org whitelist for organization: %s for signature: %s - list is empty",
+ log.WithFields(f).Debugf("clearing out github org approval list for organization: %s for signature: %s - list is empty",
GitHubOrganizationID, signatureID)
nullFlag := true
@@ -336,7 +336,7 @@ func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context
_, err = repo.dynamoDBClient.UpdateItem(input)
if err != nil {
- log.WithFields(f).Warnf("error updating github org whitelist to NULL value, error: %v", err)
+ log.WithFields(f).Warnf("error updating github org approva list to NULL value, error: %v", err)
return nil, err
}
@@ -369,13 +369,13 @@ func (repo repository) DeleteGithubOrganizationFromWhitelist(ctx context.Context
updatedValues, err := repo.dynamoDBClient.UpdateItem(input)
if err != nil {
- log.WithFields(f).Warnf("Error updating github org whitelist, error: %v", err)
+ log.WithFields(f).Warnf("Error updating github org approva list, error: %v", err)
return nil, err
}
updatedItemFromMap, ok := updatedValues.Attributes["github_org_whitelist"]
if !ok {
- msg := fmt.Sprintf("unable to fetch updated whitelist organization values for "+
+ msg := fmt.Sprintf("unable to fetch updated approva list organization values for "+
"organization id: %s for signature: %s - list is empty - returning empty list",
GitHubOrganizationID, signatureID)
log.WithFields(f).Debugf(msg)
@@ -2802,12 +2802,7 @@ func (repo repository) removeColumn(ctx context.Context, signatureID, columnName
ExpressionAttributeNames: map[string]*string{
"#" + columnName: aws.String(columnName),
},
- //ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
- // ":a": {
- // S: aws.String("bar"),
- // },
- //},
- UpdateExpression: aws.String("REMOVE #" + columnName), //aws.String("REMOVE github_org_whitelist"),
+ UpdateExpression: aws.String("REMOVE #" + columnName),
ReturnValues: aws.String(dynamodb.ReturnValueNone),
}
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 69d5bb47a..9bf4e84c5 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -45,9 +45,9 @@ type SignatureService interface {
GetUserSignatures(ctx context.Context, params signatures.GetUserSignaturesParams) (*models.Signatures, error)
InvalidateProjectRecords(ctx context.Context, projectID, note string) (int, error)
- GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string, githubAccessToken string) ([]models.GithubOrg, error)
- AddGithubOrganizationToWhitelist(ctx context.Context, signatureID string, whiteListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error)
- DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID string, whiteListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error)
+ GetGithubOrganizationsFromApprovalList(ctx context.Context, signatureID string, githubAccessToken string) ([]models.GithubOrg, error)
+ AddGithubOrganizationToApprovalList(ctx context.Context, signatureID string, approvalListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error)
+ DeleteGithubOrganizationFromApprovalList(ctx context.Context, signatureID string, approvalListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error)
UpdateApprovalList(ctx context.Context, authUser *auth.User, claGroupModel *models.ClaGroup, companyModel *models.Company, claGroupID string, params *models.ApprovalList) (*models.Signature, error)
AddCLAManager(ctx context.Context, signatureID, claManagerID string) (*models.Signature, error)
@@ -66,7 +66,7 @@ type service struct {
githubOrgValidation bool
}
-// NewService creates a new whitelist service
+// NewService creates a new signature service
func NewService(repo SignatureRepository, companyService company.IService, usersService users.Service, eventsService events.Service, githubOrgValidation bool) SignatureService {
return service{
repo,
@@ -196,18 +196,18 @@ func (s service) GetUserSignatures(ctx context.Context, params signatures.GetUse
return userSignatures, nil
}
-// GetGithubOrganizationsFromWhitelist retrieves the organization from the whitelist
-func (s service) GetGithubOrganizationsFromWhitelist(ctx context.Context, signatureID string, githubAccessToken string) ([]models.GithubOrg, error) {
+// GetGithubOrganizationsFromApprovalList retrieves the organization from the approval list
+func (s service) GetGithubOrganizationsFromApprovalList(ctx context.Context, signatureID string, githubAccessToken string) ([]models.GithubOrg, error) {
if signatureID == "" {
- msg := "unable to get GitHub organizations whitelist - signature ID is nil"
+ msg := "unable to get GitHub organizations approval list - signature ID is nil"
log.Warn(msg)
return nil, errors.New(msg)
}
- orgIds, err := s.repo.GetGithubOrganizationsFromWhitelist(ctx, signatureID)
+ orgIds, err := s.repo.GetGithubOrganizationsFromApprovalList(ctx, signatureID)
if err != nil {
- log.Warnf("error loading github organization from whitelist using signatureID: %s, error: %v",
+ log.Warnf("error loading github organization from approval list using signatureID: %s, error: %v",
signatureID, err)
return nil, err
}
@@ -249,18 +249,18 @@ func (s service) GetGithubOrganizationsFromWhitelist(ctx context.Context, signat
return orgIds, nil
}
-// AddGithubOrganizationToWhitelist adds the GH organization to the whitelist
-func (s service) AddGithubOrganizationToWhitelist(ctx context.Context, signatureID string, whiteListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error) {
- organizationID := whiteListParams.OrganizationID
+// AddGithubOrganizationToApprovalList adds the GH organization to the approval list
+func (s service) AddGithubOrganizationToApprovalList(ctx context.Context, signatureID string, approvalListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error) {
+ organizationID := approvalListParams.OrganizationID
if signatureID == "" {
- msg := "unable to add GitHub organization from whitelist - signature ID is nil"
+ msg := "unable to add GitHub organization from approval list - signature ID is nil"
log.Warn(msg)
return nil, errors.New(msg)
}
if organizationID == nil {
- msg := "unable to add GitHub organization from whitelist - organization ID is nil"
+ msg := "unable to add GitHub organization from approval list - organization ID is nil"
log.Warn(msg)
return nil, errors.New(msg)
}
@@ -309,30 +309,30 @@ func (s service) AddGithubOrganizationToWhitelist(ctx context.Context, signature
}
}
- gitHubWhiteList, err := s.repo.AddGithubOrganizationToWhitelist(ctx, signatureID, *organizationID)
+ gitHubOrgApprovalList, err := s.repo.AddGithubOrganizationToApprovalList(ctx, signatureID, *organizationID)
if err != nil {
- log.Warnf("issue adding github organization to white list using signatureID: %s, gh org id: %s, error: %v",
+ log.Warnf("issue adding github organization to approval list using signatureID: %s, gh org id: %s, error: %v",
signatureID, *organizationID, err)
return nil, err
}
- return gitHubWhiteList, nil
+ return gitHubOrgApprovalList, nil
}
-// DeleteGithubOrganizationFromWhitelist deletes the specified GH organization from the whitelist
-func (s service) DeleteGithubOrganizationFromWhitelist(ctx context.Context, signatureID string, whiteListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error) {
+// DeleteGithubOrganizationFromApprovalList deletes the specified GH organization from the approval list
+func (s service) DeleteGithubOrganizationFromApprovalList(ctx context.Context, signatureID string, approvalListParams models.GhOrgWhitelist, githubAccessToken string) ([]models.GithubOrg, error) {
// Extract the payload values
- organizationID := whiteListParams.OrganizationID
+ organizationID := approvalListParams.OrganizationID
if signatureID == "" {
- msg := "unable to delete GitHub organization from whitelist - signature ID is nil"
+ msg := "unable to delete GitHub organization from approval list - signature ID is nil"
log.Warn(msg)
return nil, errors.New(msg)
}
if organizationID == nil {
- msg := "unable to delete GitHub organization from whitelist - organization ID is nil"
+ msg := "unable to delete GitHub organization from approval list - organization ID is nil"
log.Warn(msg)
return nil, errors.New(msg)
}
@@ -381,12 +381,12 @@ func (s service) DeleteGithubOrganizationFromWhitelist(ctx context.Context, sign
}
}
- gitHubWhiteList, err := s.repo.DeleteGithubOrganizationFromWhitelist(ctx, signatureID, *organizationID)
+ gitHubOrgApprovalList, err := s.repo.DeleteGithubOrganizationFromApprovalList(ctx, signatureID, *organizationID)
if err != nil {
return nil, err
}
- return gitHubWhiteList, nil
+ return gitHubOrgApprovalList, nil
}
// UpdateApprovalList service method
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 71efaa668..fc9791eff 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -52,7 +52,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
f := logrus.Fields{
- "functionName": "v2.signatures.handlers.SignaturesGetGitHubOrgWhitelistHandler",
+ "functionName": "v2.signatures.handlers.SignaturesGetSignatureHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"signatureID": params.SignatureID,
}
@@ -208,15 +208,15 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
githubAccessToken = ""
}
- ghWhiteList, err := v1SignatureService.GetGithubOrganizationsFromWhitelist(ctx, params.SignatureID, githubAccessToken)
+ ghOrgApprovalList, err := v1SignatureService.GetGithubOrganizationsFromApprovalList(ctx, params.SignatureID, githubAccessToken)
if err != nil {
- log.WithFields(f).Warnf("error fetching github organization whitelist entries v using signature_id: %s, error: %+v",
+ log.WithFields(f).Warnf("error fetching github organization approval list entries using signature_id: %s, error: %+v",
params.SignatureID, err)
return signatures.NewGetGitHubOrgWhitelistBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
var response []models.GithubOrg
- err = copier.Copy(&response, ghWhiteList)
+ err = copier.Copy(&response, ghOrgApprovalList)
if err != nil {
return signatures.NewGetGitHubOrgWhitelistBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
@@ -254,7 +254,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
return signatures.NewAddGitHubOrgWhitelistBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
- ghApprovalList, err := v1SignatureService.AddGithubOrganizationToWhitelist(ctx, params.SignatureID, input, githubAccessToken)
+ ghApprovalList, err := v1SignatureService.AddGithubOrganizationToApprovalList(ctx, params.SignatureID, input, githubAccessToken)
if err != nil {
log.WithFields(f).Warnf("error adding github organization %s using signature_id: %s to the approval list, error: %+v",
*params.Body.OrganizationID, params.SignatureID, err)
@@ -322,7 +322,7 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
return signatures.NewDeleteGitHubOrgWhitelistBadRequest().WithXRequestID(reqID).WithPayload(errorResponse(reqID, err))
}
- ghApprovalList, err := v1SignatureService.DeleteGithubOrganizationFromWhitelist(ctx, params.SignatureID, input, githubAccessToken)
+ ghApprovalList, err := v1SignatureService.DeleteGithubOrganizationFromApprovalList(ctx, params.SignatureID, input, githubAccessToken)
if err != nil {
log.WithFields(f).Warnf("error deleting github organization %s using signature_id: %s from the approval list, error: %+v",
*params.Body.OrganizationID, params.SignatureID, err)
@@ -1487,68 +1487,6 @@ func isUserHaveAccessToCLAGroupProjects(ctx context.Context, authUser *auth.User
return false
}
-// isUserHaveAccessToCLAProject is a helper function to determine if the user has access to the specified project
-func isUserHaveAccessToCLAProject(ctx context.Context, authUser *auth.User, projectSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool { // nolint
- f := logrus.Fields{
- "functionName": "v2.signatures.handlers.isUserHaveAccessToCLAProject",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "userName": authUser.UserName,
- "userEmail": authUser.Email,
- }
-
- log.WithFields(f).Debugf("testing if user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
- if utils.IsUserAuthorizedForProject(ctx, authUser, projectSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to project SFID: %s...", authUser.UserName, authUser.Email, projectSFID)
- return true
- }
- log.WithFields(f).Debugf("user %s/%s doesn't have direct access to the project SFID: %s - loading CLA Group from project id...", authUser.UserName, authUser.Email, projectSFID)
-
- log.WithFields(f).Debug("loading CLA Group from project id...")
- projectCLAGroupModel, err := projectClaGroupsRepo.GetClaGroupIDForProject(ctx, projectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - returning false")
- return false
- }
- if projectCLAGroupModel == nil {
- log.WithFields(f).WithError(err).Warnf("problem loading project -> cla group mapping - no mapping found - returning false")
- return false
- }
-
- f["foundationSFID"] = projectCLAGroupModel.FoundationSFID
- log.WithFields(f).Debug("testing if user has access to parent foundation...")
- if utils.IsUserAuthorizedForProjectTree(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to parent foundation tree...")
- return true
- }
- if utils.IsUserAuthorizedForProject(ctx, authUser, projectCLAGroupModel.FoundationSFID, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debug("user has access to parent foundation...")
- return true
- }
- log.WithFields(f).Debug("user does not have access to parent foundation...")
-
- // Lookup the other project IDs for the CLA Group
- log.WithFields(f).Debug("looking up other projects associated with the CLA Group...")
- projectCLAGroupModels, err := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, projectCLAGroupModel.ClaGroupID)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem loading project cla group mappings by CLA Group ID - returning false")
- return false
- }
-
- projectSFIDs := getProjectIDsFromModels(f, projectCLAGroupModel.FoundationSFID, projectCLAGroupModels)
- projectSFIDsCSV := strings.Join(projectSFIDs, ",") // Create a project SFID CSV for printout
- f["projectIDs"] = projectSFIDsCSV
-
- log.WithFields(f).Debugf("testing if user %s/%s has access to any cla group projects: %s", authUser.UserName, authUser.Email, projectSFIDsCSV)
- if utils.IsUserAuthorizedForAnyProjects(ctx, authUser, projectSFIDs, utils.ALLOW_ADMIN_SCOPE) {
- log.WithFields(f).Debugf("user %s/%s has access to at least of of the projects: %s...", authUser.UserName, authUser.Email, projectSFIDsCSV)
- return true
- }
-
- log.WithFields(f).Debug("exhausted project checks - user does not have access to project")
- return false
-}
-
// isUserHaveAccessToCLAProjectOrganization is a helper function to determine if the user has access to the specified project and organization
func isUserHaveAccessToCLAProjectOrganization(ctx context.Context, authUser *auth.User, projectSFID, organizationSFID string, projectClaGroupsRepo projects_cla_groups.Repository) bool {
f := logrus.Fields{
From d3ad0a06d760ad4a170d027f3e1b00314ad18f6b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 23 Jul 2021 13:01:35 -0500
Subject: [PATCH 0364/1276] Updated Enroll/Unenroll Response Code (#3048)
- Changed 500 internal server error to a 400 bad request
- Added custom enroll/unenroll error structure/object to allow us to
determine the type of error
Signed-off-by: David Deal
---
cla-backend-go/utils/errors.go | 40 ++++++++++++++++++++++++
cla-backend-go/v2/cla_groups/handlers.go | 16 ++++++++++
cla-backend-go/v2/cla_groups/service.go | 28 ++++++++++++-----
3 files changed, 76 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index a54444e1d..62ff6d6dc 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -366,3 +366,43 @@ func (e *InvalidCLAType) Error() string {
func (e *InvalidCLAType) Unwrap() error {
return e.Err
}
+
+// EnrollError is an error model for representing enroll/un-enroll errors
+type EnrollError struct {
+ Type string
+ Message string
+ Err error
+}
+
+// Error is an error string function for enroll/un-enroll error
+func (e *EnrollError) Error() string {
+ if e.Err == nil {
+ return fmt.Sprintf("%s validation error: %s", e.Type, e.Message)
+ }
+ return fmt.Sprintf("%s validation error: %s due to error: %+v", e.Type, e.Message, e.Err)
+}
+
+// Unwrap method returns its contained error
+func (e *EnrollError) Unwrap() error {
+ return e.Err
+}
+
+// EnrollValidationError is an error model for representing enroll/un-enroll validation errors
+type EnrollValidationError struct {
+ Type string
+ Message string
+ Err error
+}
+
+// Error is an error string function for enroll/un-enroll validation error
+func (e *EnrollValidationError) Error() string {
+ if e.Err == nil {
+ return fmt.Sprintf("%s validation error: %s", e.Type, e.Message)
+ }
+ return fmt.Sprintf("%s validation error: %s due to error: %+v", e.Type, e.Message, e.Err)
+}
+
+// Unwrap method returns its contained error
+func (e *EnrollValidationError) Unwrap() error {
+ return e.Err
+}
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index a2a2d7e2f..401194f83 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -332,6 +332,14 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
})
if enrollCLAGroupErr != nil {
+ if _, ok := enrollCLAGroupErr.(*utils.EnrollValidationError); ok {
+ return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", enrollCLAGroupErr))
+ }
+ if _, ok := enrollCLAGroupErr.(*utils.EnrollError); ok {
+ return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", enrollCLAGroupErr))
+ }
if strings.Contains(enrollCLAGroupErr.Error(), "bad request") {
return cla_group.NewEnrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", enrollCLAGroupErr))
@@ -384,6 +392,14 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
ProjectSFIDList: params.ProjectSFIDList,
})
if err != nil {
+ if _, ok := err.(*utils.EnrollValidationError); ok {
+ return cla_group.NewUnenrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", err))
+ }
+ if _, ok := err.(*utils.EnrollError); ok {
+ return cla_group.NewUnenrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, "unable to enroll projects in CLA Group", err))
+ }
if strings.Contains(err.Error(), "bad request") {
return cla_group.NewUnenrollProjectsBadRequest().WithXRequestID(reqID).WithPayload(
utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to unenroll projects for CLA Group ID: %s", params.ClaGroupID), err))
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 53bbb9557..10ec2b9ca 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -980,8 +980,11 @@ func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollP
log.WithFields(f).Debug("validating enroll project input")
err := s.validateEnrollProjectsInput(ctx, request.FoundationSFID, request.ProjectSFIDList)
if err != nil {
- log.WithFields(f).Warnf("validating enroll project input failed. error = %s", err)
- return err
+ return &utils.EnrollValidationError{
+ Type: "enroll",
+ Message: "invalid project ID value",
+ Err: err,
+ }
}
// Setup a wait group to enroll and enable CLA service - we'll want to work quickly here
@@ -1020,8 +1023,11 @@ func (s *service) EnrollProjectsInClaGroup(ctx context.Context, request *EnrollP
// Wait until all go routines are done
wg.Wait()
if len(errorList) > 0 {
- log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when enrolling and enabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList))
- return errorList[0]
+ return &utils.EnrollError{
+ Type: "enroll",
+ Message: fmt.Sprintf("encountered %d errors when enrolling and disabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList)),
+ Err: errorList[0],
+ }
}
return nil
@@ -1042,8 +1048,11 @@ func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *Unenr
log.WithFields(f).Debug("validating unenroll project input")
err := s.validateUnenrollProjectsInput(ctx, request.FoundationSFID, request.ProjectSFIDList)
if err != nil {
- log.WithFields(f).Warnf("validating unenroll project input failed. error = %s", err)
- return err
+ return &utils.EnrollValidationError{
+ Type: "unenroll",
+ Message: "invalid project ID value",
+ Err: err,
+ }
}
// Setup a wait group to enroll and enable CLA service - we'll want to work quickly here
@@ -1081,8 +1090,11 @@ func (s *service) UnenrollProjectsInClaGroup(ctx context.Context, request *Unenr
// Wait until all go routines are done
wg.Wait()
if len(errorList) > 0 {
- log.WithFields(f).WithError(errorList[0]).Warnf("encountered %d errors when unenrolling and disabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList))
- return errorList[0]
+ return &utils.EnrollError{
+ Type: "unenroll",
+ Message: fmt.Sprintf("encountered %d errors when unenrolling and disabling CLA service for %d projects", len(errorList), len(request.ProjectSFIDList)),
+ Err: errorList[0],
+ }
}
return nil
From 537b4fbf997d424b9694c7017f870508d533daa6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 23 Jul 2021 15:57:14 -0500
Subject: [PATCH 0365/1276] Resolved GitHub Org Update Issue (#3049)
- Removed trailing comma in syntax of query
- Normalized API function calls for Github -> GitHub
- Updated event log message when updating to include branch protection value
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 27 +++----
.../github_organizations/handlers.go | 8 +--
cla-backend-go/github_organizations/mock.go | 60 ++++++++--------
.../github_organizations/repository.go | 70 +++++++++----------
.../github_organizations/service.go | 50 ++++++-------
.../repositories/mock/mock_service.go | 22 +++---
cla-backend-go/repositories/service.go | 8 +--
cla-backend-go/signatures/repository.go | 2 +-
cla-backend-go/v2/dynamo_events/autoenable.go | 2 +-
.../v2/dynamo_events/github_repository.go | 4 +-
cla-backend-go/v2/github_activity/service.go | 4 +-
.../v2/github_organizations/handlers.go | 6 +-
.../v2/github_organizations/service.go | 8 +--
cla-backend-go/v2/repositories/service.go | 14 ++--
14 files changed, 145 insertions(+), 140 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index eec9bcd3f..f9f8a2e11 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -185,9 +185,10 @@ type GitHubOrganizationDeletedEventData struct {
// GitHubOrganizationUpdatedEventData data model
type GitHubOrganizationUpdatedEventData struct {
- GitHubOrganizationName string
- AutoEnabled bool
- AutoEnabledClaGroupID string
+ GitHubOrganizationName string
+ AutoEnabled bool
+ AutoEnabledClaGroupID string
+ BranchProtectionEnabled bool
}
// CCLAApprovalListRequestCreatedEventData data model
@@ -635,10 +636,14 @@ func (ed *GitHubOrganizationDeletedEventData) GetEventDetailsString(args *LogEve
// GetEventDetailsString returns the details string for this event
func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization:%s was updated with auto-enabled: %t",
- ed.GitHubOrganizationName, ed.AutoEnabled)
+ data := fmt.Sprintf("The GitHub Organization '%s' was updated", ed.GitHubOrganizationName)
+ data = data + fmt.Sprintf(" with auto-enabled set to %t", ed.AutoEnabled)
+ data = data + fmt.Sprintf(" with branch protection set to %t", ed.BranchProtectionEnabled)
if ed.AutoEnabledClaGroupID != "" {
- data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group ID value of %s", ed.AutoEnabledClaGroupID)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for project %s", args.ProjectName)
}
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
@@ -1525,13 +1530,11 @@ func (ed *GitHubOrganizationDeletedEventData) GetEventSummaryString(args *LogEve
// GetEventSummaryString returns the summary string for this event
func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization: %s was updated with auto-enabled: %t",
- ed.GitHubOrganizationName, ed.AutoEnabled)
+ data := fmt.Sprintf("The GitHub Organization '%s' was updated", ed.GitHubOrganizationName)
+ data = data + fmt.Sprintf(" with auto-enabled set to %t", ed.AutoEnabled)
+ data = data + fmt.Sprintf(" with branch protection set to %t", ed.BranchProtectionEnabled)
if ed.AutoEnabledClaGroupID != "" {
- data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
- }
- if args.CLAGroupName != "" {
- data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group ID value of %s", ed.AutoEnabledClaGroupID)
}
if args.ProjectName != "" {
data = data + fmt.Sprintf(" for project %s", args.ProjectName)
diff --git a/cla-backend-go/github_organizations/handlers.go b/cla-backend-go/github_organizations/handlers.go
index 4f23a1fb6..c09ed5ffb 100644
--- a/cla-backend-go/github_organizations/handlers.go
+++ b/cla-backend-go/github_organizations/handlers.go
@@ -28,7 +28,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
- result, err := service.GetGithubOrganizations(ctx, params.ProjectSFID)
+ result, err := service.GetGitHubOrganizations(ctx, params.ProjectSFID)
if err != nil {
if _, ok := err.(*v2ProjectServiceClient.GetProjectNotFound); ok {
return github_organizations.NewGetProjectGithubOrganizationsNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
@@ -65,7 +65,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
return github_organizations.NewAddProjectGithubOrganizationNotFound().WithPayload(errorResponse(err))
}
- result, err := service.AddGithubOrganization(ctx, params.ProjectSFID, params.Body)
+ result, err := service.AddGitHubOrganization(ctx, params.ProjectSFID, params.Body)
if err != nil {
if _, ok := err.(*v2ProjectServiceClient.GetProjectNotFound); ok {
return github_organizations.NewAddProjectGithubOrganizationNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
@@ -114,7 +114,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
return github_organizations.NewDeleteProjectGithubOrganizationNotFound().WithPayload(errorResponse(err))
}
- err = service.DeleteGithubOrganization(ctx, params.ProjectSFID, params.OrgName)
+ err = service.DeleteGitHubOrganization(ctx, params.ProjectSFID, params.OrgName)
if err != nil {
if _, ok := err.(*v2ProjectServiceClient.GetProjectNotFound); ok {
return github_organizations.NewDeleteProjectGithubOrganizationNotFound().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
@@ -155,7 +155,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
})
}
- err := service.UpdateGithubOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ err := service.UpdateGitHubOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
if err != nil {
if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
return github_organizations.NewUpdateProjectGithubOrganizationConfigNotFound().WithPayload(errorResponse(err))
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index 01c35588c..12e1014e9 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -39,10 +39,10 @@ func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
return m.recorder
}
-// AddGithubOrganization mocks base method
-func (m *MockRepository) AddGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+// AddGitHubOrganization mocks base method
+func (m *MockRepository) AddGitHubOrganization(arg0 context.Context, arg1, arg2 string, arg3 *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddGithubOrganization", arg0, arg1, arg2, arg3)
+ ret := m.ctrl.Call(m, "AddGitHubOrganization", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*models.GithubOrganization)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -51,13 +51,13 @@ func (m *MockRepository) AddGithubOrganization(arg0 context.Context, arg1, arg2
// AddGithubOrganization indicates an expected call of AddGithubOrganization
func (mr *MockRepositoryMockRecorder) AddGithubOrganization(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubOrganization", reflect.TypeOf((*MockRepository)(nil).AddGithubOrganization), arg0, arg1, arg2, arg3)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGitHubOrganization", reflect.TypeOf((*MockRepository)(nil).AddGitHubOrganization), arg0, arg1, arg2, arg3)
}
-// DeleteGithubOrganization mocks base method
-func (m *MockRepository) DeleteGithubOrganization(arg0 context.Context, arg1, arg2 string) error {
+// DeleteGitHubOrganization mocks base method
+func (m *MockRepository) DeleteGitHubOrganization(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DeleteGithubOrganization", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "DeleteGitHubOrganization", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
@@ -65,13 +65,13 @@ func (m *MockRepository) DeleteGithubOrganization(arg0 context.Context, arg1, ar
// DeleteGithubOrganization indicates an expected call of DeleteGithubOrganization
func (mr *MockRepositoryMockRecorder) DeleteGithubOrganization(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganization", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganization), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGitHubOrganization", reflect.TypeOf((*MockRepository)(nil).DeleteGitHubOrganization), arg0, arg1, arg2)
}
// DeleteGithubOrganizationByParent mocks base method
-func (m *MockRepository) DeleteGithubOrganizationByParent(arg0 context.Context, arg1, arg2 string) error {
+func (m *MockRepository) DeleteGitHubOrganizationByParent(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DeleteGithubOrganizationByParent", arg0, arg1, arg2)
+ ret := m.ctrl.Call(m, "DeleteGitHubOrganizationByParent", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
@@ -79,13 +79,13 @@ func (m *MockRepository) DeleteGithubOrganizationByParent(arg0 context.Context,
// DeleteGithubOrganizationByParent indicates an expected call of DeleteGithubOrganizationByParent
func (mr *MockRepositoryMockRecorder) DeleteGithubOrganizationByParent(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGithubOrganizationByParent", reflect.TypeOf((*MockRepository)(nil).DeleteGithubOrganizationByParent), arg0, arg1, arg2)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGitHubOrganizationByParent", reflect.TypeOf((*MockRepository)(nil).DeleteGitHubOrganizationByParent), arg0, arg1, arg2)
}
// GetGithubOrganization mocks base method
-func (m *MockRepository) GetGithubOrganization(arg0 context.Context, arg1 string) (*models.GithubOrganization, error) {
+func (m *MockRepository) GetGitHubOrganization(arg0 context.Context, arg1 string) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganization", arg0, arg1)
+ ret := m.ctrl.Call(m, "GetGitHubOrganization", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganization)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -94,13 +94,13 @@ func (m *MockRepository) GetGithubOrganization(arg0 context.Context, arg1 string
// GetGithubOrganization indicates an expected call of GetGithubOrganization
func (mr *MockRepositoryMockRecorder) GetGithubOrganization(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganization", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganization), arg0, arg1)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganization", reflect.TypeOf((*MockRepository)(nil).GetGitHubOrganization), arg0, arg1)
}
-// GetGithubOrganizationByName mocks base method
-func (m *MockRepository) GetGithubOrganizationByName(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizationByName mocks base method
+func (m *MockRepository) GetGitHubOrganizationByName(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizationByName", arg0, arg1)
+ ret := m.ctrl.Call(m, "GetGitHubOrganizationByName", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -109,13 +109,13 @@ func (m *MockRepository) GetGithubOrganizationByName(arg0 context.Context, arg1
// GetGithubOrganizationByName indicates an expected call of GetGithubOrganizationByName
func (mr *MockRepositoryMockRecorder) GetGithubOrganizationByName(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationByName", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationByName), arg0, arg1)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganizationByName", reflect.TypeOf((*MockRepository)(nil).GetGitHubOrganizationByName), arg0, arg1)
}
-// GetGithubOrganizations mocks base method
-func (m *MockRepository) GetGithubOrganizations(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizations mocks base method
+func (m *MockRepository) GetGitHubOrganizations(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizations", arg0, arg1)
+ ret := m.ctrl.Call(m, "GetGitHubOrganizations", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -124,13 +124,13 @@ func (m *MockRepository) GetGithubOrganizations(arg0 context.Context, arg1 strin
// GetGithubOrganizations indicates an expected call of GetGithubOrganizations
func (mr *MockRepositoryMockRecorder) GetGithubOrganizations(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizations", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizations), arg0, arg1)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganizations", reflect.TypeOf((*MockRepository)(nil).GetGitHubOrganizations), arg0, arg1)
}
-// GetGithubOrganizationsByParent mocks base method
-func (m *MockRepository) GetGithubOrganizationsByParent(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizationsByParent mocks base method
+func (m *MockRepository) GetGitHubOrganizationsByParent(arg0 context.Context, arg1 string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizationsByParent", arg0, arg1)
+ ret := m.ctrl.Call(m, "GetGitHubOrganizationsByParent", arg0, arg1)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -139,13 +139,13 @@ func (m *MockRepository) GetGithubOrganizationsByParent(arg0 context.Context, ar
// GetGithubOrganizationsByParent indicates an expected call of GetGithubOrganizationsByParent
func (mr *MockRepositoryMockRecorder) GetGithubOrganizationsByParent(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationsByParent", reflect.TypeOf((*MockRepository)(nil).GetGithubOrganizationsByParent), arg0, arg1)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganizationsByParent", reflect.TypeOf((*MockRepository)(nil).GetGitHubOrganizationsByParent), arg0, arg1)
}
-// UpdateGithubOrganization mocks base method
-func (m *MockRepository) UpdateGithubOrganization(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 string, arg5 bool, arg6 *bool) error {
+// UpdateGitHubOrganization mocks base method
+func (m *MockRepository) UpdateGitHubOrganization(arg0 context.Context, arg1, arg2 string, arg3 bool, arg4 string, arg5 bool, arg6 *bool) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateGithubOrganization", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
+ ret := m.ctrl.Call(m, "UpdateGitHubOrganization", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
ret0, _ := ret[0].(error)
return ret0
}
@@ -153,5 +153,5 @@ func (m *MockRepository) UpdateGithubOrganization(arg0 context.Context, arg1, ar
// UpdateGithubOrganization indicates an expected call of UpdateGithubOrganization
func (mr *MockRepositoryMockRecorder) UpdateGithubOrganization(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGithubOrganization), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGitHubOrganization", reflect.TypeOf((*MockRepository)(nil).UpdateGitHubOrganization), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
}
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 2f6bc5a3c..35c104c50 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -30,21 +30,21 @@ const (
ProjectSFIDOrganizationNameIndex = "project-sfid-organization-name-index"
)
-// errors
var (
+ // ErrOrganizationDoesNotExist organization does not exist error
ErrOrganizationDoesNotExist = errors.New("github organization does not exist in cla")
)
// RepositoryInterface interface defines the functions for the github organizations data model
type RepositoryInterface interface {
- AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
- GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
- GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
- GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
- GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error)
- UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
- DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
- DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error
+ AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
+ GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
+ GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
+ GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
+ GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error)
+ UpdateGitHubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
+ DeleteGitHubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
+ DeleteGitHubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error
}
// Repository object/struct
@@ -63,8 +63,8 @@ func NewRepository(awsSession *session.Session, stage string) Repository {
}
}
-// AddGithubOrganization add github organization logic
-func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+// AddGitHubOrganization add github organization logic
+func (repo Repository) AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -76,7 +76,7 @@ func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectS
}
// First, let's check to see if we have an existing github organization with the same name
- existingRecord, getErr := repo.GetGithubOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
+ existingRecord, getErr := repo.GetGitHubOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
if getErr != nil {
log.WithFields(f).WithError(getErr).Debug("unable to locate existing github organization by name")
}
@@ -111,7 +111,7 @@ func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectS
// Attempt to simply update the existing record - we should only have one
// activate GH org by updating the enabled flag
enabled := true
- updateErr := repo.UpdateGithubOrganization(ctx,
+ updateErr := repo.UpdateGitHubOrganization(ctx,
projectSFID,
utils.StringValue(input.OrganizationName),
autoEnabled,
@@ -126,7 +126,7 @@ func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectS
// we could simply update the record we initially loaded or simply query the updated record again...
// we're using a key lookup, so it should be fast...
- existingUpdatedRecord, getUpdatedRecordErr := repo.GetGithubOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
+ existingUpdatedRecord, getUpdatedRecordErr := repo.GetGitHubOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
if getUpdatedRecordErr != nil {
log.WithFields(f).WithError(getUpdatedRecordErr).Warn("unable to locate existing github organization by name")
return nil, getUpdatedRecordErr
@@ -189,8 +189,8 @@ func (repo Repository) AddGithubOrganization(ctx context.Context, parentProjectS
return ToModel(githubOrg), nil
}
-// GetGithubOrganizations get github organizations based on the project SFID
-func (repo Repository) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizations get github organizations based on the project SFID
+func (repo Repository) GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -245,10 +245,10 @@ func (repo Repository) GetGithubOrganizations(ctx context.Context, projectSFID s
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-// GetGithubOrganizationsByParent returns a list of github organizations by parent project SFID
-func (repo Repository) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizationsByParent returns a list of github organizations by parent project SFID
+func (repo Repository) GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
- "functionName": "v1.github_organizations.repository.GetGithubOrganizationsByParent",
+ "functionName": "v1.github_organizations.repository.GetGitHubOrganizationsByParent",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
}
@@ -297,8 +297,8 @@ func (repo Repository) GetGithubOrganizationsByParent(ctx context.Context, paren
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-// GetGithubOrganizationByName get github organization by name
-func (repo Repository) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizationByName get github organization by name
+func (repo Repository) GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -346,8 +346,8 @@ func (repo Repository) GetGithubOrganizationByName(ctx context.Context, githubOr
return &models.GithubOrganizations{List: ghOrgList}, nil
}
-// GetGithubOrganization by organization name
-func (repo Repository) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
+// GetGitHubOrganization by organization name
+func (repo Repository) GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -380,8 +380,8 @@ func (repo Repository) GetGithubOrganization(ctx context.Context, githubOrganiza
return ToModel(&org), nil
}
-// UpdateGithubOrganization updates the specified GitHub organization based on the update model provided
-func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
+// UpdateGitHubOrganization updates the specified GitHub organization based on the update model provided
+func (repo Repository) UpdateGitHubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.UpdateGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -394,7 +394,7 @@ func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID
}
_, currentTime := utils.CurrentTime()
- githubOrg, lookupErr := repo.GetGithubOrganization(ctx, organizationName)
+ githubOrg, lookupErr := repo.GetGitHubOrganization(ctx, organizationName)
if lookupErr != nil {
log.WithFields(f).Warnf("error looking up GitHub organization by name, error: %+v", lookupErr)
return lookupErr
@@ -425,14 +425,14 @@ func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID
S: aws.String(currentTime),
},
}
- updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m,"
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m"
if enabled != nil {
expressionAttributeNames["#E"] = aws.String("enabled")
expressionAttributeValues[":e"] = &dynamodb.AttributeValue{
BOOL: aws.Bool(*enabled),
}
- updateExpression = updateExpression + " #E = :e "
+ updateExpression = updateExpression + ", #E = :e "
}
input := &dynamodb.UpdateItemInput{
@@ -447,7 +447,7 @@ func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID
TableName: aws.String(repo.githubOrgTableName),
}
- log.WithFields(f).Debug("updating github organization record...")
+ log.WithFields(f).Debugf("updating github organization record: %+v", input)
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
log.WithFields(f).Warnf("unable to update GitHub organization record, error: %+v", updateErr)
@@ -457,8 +457,8 @@ func (repo Repository) UpdateGithubOrganization(ctx context.Context, projectSFID
return nil
}
-// DeleteGithubOrganization deletes the github organization by project SFID
-func (repo Repository) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
+// DeleteGitHubOrganization deletes the github organization by project SFID
+func (repo Repository) DeleteGitHubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -467,7 +467,7 @@ func (repo Repository) DeleteGithubOrganization(ctx context.Context, projectSFID
}
var githubOrganizationName string
- orgs, orgErr := repo.GetGithubOrganizations(ctx, projectSFID)
+ orgs, orgErr := repo.GetGitHubOrganizations(ctx, projectSFID)
if orgErr != nil {
errMsg := fmt.Sprintf("github organization is not found using projectSFID: %s, error: %+v", projectSFID, orgErr)
log.WithFields(f).Warn(errMsg)
@@ -520,8 +520,8 @@ func (repo Repository) DeleteGithubOrganization(ctx context.Context, projectSFID
return nil
}
-// DeleteGithubOrganizationByParent deletes the github organization by parent SFID
-func (repo Repository) DeleteGithubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error {
+// DeleteGitHubOrganizationByParent deletes the github organization by parent SFID
+func (repo Repository) DeleteGitHubOrganizationByParent(ctx context.Context, parentProjectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -530,7 +530,7 @@ func (repo Repository) DeleteGithubOrganizationByParent(ctx context.Context, par
}
var githubOrganizationName string
- orgs, orgErr := repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
+ orgs, orgErr := repo.GetGitHubOrganizationsByParent(ctx, parentProjectSFID)
if orgErr != nil {
errMsg := fmt.Sprintf("github organization is not found using parentProjectSFID %s, error: - %+v", parentProjectSFID, orgErr)
log.WithFields(f).Warn(errMsg)
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index e001d26b9..bb865139e 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -21,12 +21,12 @@ import (
// ServiceInterface contains functions of GithubOrganizations service
type ServiceInterface interface {
- AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
- GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
- GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
- GetGithubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error)
- UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
- DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
+ AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
+ GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
+ GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
+ GetGitHubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error)
+ UpdateGitHubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
+ DeleteGitHubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
RemoveDuplicates(input []*models.GithubOrganization) []*models.GithubOrganization
}
@@ -46,8 +46,8 @@ func NewService(repo RepositoryInterface, ghRepository repositories.Repository,
}
}
-// AddGithubOrganization adds the github organization for the specified project
-func (s Service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+// AddGitHubOrganization adds the github organization for the specified project
+func (s Service) AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -70,11 +70,11 @@ func (s Service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
}
- return s.repo.AddGithubOrganization(ctx, parentProjectSFID, projectSFID, input)
+ return s.repo.AddGitHubOrganization(ctx, parentProjectSFID, projectSFID, input)
}
-// GetGithubOrganizations returns the github organization for the specified project
-func (s Service) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizations returns the github organization for the specified project
+func (s Service) GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
f := logrus.Fields{
"functionName": "GetGitHubOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -85,7 +85,7 @@ func (s Service) GetGithubOrganizations(ctx context.Context, projectSFID string)
var gitHubOrgModels = models.GithubOrganizations{}
var githubOrgs = make([]*models.GithubOrganization, 0)
- projectGithubModels, err := s.repo.GetGithubOrganizations(ctx, projectSFID)
+ projectGithubModels, err := s.repo.GetGitHubOrganizations(ctx, projectSFID)
if err != nil {
log.WithFields(f).Warnf("problem fetching github organizations by projectSFID, error: %+v", err)
return nil, err
@@ -113,7 +113,7 @@ func (s Service) GetGithubOrganizations(ctx context.Context, projectSFID string)
if parentProjectSFID != projectSFID && (projectDetails != nil && !utils.IsProjectHasRootParent(projectDetails)) {
log.WithFields(f).Debugf("found parent of projectSFID: %s to be %s. Searching github organization by parent SFID: %s...", projectSFID, parentProjectSFID, parentProjectSFID)
- parentGithubModels, parentErr := s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
+ parentGithubModels, parentErr := s.repo.GetGitHubOrganizationsByParent(ctx, parentProjectSFID)
if parentErr != nil {
log.WithFields(f).Warnf("problem fetching github organizations by paarent projectSFID: %s , error: %+v", parentProjectSFID, err)
return nil, parentErr
@@ -133,20 +133,20 @@ func (s Service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return &gitHubOrgModels, err
}
-// GetGithubOrganizationsByParent returns the github organizations for the specified parent project SFID
-func (s Service) GetGithubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
- return s.repo.GetGithubOrganizationsByParent(ctx, parentProjectSFID)
+// GetGitHubOrganizationsByParent returns the github organizations for the specified parent project SFID
+func (s Service) GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error) {
+ return s.repo.GetGitHubOrganizationsByParent(ctx, parentProjectSFID)
}
-// GetGithubOrganizationByName returns the github organizations for the specified github organization name
-func (s Service) GetGithubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error) {
+// GetGitHubOrganizationByName returns the github organizations for the specified github organization name
+func (s Service) GetGitHubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"githubOrgName": githubOrgName,
}
- gitHubOrgs, err := s.repo.GetGithubOrganizationByName(ctx, githubOrgName)
+ gitHubOrgs, err := s.repo.GetGitHubOrganizationByName(ctx, githubOrgName)
if err != nil {
log.WithFields(f).Warnf("problem fetching github organizations by name, error: %+v", err)
return nil, err
@@ -163,19 +163,19 @@ func (s Service) GetGithubOrganizationByName(ctx context.Context, githubOrgName
return gitHubOrgs.List[0], err
}
-// UpdateGithubOrganization updates the specified github organization based on the project SFID, organization name provided values
-func (s Service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+// UpdateGitHubOrganization updates the specified github organization based on the project SFID, organization name provided values
+func (s Service) UpdateGitHubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
if _, err := s.claRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
return err
}
}
- return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
+ return s.repo.UpdateGitHubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
}
-// DeleteGithubOrganization removes the specified github organization under the projectSFID
-func (s Service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
+// DeleteGitHubOrganization removes the specified github organization under the projectSFID
+func (s Service) DeleteGitHubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
f := logrus.Fields{
"functionName": "DeleteGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -196,7 +196,7 @@ func (s Service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
return err
}
- return s.repo.DeleteGithubOrganization(ctx, projectSFID, githubOrgName)
+ return s.repo.DeleteGitHubOrganization(ctx, projectSFID, githubOrgName)
}
// RemoveDuplicates removes any duplicates from the specified list
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index 4d6e8067f..5363e52a2 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -238,10 +238,10 @@ func (m *MockGithubOrgRepo) EXPECT() *MockGithubOrgRepoMockRecorder {
return m.recorder
}
-// GetGithubOrganizationByName mocks base method
-func (m *MockGithubOrgRepo) GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizationByName mocks base method
+func (m *MockGithubOrgRepo) GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizationByName", ctx, githubOrganizationName)
+ ret := m.ctrl.Call(m, "GetGitHubOrganizationByName", ctx, githubOrganizationName)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -250,13 +250,13 @@ func (m *MockGithubOrgRepo) GetGithubOrganizationByName(ctx context.Context, git
// GetGithubOrganizationByName indicates an expected call of GetGithubOrganizationByName
func (mr *MockGithubOrgRepoMockRecorder) GetGithubOrganizationByName(ctx, githubOrganizationName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizationByName", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGithubOrganizationByName), ctx, githubOrganizationName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganizationByName", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGitHubOrganizationByName), ctx, githubOrganizationName)
}
// GetGithubOrganization mocks base method
-func (m *MockGithubOrgRepo) GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
+func (m *MockGithubOrgRepo) GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganization", ctx, githubOrganizationName)
+ ret := m.ctrl.Call(m, "GetGitHubOrganization", ctx, githubOrganizationName)
ret0, _ := ret[0].(*models.GithubOrganization)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -265,13 +265,13 @@ func (m *MockGithubOrgRepo) GetGithubOrganization(ctx context.Context, githubOrg
// GetGithubOrganization indicates an expected call of GetGithubOrganization
func (mr *MockGithubOrgRepoMockRecorder) GetGithubOrganization(ctx, githubOrganizationName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganization", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGithubOrganization), ctx, githubOrganizationName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganization", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGitHubOrganization), ctx, githubOrganizationName)
}
-// GetGithubOrganizations mocks base method
-func (m *MockGithubOrgRepo) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
+// GetGitHubOrganizations mocks base method
+func (m *MockGithubOrgRepo) GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetGithubOrganizations", ctx, projectSFID)
+ ret := m.ctrl.Call(m, "GetGitHubOrganizations", ctx, projectSFID)
ret0, _ := ret[0].(*models.GithubOrganizations)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -280,5 +280,5 @@ func (m *MockGithubOrgRepo) GetGithubOrganizations(ctx context.Context, projectS
// GetGithubOrganizations indicates an expected call of GetGithubOrganizations
func (mr *MockGithubOrgRepoMockRecorder) GetGithubOrganizations(ctx, projectSFID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGithubOrganizations", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGithubOrganizations), ctx, projectSFID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGitHubOrganizations", reflect.TypeOf((*MockGithubOrgRepo)(nil).GetGitHubOrganizations), ctx, projectSFID)
}
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index 054e14436..ba971d2f9 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -38,9 +38,9 @@ type Service interface {
// GithubOrgRepo provide method to get github organization by name
type GithubOrgRepo interface {
- GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error)
- GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
- GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
+ GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*models.GithubOrganizations, error)
+ GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
+ GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
}
type service struct {
@@ -88,7 +88,7 @@ func (s *service) AddGithubRepository(ctx context.Context, externalProjectID str
return nil, projectErr
}
- org, err := s.ghOrgRepo.GetGithubOrganizationByName(ctx, utils.StringValue(input.RepositoryOrganizationName))
+ org, err := s.ghOrgRepo.GetGitHubOrganizationByName(ctx, utils.StringValue(input.RepositoryOrganizationName))
if err != nil {
log.WithFields(f).WithError(err).Warnf("problem loading github organization by name: %s", utils.StringValue(input.RepositoryOrganizationName))
return nil, err
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index d7dafd59f..f937e432d 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -2466,7 +2466,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
for _, ghOrgRepo := range ghOrgRepositories {
- ghOrg, getGHOrgErr := repo.ghOrgRepo.GetGithubOrganization(ctx, ghOrgRepo.RepositoryOrganizationName)
+ ghOrg, getGHOrgErr := repo.ghOrgRepo.GetGitHubOrganization(ctx, ghOrgRepo.RepositoryOrganizationName)
if getGHOrgErr != nil {
msg := fmt.Sprintf("unable to get gh org by name: %s ", ghOrgRepo.RepositoryOrganizationName)
log.WithFields(f).WithError(getGHOrgErr).Warn(msg)
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index 06fa65955..6eb4a1cf4 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -75,7 +75,7 @@ func (a *autoEnableServiceProvider) CreateAutoEnabledRepository(repo *github.Rep
}
organizationName := strings.Split(repositoryFullName, "/")[0]
- orgModel, err := a.githubOrgRepo.GetGithubOrganization(ctx, organizationName)
+ orgModel, err := a.githubOrgRepo.GetGitHubOrganization(ctx, organizationName)
if err != nil {
log.Warnf("fetching github org failed : %v", err)
return nil, err
diff --git a/cla-backend-go/v2/dynamo_events/github_repository.go b/cla-backend-go/v2/dynamo_events/github_repository.go
index 068e93284..3044b8504 100644
--- a/cla-backend-go/v2/dynamo_events/github_repository.go
+++ b/cla-backend-go/v2/dynamo_events/github_repository.go
@@ -97,7 +97,7 @@ func (s *service) EnableBranchProtectionServiceHandler(event events.DynamoDBEven
parentOrgName := newRepoModel.RepositoryOrganizationName
log.WithFields(f).Warnf("problem locating github organization by name: %s, error: %+v", parentOrgName, err)
- gitHubOrg, err := s.githubOrgService.GetGithubOrganizationByName(context.Background(), parentOrgName)
+ gitHubOrg, err := s.githubOrgService.GetGitHubOrganizationByName(context.Background(), parentOrgName)
if err != nil {
log.WithFields(f).Warnf("problem locating github organization by name: %s, error: %+v", parentOrgName, err)
return nil
@@ -150,7 +150,7 @@ func (s *service) DisableBranchProtectionServiceHandler(event events.DynamoDBEve
// Branch protection only available for GitHub
if oldRepoModel.RepositoryType == utils.GitHubType {
parentOrgName := oldRepoModel.RepositoryOrganizationName
- gitHubOrg, err := s.githubOrgService.GetGithubOrganizationByName(context.Background(), parentOrgName)
+ gitHubOrg, err := s.githubOrgService.GetGitHubOrganizationByName(context.Background(), parentOrgName)
if err != nil {
log.WithFields(f).Warnf("problem locating github organization by name: %s, error: %+v", parentOrgName, err)
return nil
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index 5cda76b0b..3c79a5fa5 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -336,12 +336,12 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
}
// fetch the old and the new github orgs from the db
- oldGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, oldOrganizationName)
+ oldGithubOrg, err := s.githubOrgRepo.GetGitHubOrganization(ctx, oldOrganizationName)
if err != nil {
return fmt.Errorf("fetching the old organization name : %s failed : %v", oldOrganizationName, err)
}
- newGithubOrg, err := s.githubOrgRepo.GetGithubOrganization(ctx, newOrganizationName)
+ newGithubOrg, err := s.githubOrgRepo.GetGitHubOrganization(ctx, newOrganizationName)
if err != nil {
disabledErr := s.disableFailedTransferRepo(ctx, sender, f, repoModel, oldGithubOrg, newGithubOrg)
if disabledErr != nil {
diff --git a/cla-backend-go/v2/github_organizations/handlers.go b/cla-backend-go/v2/github_organizations/handlers.go
index f7a776a2b..a08053bd7 100644
--- a/cla-backend-go/v2/github_organizations/handlers.go
+++ b/cla-backend-go/v2/github_organizations/handlers.go
@@ -232,8 +232,10 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
EventType: events.GitHubOrganizationUpdated,
ProjectSFID: params.ProjectSFID,
EventData: &events.GitHubOrganizationUpdatedEventData{
- GitHubOrganizationName: params.OrgName,
- AutoEnabled: utils.BoolValue(params.Body.AutoEnabled),
+ GitHubOrganizationName: params.OrgName,
+ AutoEnabled: utils.BoolValue(params.Body.AutoEnabled),
+ AutoEnabledClaGroupID: params.Body.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: params.Body.BranchProtectionEnabled,
},
})
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 114cd8c3e..3655538ba 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -82,7 +82,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
// Load the GitHub Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
log.WithFields(f).Debugf("loading GitHub organizations for projectSFID: %s", projectSFID)
- orgs, err := s.ghService.GetGithubOrganizations(ctx, projectSFID)
+ orgs, err := s.ghService.GetGitHubOrganizations(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading github organizations from the project service")
return nil, err
@@ -301,7 +301,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
log.WithFields(f).Debug("located parentProjectID...")
log.WithFields(f).Debug("adding github organization...")
- resp, err := s.repo.AddGithubOrganization(ctx, parentProjectSFID, projectSFID, &in)
+ resp, err := s.repo.AddGitHubOrganization(ctx, parentProjectSFID, projectSFID, &in)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem adding github organization for project")
return nil, err
@@ -311,7 +311,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
}
func (s service) UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
- return s.repo.UpdateGithubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
+ return s.repo.UpdateGitHubOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
}
func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error {
@@ -338,5 +338,5 @@ func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
}
log.WithFields(f).Debug("deleting github github organization...")
- return s.repo.DeleteGithubOrganization(ctx, projectSFID, githubOrgName)
+ return s.repo.DeleteGitHubOrganization(ctx, projectSFID, githubOrgName)
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 9e0f95264..c83126b5a 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -44,9 +44,9 @@ type Service interface {
// GithubOrgRepo provide method to get github organization by name
type GithubOrgRepo interface {
- GetGithubOrganizationByName(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganizations, error)
- GetGithubOrganization(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganization, error)
- GetGithubOrganizations(ctx context.Context, projectSFID string) (*v1Models.GithubOrganizations, error)
+ GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganizations, error)
+ GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganization, error)
+ GetGitHubOrganizations(ctx context.Context, projectSFID string) (*v1Models.GithubOrganizations, error)
}
type service struct {
@@ -112,7 +112,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
return nil, fmt.Errorf("provided cla group id %s is not linked to project sfid %s", utils.StringValue(input.ClaGroupID), projectSFID)
}
- org, err := s.ghOrgRepo.GetGithubOrganizationByName(ctx, utils.StringValue(input.GithubOrganizationName))
+ org, err := s.ghOrgRepo.GetGitHubOrganizationByName(ctx, utils.StringValue(input.GithubOrganizationName))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to get organization by name")
return nil, err
@@ -261,13 +261,13 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
//// Lookup orgs via projectSFID
//log.WithFields(f).Debug("querying EasyCLA for organizations by project id...")
//var githubOrgList *v1Models.GithubOrganizations
- //githubOrgList, err = s.ghOrgRepo.GetGithubOrganizations(ctx, projectSFID)
+ //githubOrgList, err = s.ghOrgRepo.GetGitHubOrganizations(ctx, projectSFID)
//if err != nil {
// log.WithFields(f).WithError(err).Warn("unable to lookup project by id in the github organization table")
// if projectModel.Parent != "" {
// log.WithFields(f).Debugf("querying for organizations by parent project id: %s...", projectModel.Parent)
// var ghOrgErr error
- // githubOrgList, ghOrgErr = s.ghOrgRepo.GetGithubOrganizations(ctx, projectModel.Parent)
+ // githubOrgList, ghOrgErr = s.ghOrgRepo.GetGitHubOrganizations(ctx, projectModel.Parent)
// if ghOrgErr != nil {
// log.WithFields(f).WithError(ghOrgErr).Warn("unable to lookup project by parent id in the github organization table")
// return nil, ghOrgErr
@@ -516,7 +516,7 @@ func (s *service) getBranchProtectionRepositoryForOrgName(ctx context.Context, g
"githubOrgName": githubOrgName,
}
- githubOrg, err := s.ghOrgRepo.GetGithubOrganization(ctx, githubOrgName)
+ githubOrg, err := s.ghOrgRepo.GetGitHubOrganization(ctx, githubOrgName)
if err != nil {
log.WithFields(f).Warnf("fetching githubOrg %s failed, error: %v", githubOrgName, err)
return nil, err
From 44d0850b09895442782292420e68c33ce9c3688a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 23 Jul 2021 17:07:16 -0500
Subject: [PATCH 0366/1276] Activity Log Cleanup for Create CLA Group (#3050)
Signed-off-by: David Deal
---
cla-backend-go/v2/cla_groups/handlers.go | 23 ++++++++++++++---------
cla-backend-go/v2/cla_groups/service.go | 12 ++++++++++--
2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/cla_groups/handlers.go b/cla-backend-go/v2/cla_groups/handlers.go
index 401194f83..bd2ffbdd1 100644
--- a/cla-backend-go/v2/cla_groups/handlers.go
+++ b/cla-backend-go/v2/cla_groups/handlers.go
@@ -60,9 +60,8 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
return cla_group.NewCreateClaGroupForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- claGroup, err := service.CreateCLAGroup(ctx, authUser, params.ClaGroupInput, utils.StringValue(params.XUSERNAME))
+ claGroupSummary, err := service.CreateCLAGroup(ctx, authUser, params.ClaGroupInput, utils.StringValue(params.XUSERNAME))
if err != nil {
- log.WithFields(f).WithError(err).Warn("unable to create the CLA Group")
return cla_group.NewCreateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
Code: "400",
Message: fmt.Sprintf("EasyCLA - 400 Bad Request - %s", err.Error()),
@@ -70,17 +69,23 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
})
}
+ claGroupModel, err := service.GetCLAGroup(ctx, claGroupSummary.ClaGroupID)
+ if err != nil {
+ return cla_group.NewCreateClaGroupBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, "problem loading newly created CLA Group", err))
+ }
+
// Log the event
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLAGroupCreated,
- CLAGroupName: claGroup.ClaGroupName,
- CLAGroupID: claGroup.ClaGroupID,
- ParentProjectSFID: utils.StringValue(params.ClaGroupInput.FoundationSfid),
+ CLAGroupName: claGroupSummary.ClaGroupName,
+ CLAGroupID: claGroupSummary.ClaGroupID,
+ ClaGroupModel: claGroupModel,
+ ParentProjectSFID: claGroupSummary.FoundationSfid,
LfUsername: authUser.UserName,
EventData: &events.CLAGroupCreatedEventData{},
})
- return cla_group.NewCreateClaGroupOK().WithXRequestID(reqID).WithPayload(claGroup)
+ return cla_group.NewCreateClaGroupOK().WithXRequestID(reqID).WithPayload(claGroupSummary)
})
api.ClaGroupUpdateClaGroupHandler = cla_group.UpdateClaGroupHandlerFunc(func(params cla_group.UpdateClaGroupParams, authUser *auth.User) middleware.Responder {
@@ -164,7 +169,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
oldCLAGroupName = claGroupModel.ProjectName
oldCLAGroupDescription = claGroupModel.ProjectDescription
- claGroup, err := service.UpdateCLAGroup(ctx, authUser, claGroupModel, params.Body, utils.StringValue(params.XUSERNAME))
+ claGroupSummary, err := service.UpdateCLAGroup(ctx, authUser, claGroupModel, params.Body)
if err != nil {
// Return a 409 conflict if we have a duplicate name
if _, ok := err.(*utils.CLAGroupNameConflict); ok {
@@ -180,7 +185,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
eventsService.LogEvent(&events.LogEventArgs{
EventType: events.CLAGroupUpdated,
ClaGroupModel: claGroupModel,
- ProjectID: claGroup.ClaGroupID,
+ ProjectID: claGroupSummary.ClaGroupID,
ProjectSFID: projectCLAGroupModels[0].ProjectSFID,
ParentProjectSFID: projectCLAGroupModels[0].FoundationSFID,
LfUsername: authUser.UserName,
@@ -192,7 +197,7 @@ func Configure(api *operations.EasyclaAPI, service Service, v1ProjectService v1P
},
})
- return cla_group.NewUpdateClaGroupOK().WithXRequestID(reqID).WithPayload(claGroup)
+ return cla_group.NewUpdateClaGroupOK().WithXRequestID(reqID).WithPayload(claGroupSummary)
})
api.ClaGroupDeleteClaGroupHandler = cla_group.DeleteClaGroupHandlerFunc(func(params cla_group.DeleteClaGroupParams, authUser *auth.User) middleware.Responder {
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index 10ec2b9ca..e0c8aaeeb 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -54,7 +54,8 @@ type service struct {
// Service interface
type Service interface {
CreateCLAGroup(ctx context.Context, authUser *auth.User, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
- UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error)
+ GetCLAGroup(ctx context.Context, claGroupID string) (*v1Models.ClaGroup, error)
+ UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput) (*models.ClaGroupSummary, error)
ListClaGroupsForFoundationOrProject(ctx context.Context, foundationSFID string) (*models.ClaGroupListSummary, error)
ListAllFoundationClaGroups(ctx context.Context, foundationID *string) (*models.FoundationMappingList, error)
DeleteCLAGroup(ctx context.Context, claGroupModel *v1Models.ClaGroup, authUser *auth.User) error
@@ -82,6 +83,7 @@ func NewService(projectService v1Project.Service, templateService v1Template.Ser
}
}
+// CreateCLAGroup creates a new CLA group
func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input *models.CreateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
// Validate the input
log.WithField("input", input).Debugf("validating create cla group input")
@@ -231,7 +233,13 @@ func (s *service) CreateCLAGroup(ctx context.Context, authUser *auth.User, input
}, nil
}
-func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput, projectManagerLFID string) (*models.ClaGroupSummary, error) {
+// GetCLAGroup returns the CLA group associated with the specified ID
+func (s *service) GetCLAGroup(ctx context.Context, claGroupID string) (*v1Models.ClaGroup, error) {
+ return s.v1ProjectService.GetCLAGroupByID(ctx, claGroupID)
+}
+
+// UpdateCLAGroup updates the specified CLA group with the input details
+func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGroupModel *v1Models.ClaGroup, input *models.UpdateClaGroupInput) (*models.ClaGroupSummary, error) {
// Validate the input
f := logrus.Fields{
"functionName": "v2.cla_groups.service.UpdateCLAGroup",
From af395ec12839515a5e2e6c14525d95ed89d2c8d7 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 23 Jul 2021 17:36:13 -0500
Subject: [PATCH 0367/1276] Resolved CLA Group Name/Description Update Issue
(#3051)
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 43 +++++++++++++++++--------
cla-backend-go/project/repository.go | 17 +++++-----
cla-backend-go/project/service.go | 2 +-
cla-backend-go/v2/cla_groups/service.go | 1 +
4 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index f9f8a2e11..22f52c5fd 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -995,26 +995,38 @@ func (ed *CLAGroupCreatedEventData) GetEventDetailsString(args *LogEventArgs) (s
// GetEventDetailsString returns the details string for this event
func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- var nameUpdated bool
+ var nameUpdated, descriptionUpdated bool
- data := fmt.Sprintf("CLA Group ID: %s was updated", args.ProjectID)
+ data := "The CLA Group" // nolint
if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName {
- data = fmt.Sprintf("%s with Name from : %s to : %s", data, ed.OldClaGroupName, ed.NewClaGroupName)
+ data = fmt.Sprintf("%s name was updated to '%s'", data, ed.NewClaGroupName)
nameUpdated = true
}
+ if args.CLAGroupID != "" {
+ data = data + fmt.Sprintf(" with the CLA group ID %s", args.CLAGroupID)
+ }
+
if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription {
+ descriptionUpdated = true
if nameUpdated {
- data = data + ","
+ data = fmt.Sprintf("%s and the description was updated to '%s'", data, ed.NewClaGroupDescription)
} else {
- data = data + " with"
+ data = fmt.Sprintf("%s description was updated to '%s'", data, ed.NewClaGroupDescription)
}
- data = fmt.Sprintf("%s Description from : %s to : %s", data, ed.OldClaGroupDescription, ed.NewClaGroupDescription)
+ }
+
+ //shouldn't happen
+ if !nameUpdated && !descriptionUpdated {
+ data = data + " was updated"
+ }
+
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
}
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
- data = data + "."
return data, true
}
@@ -1935,27 +1947,32 @@ func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (s
func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
var nameUpdated, descriptionUpdated bool
- message := "The CLA Group"
+ data := "The CLA Group" // nolint
if ed.NewClaGroupName != "" && ed.OldClaGroupName != ed.NewClaGroupName {
- message = message + " name was updated to : " + ed.NewClaGroupName
+ data = fmt.Sprintf("%s name was updated to '%s'", data, ed.NewClaGroupName)
nameUpdated = true
}
if ed.NewClaGroupDescription != "" && ed.OldClaGroupDescription != ed.NewClaGroupDescription {
descriptionUpdated = true
if nameUpdated {
- message = message + " and the description was updated to : " + ed.NewClaGroupDescription
+ data = fmt.Sprintf("%s and the description was updated to '%s'", data, ed.NewClaGroupDescription)
} else {
- message = message + " description was updated to : " + ed.NewClaGroupDescription
+ data = fmt.Sprintf("%s description was updated to '%s'", data, ed.NewClaGroupDescription)
}
}
//shouldn't happen
if !nameUpdated && !descriptionUpdated {
- message = message + " was updated"
+ data = data + " was updated"
}
- data := fmt.Sprintf("%s by the user %s.", message, args.UserName)
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
return data, true
}
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 36f839193..1250821ab 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -665,18 +665,11 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
return nil, ErrProjectDoesNotExist
}
- // We don't allow CLA Group templates to be changed - this requires a legal review
- if existingCLAGroup.ProjectTemplateID != claGroupModel.ProjectTemplateID {
- msg := fmt.Sprintf("problem updating CLA Group - changing CLA templates is not allowed - project: %s with ID: %s - previous template: %s updated template: %s",
- claGroupModel.ProjectName, claGroupModel.ProjectID, claGroupModel.ProjectTemplateID, claGroupModel.ProjectTemplateID)
- log.WithFields(f).Warn(msg)
- return nil, errors.New(msg)
- }
-
expressionAttributeNames := map[string]*string{}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
updateExpression := "SET "
+ // An update to the CLA Group name...
if claGroupModel.ProjectName != "" && existingCLAGroup.ProjectName != claGroupModel.ProjectName {
log.WithFields(f).Debugf("adding project_name: %s", claGroupModel.ProjectName)
expressionAttributeNames["#N"] = aws.String("project_name")
@@ -688,6 +681,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #LOW = :low, "
}
+ // An update to the CLA Group description...
if existingCLAGroup.ProjectDescription != claGroupModel.ProjectDescription {
log.WithFields(f).Debugf("adding project_description: %s", claGroupModel.ProjectDescription)
expressionAttributeNames["#DESC"] = aws.String("project_description")
@@ -695,6 +689,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #DESC = :desc, "
}
+ // An update to the project ACL
if claGroupModel.ProjectACL != nil && len(claGroupModel.ProjectACL) > 0 {
log.WithFields(f).Debugf("adding project_acl: %s", claGroupModel.ProjectACL)
expressionAttributeNames["#A"] = aws.String("project_acl")
@@ -702,6 +697,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #A = :a, "
}
+ // An update to the ICLA enabled flag
if claGroupModel.ProjectICLAEnabled != existingCLAGroup.ProjectICLAEnabled {
log.WithFields(f).Debugf("adding project_icla_enabled: %t", claGroupModel.ProjectICLAEnabled)
expressionAttributeNames["#I"] = aws.String("project_icla_enabled")
@@ -709,6 +705,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #I = :i, "
}
+ // An update to the CCLA enabled flag
if claGroupModel.ProjectCCLAEnabled != existingCLAGroup.ProjectCCLAEnabled {
log.WithFields(f).Debugf("adding project_ccla_enabled: %t", claGroupModel.ProjectCCLAEnabled)
expressionAttributeNames["#C"] = aws.String("project_ccla_enabled")
@@ -716,6 +713,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #C = :c, "
}
+ // An update to the CCLA requires ICLA flag
if claGroupModel.ProjectCCLARequiresICLA != existingCLAGroup.ProjectCCLARequiresICLA {
log.WithFields(f).Debugf("adding project_ccla_requires_icla_signature: %t", claGroupModel.ProjectCCLARequiresICLA)
expressionAttributeNames["#CI"] = aws.String("project_ccla_requires_icla_signature")
@@ -723,6 +721,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #CI = :ci, "
}
+ // An update to the project live flag
if claGroupModel.ProjectLive != existingCLAGroup.ProjectLive {
log.WithFields(f).Debugf("adding project_live: %t", claGroupModel.ProjectLive)
expressionAttributeNames["#PL"] = aws.String("project_live")
@@ -730,6 +729,7 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
updateExpression = updateExpression + " #PL = :pl, "
}
+ // We'll update the date modified time
_, currentTimeString := utils.CurrentTime()
log.WithFields(f).Debugf("adding date_modified: %s", currentTimeString)
expressionAttributeNames["#M"] = aws.String("date_modified")
@@ -748,7 +748,6 @@ func (repo *repo) UpdateCLAGroup(ctx context.Context, claGroupModel *models.ClaG
UpdateExpression: &updateExpression,
TableName: aws.String(repo.claGroupTable),
}
- //log.Debugf("Update input: %+V", updateInput.GoString())
// Make the DynamoDB Update API call
_, updateErr := repo.dynamoDBClient.UpdateItem(updateInput)
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index 551f5136a..f998eec85 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -323,7 +323,7 @@ func (s service) GetClaGroupsByFoundationSFID(ctx context.Context, foundationSFI
return s.repo.GetClaGroupsByFoundationSFID(ctx, foundationSFID, loadRepoDetails)
}
-// GetClaGroupByProjectSFID( service method
+// GetClaGroupByProjectSFID service method
func (s service) GetClaGroupByProjectSFID(ctx context.Context, projectSFID string, loadRepoDetails bool) (*models.ClaGroup, error) {
return s.repo.GetClaGroupByProjectSFID(ctx, projectSFID, loadRepoDetails)
}
diff --git a/cla-backend-go/v2/cla_groups/service.go b/cla-backend-go/v2/cla_groups/service.go
index e0c8aaeeb..51601dd53 100644
--- a/cla-backend-go/v2/cla_groups/service.go
+++ b/cla-backend-go/v2/cla_groups/service.go
@@ -286,6 +286,7 @@ func (s *service) UpdateCLAGroup(ctx context.Context, authUser *auth.User, claGr
ProjectACL: claGroupModel.ProjectACL,
ProjectICLAEnabled: claGroupModel.ProjectICLAEnabled,
ProjectCCLAEnabled: claGroupModel.ProjectCCLAEnabled,
+ ProjectTemplateID: claGroupModel.ProjectTemplateID,
ProjectCCLARequiresICLA: claGroupModel.ProjectCCLARequiresICLA,
ProjectIndividualDocuments: claGroupModel.ProjectIndividualDocuments,
ProjectCorporateDocuments: claGroupModel.ProjectCorporateDocuments,
From 7ee0904639833b94ffa5bb55004bc9b05a44ae0d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 23 Jul 2021 19:13:09 -0500
Subject: [PATCH 0368/1276] Resolved Event Log Unit Test Issues (#3052)
Signed-off-by: David Deal
---
cla-backend-go/events/event_data.go | 7 +++----
cla-backend-go/events/event_data_test.go | 22 +++++++++++-----------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 22f52c5fd..7a2a7247c 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -1027,7 +1027,7 @@ func (ed *CLAGroupUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (s
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
- return data, true
+ return data + ".", true
}
// GetEventDetailsString returns the details string for this event
@@ -1939,8 +1939,7 @@ func (ed *CLAGroupCreatedEventData) GetEventSummaryString(args *LogEventArgs) (s
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
- data = data + "."
- return data, true
+ return data + ".", true
}
// GetEventSummaryString returns the summary string for this event
@@ -1973,7 +1972,7 @@ func (ed *CLAGroupUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (s
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
- return data, true
+ return data + ".", true
}
// GetEventSummaryString returns the summary string for this event
diff --git a/cla-backend-go/events/event_data_test.go b/cla-backend-go/events/event_data_test.go
index e81d3d27c..4f6bd1440 100644
--- a/cla-backend-go/events/event_data_test.go
+++ b/cla-backend-go/events/event_data_test.go
@@ -30,7 +30,7 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
eventData: &CLAGroupUpdatedEventData{
NewClaGroupName: "updatedNameValue",
},
- summaryStr: "The CLA Group name was updated to : updatedNameValue by the user john.",
+ summaryStr: "The CLA Group name was updated to 'updatedNameValue' by the user john.",
},
{
name: "only name updated but old description still passed",
@@ -39,14 +39,14 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
NewClaGroupDescription: "oldDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- summaryStr: "The CLA Group name was updated to : updatedNameValue by the user john.",
+ summaryStr: "The CLA Group name was updated to 'updatedNameValue' by the user john.",
},
{
name: "only description updated",
eventData: &CLAGroupUpdatedEventData{
NewClaGroupDescription: "updatedDescriptionValue",
},
- summaryStr: "The CLA Group description was updated to : updatedDescriptionValue by the user john.",
+ summaryStr: "The CLA Group description was updated to 'updatedDescriptionValue' by the user john.",
},
{
name: "only description updated but old name still passed",
@@ -55,7 +55,7 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
NewClaGroupName: "oldNameValue",
OldClaGroupName: "oldNameValue",
},
- summaryStr: "The CLA Group description was updated to : updatedDescriptionValue by the user john.",
+ summaryStr: "The CLA Group description was updated to 'updatedDescriptionValue' by the user john.",
},
{
name: "name and description updated",
@@ -63,7 +63,7 @@ func TestCLAGroupUpdatedEventData_GetEventSummaryString(t *testing.T) {
NewClaGroupName: "updatedNameValue",
NewClaGroupDescription: "updatedDescriptionValue",
},
- summaryStr: "The CLA Group name was updated to : updatedNameValue and the description was updated to : updatedDescriptionValue by the user john.",
+ summaryStr: "The CLA Group name was updated to 'updatedNameValue' and the description was updated to 'updatedDescriptionValue' by the user john.",
},
}
@@ -86,7 +86,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
{
name: "empty",
eventData: &CLAGroupUpdatedEventData{},
- detailStr: "CLA Group ID: projectIDValue was updated by the user john.",
+ detailStr: "The CLA Group was updated by the user john.",
},
{
name: "only name updated",
@@ -94,7 +94,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupName: "updatedNameValue",
OldClaGroupName: "oldNameValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue by the user john.",
+ detailStr: "The CLA Group name was updated to 'updatedNameValue' by the user john.",
},
{
name: "only name updated but old description still passed",
@@ -104,7 +104,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "oldDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue by the user john.",
+ detailStr: "The CLA Group name was updated to 'updatedNameValue' by the user john.",
},
{
name: "only description updated",
@@ -112,7 +112,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "updatedDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated with Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
+ detailStr: "The CLA Group description was updated to 'updatedDescriptionValue' by the user john.",
},
{
name: "only description updated but old name still passed",
@@ -122,7 +122,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupName: "oldNameValue",
OldClaGroupName: "oldNameValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated with Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
+ detailStr: "The CLA Group description was updated to 'updatedDescriptionValue' by the user john.",
},
{
name: "name and description updated",
@@ -132,7 +132,7 @@ func TestCLAGroupUpdatedEventData_GetEventDetailsString(t *testing.T) {
NewClaGroupDescription: "updatedDescriptionValue",
OldClaGroupDescription: "oldDescriptionValue",
},
- detailStr: "CLA Group ID: projectIDValue was updated with Name from : oldNameValue to : updatedNameValue, Description from : oldDescriptionValue to : updatedDescriptionValue by the user john.",
+ detailStr: "The CLA Group name was updated to 'updatedNameValue' and the description was updated to 'updatedDescriptionValue' by the user john.",
},
}
From 2f9e7bfc2c630f5a8cdef5d5f8c16b1ffa3f7497 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 26 Jul 2021 21:37:38 +0300
Subject: [PATCH 0369/1276] Feature/gitlab integration poc (#3053)
---
cla-backend-go/auth/authorizer.go | 2 +
.../cmd/dynamo_events_lambda/main.go | 4 +
cla-backend-go/cmd/gitlab/api/main.go | 117 ++++++
cla-backend-go/cmd/gitlab/auth/main.go | 312 ++++++++++++++++
cla-backend-go/cmd/gitlab/webhook/main.go | 88 +++++
cla-backend-go/cmd/server.go | 10 +
cla-backend-go/config/config.go | 12 +
cla-backend-go/config/ssm.go | 17 +
cla-backend-go/events/event_data.go | 114 ++++++
cla-backend-go/events/event_types.go | 4 +
cla-backend-go/gitlab/auth.go | 33 ++
cla-backend-go/gitlab/client.go | 141 +++++++
cla-backend-go/gitlab/client_test.go | 57 +++
cla-backend-go/gitlab/init.go | 15 +
cla-backend-go/gitlab/organization.go | 26 ++
cla-backend-go/go.mod | 19 +-
cla-backend-go/go.sum | 107 ++++--
cla-backend-go/serverless.yml | 4 +
cla-backend-go/swagger/cla.v2.yaml | 175 ++++++++-
.../common/create-github-organization.yaml | 4 +-
.../swagger/common/gitlab-organization.yaml | 82 ++++
.../swagger/common/gitlab-organizations.yaml | 9 +
cla-backend-go/utils/const.go | 15 +
.../utils/utils_user_auth_standalone.go | 2 +
.../v2/github_organizations/service.go | 27 +-
.../v2/gitlab_organizations/handlers.go | 206 ++++++++++
.../v2/gitlab_organizations/models.go | 50 +++
.../v2/gitlab_organizations/repository.go | 352 ++++++++++++++++++
.../v2/gitlab_organizations/service.go | 266 +++++++++++++
29 files changed, 2208 insertions(+), 62 deletions(-)
create mode 100644 cla-backend-go/cmd/gitlab/api/main.go
create mode 100644 cla-backend-go/cmd/gitlab/auth/main.go
create mode 100644 cla-backend-go/cmd/gitlab/webhook/main.go
create mode 100644 cla-backend-go/gitlab/auth.go
create mode 100644 cla-backend-go/gitlab/client.go
create mode 100644 cla-backend-go/gitlab/client_test.go
create mode 100644 cla-backend-go/gitlab/init.go
create mode 100644 cla-backend-go/gitlab/organization.go
create mode 100644 cla-backend-go/swagger/common/gitlab-organization.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-organizations.yaml
create mode 100644 cla-backend-go/utils/const.go
create mode 100644 cla-backend-go/v2/gitlab_organizations/handlers.go
create mode 100644 cla-backend-go/v2/gitlab_organizations/models.go
create mode 100644 cla-backend-go/v2/gitlab_organizations/repository.go
create mode 100644 cla-backend-go/v2/gitlab_organizations/service.go
diff --git a/cla-backend-go/auth/authorizer.go b/cla-backend-go/auth/authorizer.go
index 49569d2b3..76532c094 100644
--- a/cla-backend-go/auth/authorizer.go
+++ b/cla-backend-go/auth/authorizer.go
@@ -125,6 +125,7 @@ func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser,
}
return nil, err
}
+ //log.WithFields(f).Debugf("user loaded : %+v with scopes : %+v", lfuser, scopes)
for _, scope := range scopes {
switch Scope(scope) {
@@ -151,5 +152,6 @@ func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser,
}
}
+ //log.WithFields(f).Debugf("returning user from auth : %+v", lfuser)
return &lfuser, nil
}
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index 8dbf6d764..72edafe5f 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -8,6 +8,8 @@ import (
"encoding/json"
"os"
+ "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -91,6 +93,8 @@ func init() {
token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
+ // initialize gitlab
+ gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
project_service.InitClient(configFile.APIGatewayURL)
diff --git a/cla-backend-go/cmd/gitlab/api/main.go b/cla-backend-go/cmd/gitlab/api/main.go
new file mode 100644
index 000000000..dec0f60aa
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab/api/main.go
@@ -0,0 +1,117 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/xanzy/go-gitlab"
+)
+
+const (
+ ProjectsURL = "https://gitlab.com/api/v4/projects"
+)
+
+var state = flag.String("state", "failed", "the state of the MR to set")
+
+func main() {
+ flag.Parse()
+
+ access_token := os.Getenv("GITLAB_ACCESS_TOKEN")
+ if access_token == "" {
+ log.Fatal("GITLAB_ACCESS_TOKEN is required")
+ }
+
+ log.Infof("The gitlab access token is : %s", access_token)
+
+ gitlabClient, err := gitlab.NewOAuthClient(access_token)
+ if err != nil {
+ log.Fatalf("creating client failed : %v", err)
+ }
+
+ user, _, err := gitlabClient.Users.CurrentUser()
+ if err != nil {
+ log.Fatalf("fetching current user failed : %v", err)
+ }
+
+ log.Infof("fetched current user : %s", user.Name)
+
+ projects, _, err := gitlabClient.Projects.ListUserProjects(user.ID, &gitlab.ListProjectsOptions{})
+ if err != nil {
+ log.Fatalf("listing projects failed : %v", err)
+ }
+ log.Printf("we fetched : %d projects for the account", len(projects))
+ for _, p := range projects {
+ log.Println("Name : ", p.Name)
+ log.Println("ID: ", p.ID)
+ }
+
+ projectID := 28118160
+ commitSha := "f7036ab67a4e464e83e16af0b02d447c53fffa74"
+
+ statuses, _, err := gitlabClient.Commits.GetCommitStatuses(projectID, commitSha,
+ &gitlab.GetCommitStatusesOptions{})
+ if err != nil {
+ log.Fatalf("fetching commit statuses failed : %v", err)
+ }
+
+ if len(statuses) == 0 {
+ log.Infof("no statuses found for commit sha")
+ setState := gitlab.Failed
+ if *state != string(gitlab.Failed) {
+ setState = gitlab.Success
+ }
+
+ _, _, err = gitlabClient.Commits.SetCommitStatus(projectID, commitSha, &gitlab.SetCommitStatusOptions{
+ State: setState,
+ Name: gitlab.String("easyCLA Bot"),
+ Description: gitlab.String(getDescription(setState)),
+ TargetURL: gitlab.String(getTargetURL("deniskurov@gmail.com")),
+ })
+ if err != nil {
+ log.Fatalf("setting commit status for the sha failed : %v", err)
+ }
+
+ statuses, _, err = gitlabClient.Commits.GetCommitStatuses(projectID, commitSha,
+ &gitlab.GetCommitStatusesOptions{})
+ if err != nil {
+ log.Fatalf("fetching commit statuses failed : %v", err)
+ }
+
+ }
+
+ for _, status := range statuses {
+ log.Println("Status : ", status.Status)
+ if status.Status != *state {
+ log.Infof("setting state of commit sha to %s", *state)
+ _, _, err = gitlabClient.Commits.SetCommitStatus(projectID, commitSha, &gitlab.SetCommitStatusOptions{
+ State: gitlab.BuildStateValue(*state),
+ Name: gitlab.String("easyCLA Bot"),
+ Description: gitlab.String(getDescription(gitlab.BuildStateValue(*state))),
+ TargetURL: gitlab.String(getTargetURL("deniskurov@gmail.com")),
+ })
+ if err != nil {
+ log.Fatalf("setting commit status for the sha failed : %v", err)
+ }
+ }
+ log.Println("Status Name : ", status.Name)
+ log.Println("Status Description : ", status.Description)
+ log.Println("Status Author : ", status.Author.Name)
+ log.Println("Status Author Email : ", status.Author.Email)
+ }
+}
+
+func getDescription(status gitlab.BuildStateValue) string {
+ if status == gitlab.Failed {
+ return "User hasn't signed CLA"
+ }
+ return "User signed CLA"
+}
+
+func getTargetURL(email string) string {
+ return fmt.Sprintf("http://localhost:8080/gitlab/sign/%s", email)
+}
diff --git a/cla-backend-go/cmd/gitlab/auth/main.go b/cla-backend-go/cmd/gitlab/auth/main.go
new file mode 100644
index 000000000..798a07ef6
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab/auth/main.go
@@ -0,0 +1,312 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/gin-gonic/gin"
+ "github.com/go-resty/resty/v2"
+ "github.com/xanzy/go-gitlab"
+)
+
+const (
+ REDIRECT_URI = "http://localhost:8080/gitlab/oauth/callback"
+ APPLICATION_ID = "18718b478096e6a257eda51414d0d446ad28866c15187aa765f602fe906d0b17"
+ APPLICATION_SECRET = "8dd14ace0eb0e4674b849b6fed4ce51bbcc456fc62d9149aff15353c1dda6327"
+)
+
+const (
+ hookURL = "https://4c1ba3f4f3c1.ngrok.io/gitlab/events"
+)
+
+type OauthSuccessResponse struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ ExpiresIn int `json:"expires_in"`
+ RefreshToken string `json:"refresh_token"`
+ CreatedAt int `json:"created_at"`
+}
+
+var passingUsers = map[string]bool{
+ "deniskurov@gmail.com": true,
+}
+
+func main() {
+ r := gin.Default()
+ r.GET("/gitlab/sign", func(c *gin.Context) {
+ email := c.Query("email")
+ if email == "" {
+ c.JSON(400, gin.H{
+ "message": "email is required parameter",
+ })
+ return
+ }
+
+ projectID := c.Query("project_id")
+ if projectID == "" {
+ c.JSON(400, gin.H{
+ "message": "projectID is required parameter",
+ })
+ return
+ }
+
+ lastCommitSha := c.Query("sha")
+ if lastCommitSha == "" {
+ c.JSON(400, gin.H{
+ "message": "sha is required parameter",
+ })
+ return
+ }
+
+ projectIDInt, err := strconv.Atoi(projectID)
+ if err != nil {
+ log.Error("project id conversion failed ", err)
+ c.JSON(400, gin.H{
+ "message": "project id conversion",
+ })
+ return
+ }
+
+ if err := setCommitStatus(projectIDInt, lastCommitSha, email, string(gitlab.Success)); err != nil {
+ log.Error("setting commit status failed", err)
+ c.JSON(500, gin.H{
+ "message": "setting commit status failed",
+ })
+ return
+ }
+
+ log.Infof("email to sign is : %s", email)
+ log.Infof("project id : %s, sha : %s", projectID, lastCommitSha)
+
+ c.JSON(http.StatusOK, gin.H{
+ "message": fmt.Sprintf("user : %s, signed for project : %s", email, projectID),
+ })
+
+ })
+
+ r.POST("/gitlab/events", func(c *gin.Context) {
+ jsonData, err := ioutil.ReadAll(c.Request.Body)
+ event, err := gitlab.ParseWebhook(gitlab.EventTypeMergeRequest, jsonData)
+ if err != nil {
+ log.Error("parsing json body failed", err)
+ c.JSON(400, gin.H{
+ "message": "code is required parameter",
+ })
+ return
+ }
+
+ mergeEvent, ok := event.(*gitlab.MergeEvent)
+ if !ok {
+ c.JSON(400, gin.H{
+ "message": "type cast failed",
+ })
+ return
+ }
+
+ if mergeEvent.ObjectAttributes.State != "opened" {
+ c.JSON(200, gin.H{
+ "message": "only interested in opened events",
+ })
+ return
+ }
+
+ projectName := mergeEvent.Project.Name
+ projectID := mergeEvent.Project.ID
+
+ mergeID := mergeEvent.ObjectAttributes.IID
+ lastCommitSha := mergeEvent.ObjectAttributes.LastCommit.ID
+ lastCommitMessage := mergeEvent.ObjectAttributes.LastCommit.Message
+
+ authorName := mergeEvent.ObjectAttributes.LastCommit.Author.Name
+ authorEmail := mergeEvent.ObjectAttributes.LastCommit.Author.Email
+
+ log.Printf("Received MR (%d) for Project %s:%d", mergeID, projectName, projectID)
+ log.Printf("last commit : %s : %s", lastCommitSha, lastCommitMessage)
+ log.Printf("author name : %s, author email : %s", authorName, authorEmail)
+
+ if err := setCommitStatus(projectID, lastCommitSha, authorEmail, ""); err != nil {
+ log.Error("setting commit status failed", err)
+ c.JSON(500, gin.H{
+ "message": "setting commit status failed",
+ })
+ return
+ }
+
+ //empJSON, err := json.MarshalIndent(mergeEvent, "", " ")
+ //if err != nil {
+ // log.Fatalf(err.Error())
+ //}
+ //fmt.Printf("MarshalIndent funnction output %s\n", string(empJSON))
+ c.JSON(http.StatusOK, gin.H{})
+
+ })
+ r.GET("/gitlab/oauth/callback", func(c *gin.Context) {
+ code := c.Query("code")
+ if code == "" {
+ c.JSON(400, gin.H{
+ "message": "code is required parameter",
+ })
+ return
+ }
+
+ state := c.Query("state")
+ if state == "" {
+ c.JSON(400, gin.H{
+ "message": "state is required parameter",
+ })
+ return
+ }
+ log.Printf("received code : %s, STATE: %s", code, state)
+
+ client := resty.New()
+ params := map[string]string{
+ "client_id": APPLICATION_ID,
+ "client_secret": APPLICATION_SECRET,
+ "code": code,
+ "grant_type": "authorization_code",
+ "redirect_uri": REDIRECT_URI,
+ }
+
+ resp, err := client.R().
+ SetQueryParams(params).
+ SetResult(&OauthSuccessResponse{}).
+ Post("https://gitlab.com/oauth/token")
+
+ if err != nil {
+ c.JSON(500, gin.H{
+ "message": fmt.Sprintf("getting the token failed : %v", err),
+ })
+ return
+ }
+
+ result := resp.Result().(*OauthSuccessResponse)
+ accessToken := result.AccessToken
+
+ err = registerWebHooksForUserProjects(accessToken)
+ if err != nil {
+ log.Error("register webhook ", err)
+ }
+
+ respData := gin.H{
+ "message": "OK",
+ "data": result,
+ }
+
+ if err != nil {
+ respData["error"] = err
+ }
+
+ c.JSON(200, respData)
+ })
+ r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
+}
+
+func registerWebHooksForUserProjects(accessToken string) error {
+ gitlabClient, err := gitlab.NewOAuthClient(accessToken)
+ if err != nil {
+ return fmt.Errorf("creating client failed : %v", err)
+ }
+
+ user, _, err := gitlabClient.Users.CurrentUser()
+ if err != nil {
+ return fmt.Errorf("fetching current user failed : %v", err)
+ }
+
+ log.Infof("fetched current user : %s", user.Name)
+
+ projects, _, err := gitlabClient.Projects.ListUserProjects(user.ID, &gitlab.ListProjectsOptions{})
+ if err != nil {
+ return fmt.Errorf("listing projects failed : %v", err)
+ }
+
+ log.Printf("we fetched : %d projects for the account", len(projects))
+
+ for _, p := range projects {
+ log.Println("**********************")
+ log.Println("Name : ", p.Name)
+ log.Println("ID: ", p.ID)
+ log.Infof("adding webhook to the project : %s (%d)", p.Name, p.ID)
+ if err := addCLAHookToProject(gitlabClient, p.ID); err != nil {
+ return fmt.Errorf("adding hook to the project : %s (%d) failed : %v", p.Name, p.ID, err)
+ }
+ }
+
+ return nil
+}
+
+func addCLAHookToProject(gitlabClient *gitlab.Client, projectID int) error {
+ _, _, err := gitlabClient.Projects.AddProjectHook(projectID, &gitlab.AddProjectHookOptions{
+ URL: gitlab.String(hookURL),
+ MergeRequestsEvents: gitlab.Bool(true),
+ EnableSSLVerification: gitlab.Bool(false),
+ })
+ return err
+}
+
+func setCommitStatus(projectID interface{}, commitSha string, userEmail string, forceState string) error {
+ accessToken := os.Getenv("GITLAB_ACCESS_TOKEN")
+ if accessToken == "" {
+ return fmt.Errorf("GITLAB_ACCESS_TOKEN is required")
+ }
+
+ gitlabClient, err := gitlab.NewOAuthClient(accessToken)
+ if err != nil {
+ return fmt.Errorf("creating client failed : %v", err)
+ }
+
+ setState := gitlab.Failed
+
+ if forceState == "" {
+ if passingUsers[userEmail] {
+ setState = gitlab.Success
+ }
+ } else {
+ setState = gitlab.BuildStateValue(forceState)
+ }
+
+ options := &gitlab.SetCommitStatusOptions{
+ State: setState,
+ Name: gitlab.String("easyCLA Bot"),
+ Description: gitlab.String(getDescription(setState)),
+ }
+
+ if setState == gitlab.Failed {
+ options.TargetURL = gitlab.String(getTargetURL(projectID, commitSha, userEmail))
+ }
+
+ _, _, err = gitlabClient.Commits.SetCommitStatus(projectID, commitSha, options)
+ if err != nil {
+ return fmt.Errorf("setting commit status for the sha failed : %v", err)
+ }
+
+ return nil
+}
+
+func getDescription(status gitlab.BuildStateValue) string {
+ if status == gitlab.Failed {
+ return "User hasn't signed CLA"
+ }
+ return "User signed CLA"
+}
+
+func getTargetURL(projectID interface{}, lastCommitSha, email string) string {
+ base := "http://localhost:8080/gitlab/sign"
+
+ projectIDInt := projectID.(int)
+ projectIDStr := strconv.Itoa(projectIDInt)
+
+ params := url.Values{}
+ params.Add("project_id", projectIDStr)
+ params.Add("sha", lastCommitSha)
+ params.Add("email", email)
+
+ return base + "?" + params.Encode()
+}
diff --git a/cla-backend-go/cmd/gitlab/webhook/main.go b/cla-backend-go/cmd/gitlab/webhook/main.go
new file mode 100644
index 000000000..b7a187b98
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab/webhook/main.go
@@ -0,0 +1,88 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package main
+
+import (
+ "os"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/xanzy/go-gitlab"
+)
+
+const (
+ hookURL = "https://7e182f2774e2.ngrok.io/gitlab/events"
+)
+
+func main() {
+ log.Println("register webhook")
+ access_token := os.Getenv("GITLAB_ACCESS_TOKEN")
+ if access_token == "" {
+ log.Fatal("GITLAB_ACCESS_TOKEN is required")
+ }
+
+ log.Infof("The gitlab access token is : %s", access_token)
+
+ gitlabClient, err := gitlab.NewOAuthClient(access_token)
+ if err != nil {
+ log.Fatalf("creating client failed : %v", err)
+ }
+
+ user, _, err := gitlabClient.Users.CurrentUser()
+ if err != nil {
+ log.Fatalf("fetching current user failed : %v", err)
+ }
+
+ log.Infof("fetched current user : %s", user.Name)
+
+ projects, _, err := gitlabClient.Projects.ListUserProjects(user.ID, &gitlab.ListProjectsOptions{})
+ if err != nil {
+ log.Fatalf("listing projects failed : %v", err)
+ }
+ log.Printf("we fetched : %d projects for the account", len(projects))
+ for _, p := range projects {
+ log.Println("**********************")
+ log.Println("Name : ", p.Name)
+ log.Println("ID: ", p.ID)
+ hooks, _, err := gitlabClient.Projects.ListProjectHooks(p.ID, &gitlab.ListProjectHooksOptions{})
+
+ if err != nil {
+ log.Fatalf("fetching hooks for project : %s, failed : %v", p.Name, err)
+ }
+
+ var claHookFound bool
+ for _, hook := range hooks {
+ log.Println("**********************")
+ log.Infof("hook ID : %d", hook.ID)
+ log.Infof("URL : %s", hook.URL)
+ log.Infof("Merge Request Events Enabled : %v", hook.MergeRequestsEvents)
+ log.Infof("Enable SSL Verification : %v", hook.EnableSSLVerification)
+
+ if hookURL == hook.URL {
+ claHookFound = true
+ break
+ }
+ }
+
+ if claHookFound {
+ log.Infof("CLA Hook was found nothing to do")
+ continue
+ }
+
+ log.Infof("adding webhook to the project : %s (%d)", p.Name, p.ID)
+ if err := addCLAHookToProject(gitlabClient, p.ID); err != nil {
+ log.Fatalf("adding hook to the project : %s (%d) failed : %v", p.Name, p.ID, err)
+ }
+
+ }
+
+}
+
+func addCLAHookToProject(gitlabClient *gitlab.Client, projectID int) error {
+ _, _, err := gitlabClient.Projects.AddProjectHook(projectID, &gitlab.AddProjectHookOptions{
+ URL: gitlab.String(hookURL),
+ MergeRequestsEvents: gitlab.Bool(true),
+ EnableSSLVerification: gitlab.Bool(false),
+ })
+ return err
+}
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 4cf1af8be..148d0d181 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -14,6 +14,10 @@ import (
"strconv"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+
"github.com/communitybridge/easycla/cla-backend-go/emails"
"github.com/communitybridge/easycla/cla-backend-go/v2/dynamo_events"
@@ -226,7 +230,10 @@ func server(localMode bool) http.Handler {
if err != nil {
logrus.Panic(err)
}
+ // initialize github
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
+ // initialize gitlab
+ gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
@@ -241,6 +248,7 @@ func server(localMode bool) http.Handler {
v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, v1ProjectClaGroupRepo)
metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, v1ProjectClaGroupRepo)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
+ gitlabOrganizationRepo := gitlab_organizations.NewRepository(awsSession, stage)
claManagerReqRepo := cla_manager.NewRepository(awsSession, stage)
// Our service layer handlers
@@ -291,6 +299,7 @@ func server(localMode bool) http.Handler {
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, v1ProjectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo)
+ gitlabOrganizationsService := gitlab_organizations.NewService(gitlabOrganizationRepo, v1ProjectClaGroupRepo)
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo, githubOrganizationsService)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
@@ -330,6 +339,7 @@ func server(localMode bool) http.Handler {
v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
+ gitlab_organizations.Configure(v2API, gitlabOrganizationsService, eventsService)
repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 2d3629674..07c6d58dd 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -51,6 +51,9 @@ type Config struct {
// GitHub Application
GitHub GitHub `json:"github"`
+ // Gitlab Application
+ Gitlab Gitlab `json:"gitlab"`
+
// Dynamo Session Store
SessionStoreTableName string `json:"sessionStoreTableName"`
@@ -134,6 +137,15 @@ type GitHub struct {
TestRepositoryID string `json:"test_repository_id"`
}
+// Gitlab model
+type Gitlab struct {
+ ClientSecret string `json:"clientSecret"`
+ AppID string `json:"app_id"`
+ AppPrivateKey string `json:"app_private_key"`
+ RedirectURI string `json:"redirect_uri"`
+ WebHookURI string `json:"web_hook_uri"`
+}
+
// MetricsReport keeps the config needed to send the metrics data report
type MetricsReport struct {
AwsSQSRegion string `json:"aws_sqs_region"`
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 06d83a970..4d98e3f1e 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -70,6 +70,11 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-gh-test-organization-installation-id-%s", stage),
fmt.Sprintf("cla-gh-test-repository-%s", stage),
fmt.Sprintf("cla-gh-test-repository-id-%s", stage),
+ fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage),
+ fmt.Sprintf("cla-gitlab-app-id-%s", stage),
+ fmt.Sprintf("cla-gitlab-app-private-key-%s", stage),
+ fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage),
+ fmt.Sprintf("cla-gitlab-app-web-hook-uri-%s", stage),
fmt.Sprintf("cla-corporate-base-%s", stage),
fmt.Sprintf("cla-corporate-v1-base-%s", stage),
fmt.Sprintf("cla-corporate-v2-base-%s", stage),
@@ -150,6 +155,18 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
case fmt.Sprintf("cla-gh-test-repository-id-%s", stage):
config.GitHub.TestRepositoryID = resp.value
+ // gitlab ssm
+ case fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage):
+ config.Gitlab.ClientSecret = resp.value
+ case fmt.Sprintf("cla-gitlab-app-id-%s", stage):
+ config.Gitlab.AppID = resp.value
+ case fmt.Sprintf("cla-gitlab-app-private-key-%s", stage):
+ config.Gitlab.AppPrivateKey = resp.value
+ case fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage):
+ config.Gitlab.RedirectURI = resp.value
+ case fmt.Sprintf("cla-gitlab-app-web-hook-uri-%s", stage):
+ config.Gitlab.WebHookURI = resp.value
+
case fmt.Sprintf("cla-corporate-base-%s", stage):
config.CorporateConsoleURL = resp.value
case fmt.Sprintf("cla-corporate-v1-base-%s", stage):
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 7a2a7247c..47954707b 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -191,6 +191,26 @@ type GitHubOrganizationUpdatedEventData struct {
BranchProtectionEnabled bool
}
+// GitlabOrganizationAddedEventData data model
+type GitlabOrganizationAddedEventData struct {
+ GitlabOrganizationName string
+ AutoEnabled bool
+ AutoEnabledClaGroupID string
+ BranchProtectionEnabled bool
+}
+
+// GitlabOrganizationDeletedEventData data model
+type GitlabOrganizationDeletedEventData struct {
+ GitlabOrganizationName string
+}
+
+// GitlabOrganizationUpdatedEventData data model
+type GitlabOrganizationUpdatedEventData struct {
+ GitlabOrganizationName string
+ AutoEnabled bool
+ AutoEnabledClaGroupID string
+}
+
// CCLAApprovalListRequestCreatedEventData data model
type CCLAApprovalListRequestCreatedEventData struct {
RequestID string
@@ -652,6 +672,44 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventDetailsString(args *LogEve
return data, true
}
+// GetEventDetailsString returns the details string for this event
+func (ed *GitlabOrganizationAddedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("Gitlab Organization: %s was added with auto-enabled: %t, with branch protection enabled: %t",
+ ed.GitlabOrganizationName, ed.AutoEnabled, ed.BranchProtectionEnabled)
+ if ed.AutoEnabledClaGroupID != "" {
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString returns the details string for this event
+func (ed *GitlabOrganizationDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("GitHub Organization: %s was deleted ", ed.GitlabOrganizationName)
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString returns the details string for this event
+func (ed *GitlabOrganizationUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("GitHub Organization:%s was updated with auto-enabled: %t",
+ ed.GitlabOrganizationName, ed.AutoEnabled)
+ if ed.AutoEnabledClaGroupID != "" {
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventDetailsString returns the details string for this event
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("User: %s approved a CCLA Approval Request for Project: %s and Company: %s with Request ID: %s.",
@@ -1558,6 +1616,62 @@ func (ed *GitHubOrganizationUpdatedEventData) GetEventSummaryString(args *LogEve
return data, true
}
+// GetEventSummaryString returns the summary string for this event
+func (ed *GitlabOrganizationAddedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was added with auto-enabled set to %t with branch protection enabled set to %t",
+ ed.GitlabOrganizationName, ed.AutoEnabled, ed.BranchProtectionEnabled)
+ if ed.AutoEnabledClaGroupID != "" {
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group set to %s", ed.AutoEnabledClaGroupID)
+ }
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString returns the summary string for this event
+func (ed *GitlabOrganizationDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was deleted", ed.GitlabOrganizationName)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString returns the summary string for this event
+func (ed *GitlabOrganizationUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("Gitlab Organization: %s was updated with auto-enabled: %t",
+ ed.GitlabOrganizationName, ed.AutoEnabled)
+ if ed.AutoEnabledClaGroupID != "" {
+ data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ }
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for project %s", args.ProjectName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventSummaryString returns the summary string for this event
func (ed *CCLAApprovalListRequestApprovedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s approved a CCLA approval request", args.UserName)
diff --git a/cla-backend-go/events/event_types.go b/cla-backend-go/events/event_types.go
index 4f36d5459..85cffeea7 100644
--- a/cla-backend-go/events/event_types.go
+++ b/cla-backend-go/events/event_types.go
@@ -49,6 +49,10 @@ const (
GitHubOrganizationDeleted = "github_organization.deleted"
GitHubOrganizationUpdated = "github_organization.updated"
+ GitlabOrganizationAdded = "gitlab_organization.added"
+ GitlabOrganizationDeleted = "gitlab_organization.deleted"
+ GitlabOrganizationUpdated = "gitlab_organization.updated"
+
CompanyACLUserAdded = "company_acl.user_added"
CompanyACLRequestAdded = "company_acl.request_added"
CompanyACLRequestApproved = "company_acl.request_approved"
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab/auth.go
new file mode 100644
index 000000000..d9da3a218
--- /dev/null
+++ b/cla-backend-go/gitlab/auth.go
@@ -0,0 +1,33 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+ "github.com/go-resty/resty/v2"
+)
+
+// FetchOauthCredentials is responsible for fetching the credentials from gitlab for alredy started Oauth process (access_token, refresh_token)
+func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
+ client := resty.New()
+ params := map[string]string{
+ "client_id": config.GetConfig().Gitlab.AppID,
+ "client_secret": config.GetConfig().Gitlab.ClientSecret,
+ "code": code,
+ "grant_type": "authorization_code",
+ "redirect_uri": config.GetConfig().Gitlab.RedirectURI,
+ //"redirect_uri": "http://localhost:8080/v4/gitlab/oauth/callback",
+ }
+
+ resp, err := client.R().
+ SetQueryParams(params).
+ SetResult(&OauthSuccessResponse{}).
+ Post("https://gitlab.com/oauth/token")
+
+ if err != nil {
+ return nil, err
+ }
+
+ return resp.Result().(*OauthSuccessResponse), nil
+}
diff --git a/cla-backend-go/gitlab/client.go b/cla-backend-go/gitlab/client.go
new file mode 100644
index 000000000..d04757245
--- /dev/null
+++ b/cla-backend-go/gitlab/client.go
@@ -0,0 +1,141 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
+ "github.com/xanzy/go-gitlab"
+)
+
+// OauthSuccessResponse is success response from Gitlab
+type OauthSuccessResponse struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ ExpiresIn int `json:"expires_in"`
+ RefreshToken string `json:"refresh_token"`
+ CreatedAt int `json:"created_at"`
+}
+
+// NewGitlabOauthClient creates a new gitlab client from the given oauth info, authInfo is encrypted
+func NewGitlabOauthClient(authInfo string) (*gitlab.Client, error) {
+ oauthResp, err := DecryptAuthInfo(authInfo)
+ if err != nil {
+ return nil, err
+ }
+
+ log.Infof("creating oauth client with access token : %s", oauthResp.AccessToken)
+ return gitlab.NewOAuthClient(oauthResp.AccessToken)
+}
+
+// EncryptAuthInfo encrypts the oauth response into a string
+func EncryptAuthInfo(oauthResp *OauthSuccessResponse) (string, error) {
+ key := getGitlabAppPrivateKey()
+ keyDecoded, err := base64.StdEncoding.DecodeString(key)
+ if err != nil {
+ return "", fmt.Errorf("decode key : %v", err)
+ }
+
+ b, err := json.Marshal(oauthResp)
+ if err != nil {
+ return "", fmt.Errorf("oauth resp json marshall : %v", err)
+ }
+ authInfo := string(b)
+ //log.Infof("auth info before encrypting : %s", authInfo)
+
+ encrypted, err := encrypt(keyDecoded, []byte(authInfo))
+ if err != nil {
+ return "", fmt.Errorf("encrypt failed : %v", err)
+ }
+
+ return hex.EncodeToString(encrypted), nil
+}
+
+// DecryptAuthInfo decrytps the authinfo into OauthSuccessResponse data structure
+func DecryptAuthInfo(authInfoEncoded string) (*OauthSuccessResponse, error) {
+ ciphertext, err := hex.DecodeString(authInfoEncoded)
+ if err != nil {
+ return nil, fmt.Errorf("decode auth info %s : %v", authInfoEncoded, err)
+ }
+
+ //log.Infof("auth info decoded : %s", ciphertext)
+
+ key := getGitlabAppPrivateKey()
+ keyDecoded, err := base64.StdEncoding.DecodeString(key)
+ if err != nil {
+ return nil, fmt.Errorf("decode key : %v", err)
+ }
+
+ //log.Debugf("before decrypt : keyDecoded : %s, cipherText : %s", keyDecoded, ciphertext)
+ decrypted, err := decrypt(keyDecoded, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("decrypt failed : %v", err)
+ }
+ //log.Debugf("after decrypt : keyDecoded : %s, decrypted : %s", keyDecoded, decrypted)
+
+ var oauthResp OauthSuccessResponse
+ if err := json.Unmarshal(decrypted, &oauthResp); err != nil {
+ return nil, fmt.Errorf("unmarshall auth info : %v", err)
+ }
+
+ return &oauthResp, nil
+}
+
+func encrypt(key, message []byte) ([]byte, error) {
+ // Initialize block cipher
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the byte slice that will hold encrypted message
+ cipherText := make([]byte, aes.BlockSize+len(message))
+
+ // Generate the Initialization Vector (IV) nonce
+ // which is stored at the beginning of the byte slice
+ // The IV is the same length as the AES blocksize
+ iv := cipherText[:aes.BlockSize]
+ _, err = io.ReadFull(rand.Reader, iv)
+ if err != nil {
+ return nil, err
+ }
+
+ // Choose the block cipher mode of operation
+ // Using the cipher feedback (CFB) mode here.
+ // CBCEncrypter also available.
+ cfb := cipher.NewCFBEncrypter(block, iv)
+ // Generate the encrypted message and store it
+ // in the remaining bytes after the IV nonce
+ cfb.XORKeyStream(cipherText[aes.BlockSize:], message)
+
+ return cipherText, nil
+}
+
+// AES decryption
+func decrypt(key, cipherText []byte) ([]byte, error) {
+ // Initialize block cipher
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ // Separate the IV nonce from the encrypted message bytes
+ iv := cipherText[:aes.BlockSize]
+ cipherText = cipherText[aes.BlockSize:]
+
+ // Decrypt the message using the CFB block mode
+ cfb := cipher.NewCFBDecrypter(block, iv)
+ cfb.XORKeyStream(cipherText, cipherText)
+
+ return cipherText, nil
+}
diff --git a/cla-backend-go/gitlab/client_test.go b/cla-backend-go/gitlab/client_test.go
new file mode 100644
index 000000000..46c994c2c
--- /dev/null
+++ b/cla-backend-go/gitlab/client_test.go
@@ -0,0 +1,57 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+var key = "0WqnDWHnZKo2cmQ8m93EtY9ZBpfzQW4UnnEuRmgtJKM="
+var oauthRespStr = `{"access_token":"a30671b8749ba5d48925712344377f11a5aba43ec630f099e464b9843796e6a6","token_type":"Bearer","expires_in":0,"refresh_token":"0838a31d0d796973eacefdf513523e6e47aa06fac9d26622964da1e473509458","created_at":1626435922}`
+
+func TestNewGitlabOauthClient(t *testing.T) {
+ Init("124453345", key)
+ t.Cleanup(func() {
+ gitlabAppPrivateKey = ""
+ })
+
+ t.Logf("app private key is : %s", getGitlabAppPrivateKey())
+
+ var oauthResp OauthSuccessResponse
+ err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
+ assert.NoError(t, err)
+
+ encrypted, err := EncryptAuthInfo(&oauthResp)
+ assert.NoError(t, err)
+
+ client, err := NewGitlabOauthClient(encrypted)
+ assert.NoError(t, err)
+ assert.NotNil(t, client)
+}
+
+func TestEncryptDecryptAuthInfo(t *testing.T) {
+ Init("124453345", key)
+ t.Cleanup(func() {
+ gitlabAppPrivateKey = ""
+ })
+
+ t.Logf("app private key is : %s", getGitlabAppPrivateKey())
+
+ var oauthResp OauthSuccessResponse
+ err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
+ assert.NoError(t, err)
+ t.Logf("unmarshall ok : %+v", oauthResp)
+
+ encrypted, err := EncryptAuthInfo(&oauthResp)
+ assert.NoError(t, err)
+ t.Logf("encrypted auth info : %s", encrypted)
+
+ oauthRespDecrypted, err := DecryptAuthInfo(encrypted)
+ assert.NoError(t, err)
+
+ assert.Equal(t, &oauthResp, oauthRespDecrypted)
+}
diff --git a/cla-backend-go/gitlab/init.go b/cla-backend-go/gitlab/init.go
new file mode 100644
index 000000000..b390e6124
--- /dev/null
+++ b/cla-backend-go/gitlab/init.go
@@ -0,0 +1,15 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+var gitlabAppPrivateKey string
+
+// Init initializes the required gitlab variables
+func Init(glAppID string, glAppPrivateKey string) {
+ gitlabAppPrivateKey = glAppPrivateKey
+}
+
+func getGitlabAppPrivateKey() string {
+ return gitlabAppPrivateKey
+}
diff --git a/cla-backend-go/gitlab/organization.go b/cla-backend-go/gitlab/organization.go
new file mode 100644
index 000000000..5144bd4ad
--- /dev/null
+++ b/cla-backend-go/gitlab/organization.go
@@ -0,0 +1,26 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "fmt"
+
+ "github.com/xanzy/go-gitlab"
+)
+
+// 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{})
+ if err != nil {
+ return nil, fmt.Errorf("fetching groups failed : %v", err)
+ }
+
+ for _, group := range groups {
+ if group.Name == name {
+ return group, nil
+ }
+ }
+
+ return nil, nil
+}
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index 106261197..6e9d455f7 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -20,6 +20,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fnproject/fdk-go v0.0.2
github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/gin-gonic/gin v1.7.2
github.com/go-openapi/errors v0.19.6
github.com/go-openapi/loads v0.19.5
github.com/go-openapi/runtime v0.19.19
@@ -27,10 +28,11 @@ require (
github.com/go-openapi/strfmt v0.19.5
github.com/go-openapi/swag v0.19.9
github.com/go-openapi/validate v0.19.10
+ github.com/go-playground/validator/v10 v10.7.0 // indirect
github.com/go-resty/resty/v2 v2.3.0
github.com/gofrs/uuid v4.0.0+incompatible
github.com/golang/mock v1.4.4
- github.com/golang/protobuf v1.4.3 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-github/v33 v33.0.0
github.com/google/uuid v1.1.4
github.com/gorilla/sessions v1.2.1 // indirect
@@ -38,9 +40,12 @@ require (
github.com/jessevdk/go-flags v1.4.0
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0
+ github.com/json-iterator/go v1.1.11 // indirect
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2
github.com/kr/pretty v0.2.0 // indirect
+ github.com/leodido/go-urn v1.2.1 // indirect
+ github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mitchellh/mapstructure v1.3.2
github.com/mozillazg/request v0.8.0 // indirect
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc
@@ -57,13 +62,19 @@ require (
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8
+ github.com/ugorji/go v1.2.6 // indirect
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
+ github.com/xanzy/go-gitlab v0.50.1
go.uber.org/ratelimit v0.1.0
- golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
+ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
- golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
+ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
+ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
+ golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
google.golang.org/appengine v1.6.6 // indirect
- google.golang.org/protobuf v1.24.0 // indirect
+ google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.57.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 7e1875a77..e5bde5d75 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -80,8 +80,6 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
-github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
@@ -113,10 +111,6 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
@@ -126,10 +120,12 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
-github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e h1:5CNDPg63TSvbNi3viqFnIywPu2TqLMlWAPuFuuTrFe4=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
+github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
+github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -206,6 +202,15 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
github.com/go-openapi/validate v0.19.10 h1:tG3SZ5DC5KF4cyt7nqLVcQXGj5A7mpaYkAcNPlDK+Yk=
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/go-playground/validator/v10 v10.7.0 h1:gLi5ajTBBheLNt0ctewgq7eolXoDALQd5/y90Hh9ZgM=
+github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
@@ -267,14 +272,15 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -283,14 +289,17 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -334,12 +343,16 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
+github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
@@ -390,8 +403,10 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180128142709-bca911dae073/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -434,6 +449,9 @@ github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
@@ -452,8 +470,10 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
+github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -475,8 +495,10 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
@@ -520,9 +542,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -604,8 +625,13 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v0.0.0-20180129160544-d2b24cf3d3b4 h1:euf5tLM++W5h5uyfs6NSMoCGGhw+hRXMLE/DU6hireM=
github.com/ugorji/go v0.0.0-20180129160544-d2b24cf3d3b4/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
+github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
+github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862 h1:eg5xqGZGatsyRpVnFJkdeUWSFk46lDgkXLvOryv5ySg=
@@ -616,6 +642,8 @@ github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8W
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA=
+github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ=
+github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
@@ -654,8 +682,9 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -703,9 +732,12 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
@@ -717,8 +749,9 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -741,17 +774,25 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -768,7 +809,6 @@ golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -793,6 +833,7 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -806,25 +847,21 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -845,8 +882,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
@@ -854,7 +892,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index a92ccab80..60cf10ac0 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -134,6 +134,7 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-metrics"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs"
- Effect: Allow
Action:
- dynamodb:Query
@@ -193,6 +194,9 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-cla-manager-requests/index/cla-manager-requests-project-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/cla-group-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/foundation-sfid-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-org-sfid-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
environment:
STAGE: ${self:provider.stage}
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 06a4ac8b5..27131ee98 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1604,6 +1604,81 @@ paths:
tags:
- github-repositories
+ /project/{projectSFID}/gitlab/organizations:
+ post:
+ summary: Add new Gitlab Organization in the project
+ description: Endpoint to create a new Gitlab Organization in EasyCLA
+ operationId: addProjectGitlabOrganization
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ - in: body
+ name: body
+ schema:
+ $ref: '#/definitions/create-gitlab-organization'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gitlab-organization'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-organizations
+ get:
+ summary: Get the Gitlab organizations of the project
+ description: Endpoint to return the list of Gitlab organization for the project
+ operationId: getProjectGitlabOrganizations
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/project-gitlab-organizations'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-organizations
+
/cla-group/{claGroupID}/icla/signatures:
get:
summary: List individual signatures for CLA Group
@@ -3665,6 +3740,43 @@ paths:
tags:
- github-activity
+ /gitlab/oauth/callback:
+ 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: []
+ operationId: gitlabOauthCallback
+ parameters:
+ - name: code
+ description: oauth code used to fetch the access token
+ in: query
+ type: string
+ required: true
+ - name: state
+ description: state is used to find the gitlab organization
+ in: query
+ type: string
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/success-response'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-activity
+
responses:
unauthorized:
description: Unauthorized
@@ -3825,7 +3937,7 @@ parameters:
in: query
type: string
required: false
- enum: [ccla,ecla,cla]
+ enum: [ ccla,ecla,cla ]
claType:
name: claType
description: >
@@ -3836,7 +3948,7 @@ parameters:
in: query
type: string
required: false
- enum: [ccla,ecla,icla]
+ enum: [ ccla,ecla,icla ]
templateCLAType:
name: claType
in: query
@@ -4119,6 +4231,12 @@ definitions:
update-github-organization:
$ref: './common/update-github-organization.yaml'
+ gitlab-organization:
+ $ref: './common/gitlab-organization.yaml'
+
+ create-gitlab-organization:
+ $ref: './common/create-github-organization.yaml'
+
user:
$ref: './common/user.yaml'
@@ -5139,6 +5257,9 @@ definitions:
items:
type: string
+ gitlab-organizations:
+ $ref: './common/gitlab-organizations.yaml'
+
project-github-organizations:
type: object
properties:
@@ -5190,6 +5311,56 @@ definitions:
items:
$ref: '#/definitions/project-github-repository'
+ project-gitlab-organizations:
+ type: object
+ properties:
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/project-gitlab-organization'
+
+ project-gitlab-organization:
+ type: object
+ properties:
+ auto_enabled:
+ type: boolean
+ description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
+ x-omitempty: false
+ autoEnableCLAGroupID:
+ type: string
+ description: The CLA Group ID which is attached to the auto-enabled flag
+ autoEnabledCLAGroupName:
+ type: string
+ description: The CLA Group name which is attached to the auto-enabled flag
+ branchProtectionEnabled:
+ type: boolean
+ description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ x-omitempty: false
+ installationURL:
+ type: string
+ x-nullable: true
+ format: uri
+ gitlab_organization_name:
+ type: string
+ description: The Gitlab Organization name
+ example: "kubernetes"
+ # Pattern aligns with UI and other platform services including Org Service
+ # \w Any word character (alphanumeric & underscore), dashes, periods
+ pattern: '^([\w\-\.]+){2,255}$'
+ minLength: 2
+ maxLength: 255
+ connection_status:
+ type: string
+ enum:
+ - connected
+ - partial_connection
+ - connection_failure
+ - no_connection
+ repositories:
+ type: array
+ items:
+ $ref: '#/definitions/project-github-repository'
+
project-github-repository:
type: object
properties:
diff --git a/cla-backend-go/swagger/common/create-github-organization.yaml b/cla-backend-go/swagger/common/create-github-organization.yaml
index 56e32e3ab..1432b16ad 100644
--- a/cla-backend-go/swagger/common/create-github-organization.yaml
+++ b/cla-backend-go/swagger/common/create-github-organization.yaml
@@ -7,7 +7,7 @@ required:
properties:
organizationName:
type: string
- description: The GitHub Organization name
+ description: The Organization name
example: "kubernetes"
# Pattern aligns with UI and other platform services including Org Service
# \w Any word character (alphanumeric & underscore), dashes, periods
@@ -23,5 +23,5 @@ properties:
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
branchProtectionEnabled:
type: boolean
- description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ description: Flag to indicate if this Organization is configured to automatically setup branch protection on CLA enabled repositories.
default: false
diff --git a/cla-backend-go/swagger/common/gitlab-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization.yaml
new file mode 100644
index 000000000..e344805bf
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-organization.yaml
@@ -0,0 +1,82 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ organizationID:
+ type: string
+ description: internal id of the gitlab organization
+ dateCreated:
+ type: string
+ example: "2020-02-06T09:31:49.245630+0000"
+ minLength: 18
+ maxLength: 64
+ dateModified:
+ type: string
+ example: "2020-02-06T09:31:49.245646+0000"
+ minLength: 18
+ maxLength: 64
+ organizationName:
+ type: string
+ example: "communitybridge"
+ organizationSfid:
+ type: string
+ example: "a0941000002wBz4AAA"
+ version:
+ type: string
+ example: "v1"
+ projectSFID:
+ type: string
+ example: "a0941000002wBz4AAA"
+ enabled:
+ type: boolean
+ description: Flag that indicates whether this Gitlab Organization is active
+ x-omitempty: false
+ connected:
+ type: boolean
+ description: Flag that indicates whether this Gitlab Organization is authorized with Gitlab, if false it might mean that Gitlab Oauth process is not compeleted yet or the token was revoked and user needs to go through the auth process again
+ x-omitempty: false
+ autoEnabled:
+ type: boolean
+ description: Flag to indicate if this Gitlab Organization is configured to allow new repositories to be auto-enabled/auto-enrolled in EasyCLA.
+ x-omitempty: false
+ autoEnabledClaGroupID:
+ type: string
+ description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
+ gitlabInfo:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "unable to get gitlab info of communitybridge"
+ details:
+ type: object
+ properties:
+ id:
+ type: integer
+ x-nullable: true
+ example: 1476068
+ bio:
+ type: string
+ x-nullable: true
+ htmlUrl:
+ type: string
+ x-nullable: true
+ example: "https://github.com/communitybridge"
+ format: uri
+ installationURL:
+ type: string
+ x-nullable: true
+ description: "if the Gitlab Organization is not connected yet can use this url to go through the process of authorizing the easyCLA bot"
+ format: uri
+
+ repositories:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "unable to get repositories for installation id : 6854001"
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/github-repository-info'
diff --git a/cla-backend-go/swagger/common/gitlab-organizations.yaml b/cla-backend-go/swagger/common/gitlab-organizations.yaml
new file mode 100644
index 000000000..42a292a78
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-organizations.yaml
@@ -0,0 +1,9 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/gitlab-organization'
diff --git a/cla-backend-go/utils/const.go b/cla-backend-go/utils/const.go
new file mode 100644
index 000000000..bc69f3844
--- /dev/null
+++ b/cla-backend-go/utils/const.go
@@ -0,0 +1,15 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+const (
+ // Connected status
+ Connected = "connected"
+ // PartialConnection status
+ PartialConnection = "partial_connection"
+ // ConnectionFailure status
+ ConnectionFailure = "connection_failure"
+ // NoConnection status
+ NoConnection = "no_connection"
+)
diff --git a/cla-backend-go/utils/utils_user_auth_standalone.go b/cla-backend-go/utils/utils_user_auth_standalone.go
index 8654d9d7e..85a61676f 100644
--- a/cla-backend-go/utils/utils_user_auth_standalone.go
+++ b/cla-backend-go/utils/utils_user_auth_standalone.go
@@ -87,6 +87,8 @@ func IsUserAuthorizedForProjectTree(ctx context.Context, user *auth.User, projec
"adminScopeAllowed": adminScopeAllowed,
}
+ log.WithFields(f).Debugf("checking user auth for project tree")
+
// If we are running locally and want to disable permission checks
if skipPermissionChecks() {
log.WithFields(f).Debug("skipping permissions check")
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index 3655538ba..e4ccf784b 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -62,17 +62,6 @@ func NewService(repo v1GithubOrg.RepositoryInterface, ghRepository v1Repositorie
}
}
-const (
- // Connected status
- Connected = "connected"
- // PartialConnection status
- PartialConnection = "partial_connection"
- // ConnectionFailure status
- ConnectionFailure = "connection_failure"
- // NoConnection status
- NoConnection = "no_connection"
-)
-
func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGithubOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.github_organizations.service.GetGitHubOrganizations",
@@ -166,12 +155,12 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
orgmap[org.OrganizationName] = rorg
out.List = append(out.List, rorg)
if org.OrganizationInstallationID == 0 {
- rorg.ConnectionStatus = NoConnection
+ rorg.ConnectionStatus = utils.NoConnection
} else {
if org.Repositories.Error != "" {
- rorg.ConnectionStatus = ConnectionFailure
+ rorg.ConnectionStatus = utils.ConnectionFailure
} else {
- rorg.ConnectionStatus = Connected
+ rorg.ConnectionStatus = utils.Connected
}
}
}
@@ -208,7 +197,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
log.WithFields(f).WithError(err).Warn("repository github id is not integer")
}
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
- ConnectionStatus: Connected,
+ ConnectionStatus: utils.Connected,
Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
@@ -223,7 +212,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
delete(connectedRepo, key)
} else {
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
- ConnectionStatus: ConnectionFailure,
+ ConnectionStatus: utils.ConnectionFailure,
Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
@@ -231,8 +220,8 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
ProjectID: repo.ProjectSFID,
ParentProjectID: repo.RepositorySfdcID,
})
- if rorg.ConnectionStatus == Connected {
- rorg.ConnectionStatus = PartialConnection
+ if rorg.ConnectionStatus == utils.Connected {
+ rorg.ConnectionStatus = utils.PartialConnection
}
}
}
@@ -244,7 +233,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
continue
}
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
- ConnectionStatus: Connected,
+ ConnectionStatus: utils.Connected,
Enabled: false,
RepositoryID: "",
RepositoryName: notEnabledRepo.repoInfo.RepositoryName,
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
new file mode 100644
index 000000000..ef77190ba
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -0,0 +1,206 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_organizations
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
+ "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ "github.com/gofrs/uuid"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/sirupsen/logrus"
+
+ "github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_organizations"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// Configure setups handlers on api with service
+func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+
+ api.GitlabOrganizationsGetProjectGitlabOrganizationsHandler = gitlab_organizations.GetProjectGitlabOrganizationsHandlerFunc(
+ func(params gitlab_organizations.GetProjectGitlabOrganizationsParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+
+ f := logrus.Fields{
+ "functionName": "gitlab_organizations.handlers.GitlabOrganizationsGetProjectGitlabOrganizationsHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Get Project GitHub Organizations with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewGetProjectGitlabOrganizationsForbidden().WithPayload(
+ utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ result, err := service.GetGitlabOrganizations(ctx, params.ProjectSFID)
+ if err != nil {
+ if strings.ContainsAny(err.Error(), "getProjectNotFound") {
+ msg := fmt.Sprintf("Gitlab organization with project SFID not found: %s", params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewGetProjectGitlabOrganizationsNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ msg := fmt.Sprintf("failed to locate Gitlab organization by project SFID: %s, error: %+v", params.ProjectSFID, err)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewGetProjectGitlabOrganizationsBadRequest().WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ return gitlab_organizations.NewGetProjectGitlabOrganizationsOK().WithPayload(result)
+ })
+
+ api.GitlabOrganizationsAddProjectGitlabOrganizationHandler = gitlab_organizations.AddProjectGitlabOrganizationHandlerFunc(
+ func(params gitlab_organizations.AddProjectGitlabOrganizationParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+
+ f := logrus.Fields{
+ "functionName": "Gitlab_organization.handlers.GitlabOrganizationsAddProjectGitlabOrganizationHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Add Project Gitlab Organizations with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ // Quick check of the parameters
+ if params.Body == nil || params.Body.OrganizationName == nil {
+ msg := fmt.Sprintf("missing organization name in body: %+v", params.Body)
+ log.WithFields(f).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+ f["organizationName"] = utils.StringValue(params.Body.OrganizationName)
+
+ if params.Body.AutoEnabled == nil {
+ msg := fmt.Sprintf("missing autoEnabled name in body: %+v", params.Body)
+ log.WithFields(f).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+ f["autoEnabled"] = utils.BoolValue(params.Body.AutoEnabled)
+ f["autoEnabledClaGroupID"] = params.Body.AutoEnabledClaGroupID
+
+ if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ msg := "AutoEnabledClaGroupID can't be empty when AutoEnabled"
+ err := fmt.Errorf(msg)
+ log.WithFields(f).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ result, err := service.AddGitlabOrganization(ctx, params.ProjectSFID, params.Body)
+ if err != nil {
+ msg := fmt.Sprintf("unable to add github organization, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ // Log the event
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ LfUsername: authUser.UserName,
+ EventType: events.GitlabOrganizationAdded,
+ ProjectSFID: params.ProjectSFID,
+ EventData: &events.GitlabOrganizationAddedEventData{
+ GitlabOrganizationName: *params.Body.OrganizationName,
+ },
+ })
+
+ return gitlab_organizations.NewAddProjectGitlabOrganizationOK().WithPayload(result)
+ })
+
+ api.GitlabActivityGitlabOauthCallbackHandler = gitlab_activity.GitlabOauthCallbackHandlerFunc(func(params gitlab_activity.GitlabOauthCallbackParams) middleware.Responder {
+ f := logrus.Fields{
+ "functionName": "gitlab_organization.handlers.GitlabActivityGitlabOauthCallbackHandler",
+ "code": params.Code,
+ "state": params.State,
+ }
+
+ requestID, _ := uuid.NewV4()
+ reqID := requestID.String()
+ if params.Code == "" {
+ msg := "missing code parameter"
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ if params.State == "" {
+ msg := "missing state parameter"
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ codeParts := strings.Split(params.State, ":")
+ if len(codeParts) != 2 {
+ msg := fmt.Sprintf("invalid state variable passed : %s", params.State)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ gitlabOrganizationID := codeParts[0]
+ stateVar := codeParts[1]
+
+ ctx := context.Background()
+ _, err := service.GetGitlabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
+ if err != nil {
+ msg := fmt.Sprintf("fetching gitlab model failed : %s : %v", gitlabOrganizationID, err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ // now fetch the oauth credentials and store to db
+ oauthResp, err := gitlab.FetchOauthCredentials(params.Code)
+ if err != nil {
+ msg := fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackInternalServerError().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+ log.Infof("oauth resp is like : %+v", oauthResp)
+
+ err = service.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
+ if err != nil {
+ msg := fmt.Sprintf("updating gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabOauthCallbackInternalServerError().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ return gitlab_activity.NewGitlabOauthCallbackOK().WithPayload(&models.SuccessResponse{
+ Code: "200",
+ Message: "oauth credentials stored successfully",
+ XRequestID: reqID,
+ })
+ })
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/models.go b/cla-backend-go/v2/gitlab_organizations/models.go
new file mode 100644
index 000000000..2094a002b
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_organizations/models.go
@@ -0,0 +1,50 @@
+package gitlab_organizations
+
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+import (
+ models2 "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+)
+
+// GitlabOrganization is data model for gitlab organizations
+type GitlabOrganization struct {
+ OrganizationID string `json:"organization_id"`
+ DateCreated string `json:"date_created,omitempty"`
+ DateModified string `json:"date_modified,omitempty"`
+ OrganizationName string `json:"organization_name,omitempty"`
+ OrganizationNameLower string `json:"organization_name_lower,omitempty"`
+ OrganizationSFID string `json:"organization_sfid,omitempty"`
+ ProjectSFID string `json:"project_sfid"`
+ Enabled bool `json:"enabled"`
+ AutoEnabled bool `json:"auto_enabled"`
+ BranchProtectionEnabled bool `json:"branch_protection_enabled"`
+ AutoEnabledClaGroupID string `json:"auto_enabled_cla_group_id,omitempty"`
+ AuthInfo string `json:"auth_info"`
+ AuthState string `json:"auth_state"`
+ Version string `json:"version,omitempty"`
+}
+
+// ToModel converts to models.GitlabOrganization
+func ToModel(in *GitlabOrganization) *models2.GitlabOrganization {
+ return &models2.GitlabOrganization{
+ OrganizationID: in.OrganizationID,
+ DateCreated: in.DateCreated,
+ DateModified: in.DateModified,
+ OrganizationName: in.OrganizationName,
+ OrganizationSfid: in.OrganizationSFID,
+ Version: in.Version,
+ Enabled: in.Enabled,
+ AutoEnabled: in.AutoEnabled,
+ AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
+ ProjectSFID: in.ProjectSFID,
+ }
+}
+
+func toModels(input []*GitlabOrganization) []*models2.GitlabOrganization {
+ out := make([]*models2.GitlabOrganization, 0)
+ for _, in := range input {
+ out = append(out, ToModel(in))
+ }
+ return out
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
new file mode 100644
index 000000000..667726689
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -0,0 +1,352 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_organizations
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/gofrs/uuid"
+ "strings"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/awserr"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/dynamodb"
+ "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
+ "github.com/aws/aws-sdk-go/service/dynamodb/expression"
+ models2 "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+)
+
+// indexes
+const (
+ GitlabOrgSFIDIndex = "gitlab-org-sfid-index"
+ GitlabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
+ GitlabProjectSFIDOrganizationNameIndex = "gitlab-project-sfid-organization-name-index"
+)
+
+// RepositoryInterface is interface for gitlab org data model
+type RepositoryInterface interface {
+ AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.CreateGitlabOrganization) (*models2.GitlabOrganization, error)
+ GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
+ GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error)
+ UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error
+}
+
+// Repository object/struct
+type Repository struct {
+ stage string
+ dynamoDBClient *dynamodb.DynamoDB
+ gitlabOrgTableName string
+}
+
+// NewRepository creates a new instance of the gitlabOrganizations repository
+func NewRepository(awsSession *session.Session, stage string) RepositoryInterface {
+ return Repository{
+ stage: stage,
+ dynamoDBClient: dynamodb.New(awsSession),
+ gitlabOrgTableName: fmt.Sprintf("cla-%s-gitlab-orgs", stage),
+ }
+}
+
+func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.CreateGitlabOrganization) (*models2.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.repository.AddGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "parentProjectSFID": parentProjectSFID,
+ "projectSFID": projectSFID,
+ "organizationName": utils.StringValue(input.OrganizationName),
+ "autoEnabled": utils.BoolValue(input.AutoEnabled),
+ "branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
+ }
+
+ // First, let's check to see if we have an existing gitlab organization with the same name
+ existingRecord, getErr := repo.GetGitlabOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
+ if getErr != nil {
+ log.WithFields(f).WithError(getErr).Debug("unable to locate existing github organization by name")
+ }
+
+ if existingRecord != nil && len(existingRecord.List) > 0 {
+ log.WithFields(f).Debugf("Existing github organization exists in our database, count: %d", len(existingRecord.List))
+ if len(existingRecord.List) > 1 {
+ log.WithFields(f).Warning("more than one github organization with the same name in the database")
+ }
+ if parentProjectSFID == existingRecord.List[0].OrganizationSfid {
+ log.WithFields(f).Debug("Existing github organization with same parent SFID - should be able to update it")
+ } else {
+ log.WithFields(f).Debug("Existing github organization with different parent SFID - won't be able to update it - will return conflict")
+ }
+ return nil, fmt.Errorf("record already exists")
+ }
+
+ // No existing records - create one
+ _, currentTime := utils.CurrentTime()
+ organizationID, err := uuid.NewV4()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("Unable to generate a UUID for gitlab org, error: %v", err)
+ return nil, err
+ }
+
+ authStateNonce, err := uuid.NewV4()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("Unable to generate a auth nonce UUID for gitlab org, error: %v", err)
+ return nil, err
+ }
+
+ enabled := false
+ gitlabOrg := &GitlabOrganization{
+ OrganizationID: organizationID.String(),
+ DateCreated: currentTime,
+ DateModified: currentTime,
+ OrganizationName: *input.OrganizationName,
+ OrganizationNameLower: strings.ToLower(*input.OrganizationName),
+ OrganizationSFID: parentProjectSFID,
+ ProjectSFID: projectSFID,
+ Enabled: aws.BoolValue(&enabled),
+ AutoEnabled: aws.BoolValue(input.AutoEnabled),
+ AutoEnabledClaGroupID: input.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: aws.BoolValue(input.BranchProtectionEnabled),
+ AuthState: authStateNonce.String(),
+ Version: "v1",
+ }
+
+ log.WithFields(f).Debug("Encoding github organization record for adding to the database...")
+ av, err := dynamodbattribute.MarshalMap(gitlabOrg)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to marshall request for query")
+ return nil, err
+ }
+
+ log.WithFields(f).Debug("Adding gitlab organization record to the database...")
+ _, err = repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
+ Item: av,
+ TableName: aws.String(repo.gitlabOrgTableName),
+ ConditionExpression: aws.String("attribute_not_exists(organization_name)"),
+ })
+ if err != nil {
+ if aerr, ok := err.(awserr.Error); ok {
+ switch aerr.Code() {
+ case dynamodb.ErrCodeConditionalCheckFailedException:
+ log.WithFields(f).WithError(err).Warn("gitlab organization already exists")
+ return nil, fmt.Errorf("gitlab organization already exists")
+ }
+ }
+ log.WithFields(f).WithError(err).Warn("cannot put gitlab organization in dynamodb")
+ return nil, err
+ }
+
+ return ToModel(gitlabOrg), nil
+}
+
+// GetGitlabOrganizations get github organizations based on the project SFID
+func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.repository.GetGitHubOrganizations",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ }
+
+ condition := expression.Key("organization_sfid").Equal(expression.Value(projectSFID))
+ builder := expression.NewBuilder().WithKeyCondition(condition)
+
+ //filter := expression.Name("enabled").Equal(expression.Value(true))
+ //builder = builder.WithFilter(filter)
+
+ // Use the nice builder to create the expression
+ expr, err := builder.Build()
+ if err != nil {
+ log.WithFields(f).Warnf("problem building query expression, error: %+v", err)
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ IndexName: aws.String(GitlabOrgSFIDIndex),
+ }
+
+ results, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).Warnf("error retrieving github_organizations using project_sfid = %s. error = %s", projectSFID, err.Error())
+ return nil, err
+ }
+
+ if len(results.Items) == 0 {
+ log.WithFields(f).Debug("no results from query")
+ return &models2.GitlabOrganizations{
+ List: []*models2.GitlabOrganization{},
+ }, nil
+ }
+
+ var resultOutput []*GitlabOrganization
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
+ if err != nil {
+ return nil, err
+ }
+
+ log.WithFields(f).Debug("building response model...")
+ gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
+ return &models2.GitlabOrganizations{List: gitlabOrgList}, nil
+}
+
+// GetGitlabOrganizationByName get github organization by name
+func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganizations, error) {
+ f := logrus.Fields{
+ "functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "githubOrganizationName": githubOrganizationName,
+ }
+
+ condition := expression.Key("organization_name_lower").Equal(expression.Value(strings.ToLower(githubOrganizationName)))
+ builder := expression.NewBuilder().WithKeyCondition(condition)
+ // Use the nice builder to create the expression
+ expr, err := builder.Build()
+ if err != nil {
+ return nil, err
+ }
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ IndexName: aws.String(GitlabOrgLowerNameIndex),
+ }
+
+ log.WithFields(f).Debugf("querying for github organization by name using organization_name_lower=%s...", strings.ToLower(githubOrganizationName))
+ results, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving github_organizations using githubOrganizationName = %s", githubOrganizationName)
+ return nil, err
+ }
+ if len(results.Items) == 0 {
+ log.WithFields(f).Debug("Unable to find github organization by name - no results")
+ return &models2.GitlabOrganizations{
+ List: []*models2.GitlabOrganization{},
+ }, nil
+ }
+ var resultOutput []*GitlabOrganization
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
+ if err != nil {
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ return nil, err
+ }
+
+ ghOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
+ return &models2.GitlabOrganizations{List: ghOrgList}, nil
+}
+
+// GetGitlabOrganization by organization name
+func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_organizations.repository.GetGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationID,
+ }
+
+ log.WithFields(f).Debug("Querying for github organization by name...")
+ result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "organization_id": {
+ S: aws.String(gitlabOrganizationID),
+ },
+ },
+ TableName: aws.String(repo.gitlabOrgTableName),
+ })
+ if err != nil {
+ return nil, err
+ }
+ if len(result.Item) == 0 {
+ log.WithFields(f).Debug("Unable to find github organization by name - no results")
+ return nil, nil
+ }
+
+ var org GitlabOrganization
+ err = dynamodbattribute.UnmarshalMap(result.Item, &org)
+ if err != nil {
+ log.WithFields(f).Warnf("error unmarshalling organization table data, error: %v", err)
+ return nil, err
+ }
+ return &org, nil
+}
+
+// UpdateGitlabOrganizationAuth updates the specified Gitlab organization oauth info
+func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error {
+ f := logrus.Fields{
+ "functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationAuth",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationID,
+ "tableName": repo.gitlabOrgTableName,
+ }
+
+ _, currentTime := utils.CurrentTime()
+ gitlabOrg, lookupErr := repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ if lookupErr != nil {
+ log.WithFields(f).Warnf("error looking up Gitlab organization by id, error: %+v", lookupErr)
+ return lookupErr
+ }
+ if gitlabOrg == nil {
+ lookupErr := errors.New("unable to lookup Gitlab organization by id")
+ log.WithFields(f).Warnf("error looking up Gitlab organization, error: %+v", lookupErr)
+ return lookupErr
+ }
+
+ expressionAttributeNames := map[string]*string{
+ "#A": aws.String("auth_info"),
+ "#M": aws.String("date_modified"),
+ }
+ expressionAttributeValues := map[string]*dynamodb.AttributeValue{
+ ":a": {
+ S: aws.String(authInfo),
+ },
+ ":m": {
+ S: aws.String(currentTime),
+ },
+ }
+ updateExpression := "SET #A = :a, #M = :m"
+
+ input := &dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "organization_id": {
+ S: aws.String(gitlabOrg.OrganizationID),
+ },
+ },
+ ExpressionAttributeNames: expressionAttributeNames,
+ ExpressionAttributeValues: expressionAttributeValues,
+ UpdateExpression: &updateExpression,
+ TableName: aws.String(repo.gitlabOrgTableName),
+ }
+
+ log.WithFields(f).Debug("updating gitlab organization record...")
+ _, updateErr := repo.dynamoDBClient.UpdateItem(input)
+ if updateErr != nil {
+ log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v", updateErr)
+ return updateErr
+ }
+
+ return nil
+}
+
+func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*GitlabOrganization) []*models2.GitlabOrganization {
+ f := logrus.Fields{
+ "functionName": "buildGitlabOrganizationListModels",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab info for the list")
+ // Convert the database model to a response model
+ return toModels(gitlabOrganizations)
+
+ // TODO: Fetch the gitlab information
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
new file mode 100644
index 000000000..7ef3cf11a
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -0,0 +1,266 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_organizations
+
+import (
+ "context"
+ "fmt"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+ "github.com/go-openapi/strfmt"
+ "net/url"
+ "sort"
+ "strings"
+
+ v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+ "github.com/sirupsen/logrus"
+)
+
+// Service contains functions of GitlabOrganizations service
+type Service interface {
+ GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGitlabOrganizations, error)
+ AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.CreateGitlabOrganization) (*models.GitlabOrganization, error)
+ GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
+ GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
+ UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab.OauthSuccessResponse) error
+}
+
+type service struct {
+ repo RepositoryInterface
+ projectsCLAGroupService projects_cla_groups.Repository
+}
+
+// NewService creates a new githubOrganizations service
+func NewService(repo RepositoryInterface, projectsCLAGroupService projects_cla_groups.Repository) Service {
+ return service{
+ repo: repo,
+ projectsCLAGroupService: projectsCLAGroupService,
+ }
+}
+
+func (s service) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationID,
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationID)
+ dbModel, err := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ if err != nil {
+ return nil, err
+ }
+
+ return ToModel(dbModel), nil
+}
+
+func (s service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab.OauthSuccessResponse) error {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.UpdateGitlabOrganizationAuth",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationID,
+ }
+
+ log.WithFields(f).Debugf("updating gitlab org auth")
+ authInfoEncrypted, err := gitlab.EncryptAuthInfo(oauthResp)
+ if err != nil {
+ return fmt.Errorf("encrypt failed : %v", err)
+ }
+
+ return s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, authInfoEncrypted)
+
+}
+
+func (s service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationID,
+ "authState": authState,
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationID)
+ dbModel, err := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ if err != nil {
+ return nil, err
+ }
+
+ if dbModel.AuthState != authState {
+ return nil, fmt.Errorf("auth state doesn't match")
+ }
+
+ return ToModel(dbModel), nil
+}
+
+func (s service) AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.CreateGitlabOrganization) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.AddGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "autoEnabled": utils.BoolValue(input.AutoEnabled),
+ "branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
+ "organizationName": utils.StringValue(input.OrganizationName),
+ }
+
+ log.WithFields(f).Debug("looking up project in project service...")
+ psc := v2ProjectService.GetClient()
+ project, err := psc.GetProject(projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading project details from the project service")
+ return nil, err
+ }
+
+ var parentProjectSFID string
+ if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
+ (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
+ parentProjectSFID = projectSFID
+ } else {
+ parentProjectSFID = utils.StringValue(project.Parent)
+ }
+ f["parentProjectSFID"] = parentProjectSFID
+ log.WithFields(f).Debug("located parentProjectID...")
+
+ log.WithFields(f).Debug("adding github organization...")
+ resp, err := s.repo.AddGitlabOrganization(ctx, parentProjectSFID, projectSFID, input)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem adding github organization for project")
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGitlabOrganizations, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitlabOrganizations",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ }
+
+ // Load the GitHub Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
+ log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
+ orgs, err := s.repo.GetGitlabOrganizations(ctx, projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading gitlab organizations from the project service")
+ return nil, err
+ }
+
+ psc := v2ProjectService.GetClient()
+ log.WithFields(f).Debug("loading project details from the project service...")
+ projectServiceRecord, err := psc.GetProject(projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading project details from the project service")
+ return nil, err
+ }
+
+ var parentProjectSFID string
+ if utils.IsProjectHasRootParent(projectServiceRecord) {
+ parentProjectSFID = projectSFID
+ } else {
+ parentProjectSFID = utils.StringValue(projectServiceRecord.Parent)
+ }
+ f["parentProjectSFID"] = parentProjectSFID
+ log.WithFields(f).Debug("located parentProjectID...")
+
+ // Our response model
+ out := &models.ProjectGitlabOrganizations{
+ List: make([]*models.ProjectGitlabOrganization, 0),
+ }
+
+ // Next, we need to load a bunch of additional data for the response including the github status (if it's still connected/live, not renamed/moved), the CLA Group details, etc.
+
+ // A temp data model for holding the intermediate results
+ type gitlabRepoInfo struct {
+ orgName string
+ repoInfo *v1Models.GithubRepositoryInfo
+ }
+
+ orgmap := make(map[string]*models.ProjectGitlabOrganization)
+ for _, org := range orgs.List {
+ autoEnabledCLAGroupName := ""
+ if org.AutoEnabledClaGroupID != "" {
+ log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitHub auth enabled CLA Group response", org.AutoEnabledClaGroupID)
+ claGroupMode, claGroupLookupErr := s.projectsCLAGroupService.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
+ if claGroupLookupErr != nil {
+ log.WithFields(f).WithError(claGroupLookupErr).Warnf("Unable to lookup CLA Group by ID: %s", org.AutoEnabledClaGroupID)
+ }
+ if claGroupMode != nil {
+ autoEnabledCLAGroupName = claGroupMode.ProjectName
+ }
+ }
+
+ orgDetailed, err := s.repo.GetGitlabOrganization(ctx, org.OrganizationID)
+ if err != nil {
+ log.WithFields(f).Errorf("fetching gitlab org failed : %s : %v", org.OrganizationID, err)
+ continue
+ }
+
+ installationURL := buildInstallationURL(org.OrganizationID, orgDetailed.AuthState)
+ rorg := &models.ProjectGitlabOrganization{
+ AutoEnabled: org.AutoEnabled,
+ AutoEnableCLAGroupID: org.AutoEnabledClaGroupID,
+ AutoEnabledCLAGroupName: autoEnabledCLAGroupName,
+ GitlabOrganizationName: org.OrganizationName,
+ Repositories: make([]*models.ProjectGithubRepository, 0),
+ InstallationURL: installationURL,
+ }
+
+ if orgDetailed.AuthInfo == "" {
+ rorg.ConnectionStatus = utils.NoConnection
+ } else {
+ glClient, err := gitlab.NewGitlabOauthClient(orgDetailed.AuthInfo)
+ if err != nil {
+ log.WithFields(f).Errorf("initializing gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
+ rorg.ConnectionStatus = utils.ConnectionFailure
+ } else {
+ user, _, err := glClient.Users.CurrentUser()
+ if err != nil {
+ log.WithFields(f).Errorf("using gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
+ rorg.ConnectionStatus = utils.ConnectionFailure
+ } else {
+ log.WithFields(f).Debugf("connected to user : %s for gitlab org : %s", user.Name, org.OrganizationID)
+ rorg.ConnectionStatus = utils.Connected
+ }
+ }
+ }
+
+ orgmap[org.OrganizationName] = rorg
+ out.List = append(out.List, rorg)
+ }
+
+ // Sort everything nicely
+ sort.Slice(out.List, func(i, j int) bool {
+ return strings.ToLower(out.List[i].GitlabOrganizationName) < strings.ToLower(out.List[j].GitlabOrganizationName)
+ })
+ for _, orgList := range out.List {
+ sort.Slice(orgList.Repositories, func(i, j int) bool {
+ return strings.ToLower(orgList.Repositories[i].RepositoryName) < strings.ToLower(orgList.Repositories[j].RepositoryName)
+ })
+ }
+
+ return out, nil
+}
+
+func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {
+ base := "https://gitlab.com/oauth/authorize"
+ c := config.GetConfig()
+ state := fmt.Sprintf("%s:%s", gitlabOrgID, authStateNonce)
+
+ params := url.Values{}
+ params.Add("client_id", c.Gitlab.AppID)
+ params.Add("redirect_uri", c.Gitlab.RedirectURI)
+ //params.Add("redirect_uri", "http://localhost:8080/v4/gitlab/oauth/callback")
+ params.Add("response_type", "code")
+ params.Add("state", state)
+ params.Add("scope", "read_user read_api read_repository write_repository api")
+
+ installationURL := strfmt.URI(base + "?" + params.Encode())
+ return &installationURL
+
+}
From e77b6453ada25acda1545188db9096b6628bd3d0 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Tue, 27 Jul 2021 18:54:51 +0300
Subject: [PATCH 0370/1276] gitlab org python dynamo models (#3059)
Signed-off-by: makkalot
---
cla-backend/cla/models/dynamo_models.py | 233 +++++++++++++++++-
cla-backend/cla/models/model_interfaces.py | 46 ++++
.../cla/tests/unit/test_gitlab_org_models.py | 40 +++
3 files changed, 313 insertions(+), 6 deletions(-)
create mode 100644 cla-backend/cla/tests/unit/test_gitlab_org_models.py
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 221106e6e..f50f85ff6 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -403,6 +403,56 @@ class Meta:
organization_sfid = UnicodeAttribute(hash_key=True)
+class GitlabOrgSFIndex(GlobalSecondaryIndex):
+ """
+ This class represents a global secondary index for querying gitlab organizations by a Salesforce ID.
+ """
+
+ class Meta:
+ """Meta class for external ID github org index."""
+
+ index_name = "gitlab-org-sfid-index"
+ write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
+ read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
+ projection = AllProjection()
+
+ organization_sfid = UnicodeAttribute(hash_key=True)
+
+
+class GitlabOrgProjectSfidOrganizationNameIndex(GlobalSecondaryIndex):
+ """
+ This class represents a global secondary index for querying gitlab organizations by a Project sfid and
+ Organization Name.
+ """
+
+ class Meta:
+ """Meta class for external ID github org index."""
+
+ index_name = "gitlab-project-sfid-organization-name-index"
+ write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
+ read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
+ projection = AllProjection()
+
+ project_sfid = UnicodeAttribute(hash_key=True)
+ organization_name = UnicodeAttribute(range_key=True)
+
+
+class GitlabOrganizationNameLowerIndex(GlobalSecondaryIndex):
+ """
+ This class represents a global secondary index for querying gitlab organizations by Organization Name.
+ """
+
+ class Meta:
+ """Meta class for external ID github org index."""
+
+ index_name = "gitlab-organization-name-lower-search-index"
+ write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
+ read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
+ projection = AllProjection()
+
+ organization_name_lower = UnicodeAttribute(hash_key=True)
+
+
class GerritProjectIDIndex(GlobalSecondaryIndex):
"""
This class represents a global secondary index for querying gerrit's by the project ID
@@ -2943,7 +2993,8 @@ def get_employee_signature_by_company_project(self, company_id, project_id, user
"Why do we have more than one employee signature for this user? - Will return the first one only.")
return signatures[0]
- def get_employee_signature_by_company_project_list(self, company_id, project_id, user_id) -> Optional[List[Signature]]:
+ def get_employee_signature_by_company_project_list(self, company_id, project_id, user_id) -> Optional[
+ List[Signature]]:
"""
Returns the employee signature for the specified user associated with
the project/company. Returns None if no employee signature exists for
@@ -3535,10 +3586,34 @@ def get_expire_timestamp(self):
return exp_datetime.timestamp()
+class GitlabOrgModel(BaseModel):
+ """
+ Represents a Gitlab Organization in the database.
+ """
+
+ class Meta:
+ table_name = "cla-{}-gitlab-orgs".format(stage)
+ if stage == "local":
+ host = "http://localhost:8000"
+
+ organization_id = UnicodeAttribute(hash_key=True)
+ organization_name = UnicodeAttribute(null=True)
+ organization_name_lower = UnicodeAttribute(null=True)
+ organization_sfid = UnicodeAttribute()
+ project_sfid = UnicodeAttribute()
+ organization_sfid_index = GitlabOrgSFIndex()
+ project_sfid_organization_name_index = GitlabOrgProjectSfidOrganizationNameIndex()
+ organization_name_lowe_index = GitlabOrganizationNameLowerIndex()
+ auto_enabled = BooleanAttribute(null=True)
+ auto_enabled_cla_group_id = UnicodeAttribute(null=True)
+ branch_protection_enabled = BooleanAttribute(null=True)
+ enabled = BooleanAttribute(null=True)
+ note = UnicodeAttribute(null=True)
+
+
class GitHubOrgModel(BaseModel):
"""
- Represents a Github Organization in the database.
- Company_id, project_id are deprecated now that organizations are under an SFDC ID.
+ Represents a Gitlab Organization in the database.
"""
class Meta:
@@ -3553,7 +3628,9 @@ class Meta:
organization_installation_id = NumberAttribute(null=True)
organization_sfid = UnicodeAttribute()
project_sfid = UnicodeAttribute()
- organization_sfid_index = GithubOrgSFIndex()
+ organization_sfid_index = GitlabOrgSFIndex()
+ project_sfid_organization_name_index = GitlabOrgProjectSfidOrganizationNameIndex()
+ organization_name_lowe_index = GitlabOrganizationNameLowerIndex()
organization_project_id = UnicodeAttribute(null=True)
organization_company_id = UnicodeAttribute(null=True)
auto_enabled = BooleanAttribute(null=True)
@@ -3646,7 +3723,7 @@ def get_note(self):
:rtype: str
"""
return self.model.note
-
+
def get_enabled(self):
return self.model.enabled
@@ -3678,7 +3755,7 @@ def set_branch_protection_enabled(self, branch_protection_enabled):
def set_note(self, note):
self.model.note = note
-
+
def set_enabled(self, enabled):
self.model.enabled = enabled
@@ -3717,6 +3794,150 @@ def all(self):
return ret
+class GitlabOrg(model_interfaces.GitlabOrg): # pylint: disable=too-many-public-methods
+ """
+ ORM-agnostic wrapper for the DynamoDB GitlabOrg model.
+ """
+
+ def __init__(
+ self, organization_id=None, organization_name=None, organization_sfid=None,
+ project_sfid=None, auto_enabled=False, branch_protection_enabled=False, note=None, enabled=True
+ ):
+ super(GitlabOrg).__init__()
+ self.model = GitlabOrgModel()
+ if not organization_id:
+ organization_id = str(uuid.uuid4())
+ self.model.organization_id = organization_id
+
+ self.model.organization_name = organization_name
+ if self.model.organization_name:
+ self.model.organization_name_lower = self.model.organization_name.lower()
+
+ self.model.organization_sfid = organization_sfid
+ self.model.project_sfid = project_sfid
+ self.model.auto_enabled = auto_enabled
+ self.model.branch_protection_enabled = branch_protection_enabled
+ self.model.enabled = enabled
+ self.model.note = note
+
+ def __str__(self):
+ return (
+ f'organization id:{self.model.organization_id}, '
+ f'organization name:{self.model.organization_name}, '
+ f'organization SFID: {self.model.organization_sfid}, '
+ f'auto_enabled: {self.model.auto_enabled},'
+ f'branch_protection_enabled: {self.model.branch_protection_enabled},'
+ f'enabled: {self.model.enabled},'
+ f'note: {self.model.note}'
+ )
+
+ def to_dict(self):
+ ret = dict(self.model)
+ if ret["organization_sfid"] == "null":
+ ret["organization_sfid"] = None
+ return ret
+
+ def save(self) -> None:
+ self.model.date_modified = datetime.datetime.utcnow()
+ self.model.save()
+
+ def load(self, organization_id: str):
+ try:
+ organization = self.model.get(organization_id)
+ except GitlabOrgModel.DoesNotExist:
+ raise cla.models.DoesNotExist("Gitlab Org not found")
+ self.model = organization
+
+ def delete(self):
+ self.model.delete()
+
+ def get_organization_id(self):
+ return self.model.organization_id
+
+ def get_organization_name(self):
+ return self.model.organization_name
+
+ def get_organization_sfid(self):
+ return self.model.organization_sfid
+
+ def get_project_sfid(self):
+ return self.model.project_sfid
+
+ def get_organization_name_lower(self):
+ return self.model.organization_name_lower
+
+ def get_auto_enabled(self):
+ return self.model.auto_enabled
+
+ def get_branch_protection_enabled(self):
+ return self.model.branch_protection_enabled
+
+ def get_note(self):
+ """
+ Getter for the note.
+ :return: the note value for the github organization record
+ :rtype: str
+ """
+ return self.model.note
+
+ def get_enabled(self):
+ return self.model.enabled
+
+ def set_organization_name(self, organization_name):
+ self.model.organization_name = organization_name
+ if self.model.organization_name:
+ self.model.organization_name_lower = self.model.organization_name.lower()
+
+ def set_organization_sfid(self, organization_sfid):
+ self.model.organization_sfid = organization_sfid
+
+ def set_project_sfid(self, project_sfid):
+ self.model.project_sfid = project_sfid
+
+ def set_organization_name_lower(self, organization_name_lower):
+ self.model.organization_name_lower = organization_name_lower
+
+ def set_auto_enabled(self, auto_enabled):
+ self.model.auto_enabled = auto_enabled
+
+ def set_branch_protection_enabled(self, branch_protection_enabled):
+ self.model.branch_protection_enabled = branch_protection_enabled
+
+ def set_note(self, note):
+ self.model.note = note
+
+ def set_enabled(self, enabled):
+ self.model.enabled = enabled
+
+ def get_organization_by_sfid(self, sfid) -> List:
+ organization_generator = self.model.organization_sfid_index.query(sfid)
+ organizations = []
+ for org_model in organization_generator:
+ org = GitlabOrg()
+ org.model = org_model
+ organizations.append(org)
+ return organizations
+
+ def get_organization_by_lower_name(self, organization_name):
+ organization_name = organization_name.lower()
+ organization_generator = self.model.organization_name_lowe_index.query(organization_name)
+ organizations = []
+ for org_model in organization_generator:
+ org = GitlabOrg()
+ org.model = org_model
+ organizations.append(org)
+ return organizations
+
+ def all(self):
+ orgs = self.model.scan()
+ ret = []
+ for organization in orgs:
+ org = GitlabOrg()
+ org.model = organization
+ ret.append(org)
+ return ret
+
+
class GerritModel(BaseModel):
"""
Represents a Gerrit Instance in the database.
diff --git a/cla-backend/cla/models/model_interfaces.py b/cla-backend/cla/models/model_interfaces.py
index 49665dacc..0dce58e9d 100644
--- a/cla-backend/cla/models/model_interfaces.py
+++ b/cla-backend/cla/models/model_interfaces.py
@@ -1788,6 +1788,52 @@ def set_document_tab_height(self, tab_height):
raise NotImplementedError()
+class GitlabOrg(object):
+ """
+ Interface to the GitlabOrg model
+ """
+
+ def to_dict(self):
+ """
+ Converts models to dictionaries for JSON serialization.
+
+ :return: A dict representation of the model.
+ :rtype: dict
+ """
+ raise NotImplementedError()
+
+ def save(self):
+ """
+ Simple abstraction around the supported ORMs to save a model.
+ """
+ raise NotImplementedError()
+
+ def load(self, organization_id):
+ """
+ Simple abstraction around the supported ORMs to load a model.
+ Should populate the current object.
+
+ :param organization_id: The gitlab organization's ID.
+ :type organization_id: string
+ """
+ raise NotImplementedError()
+
+ def delete(self):
+ """
+ Simple abstraction around the supported ORMs to delete a model.
+ """
+ raise NotImplementedError()
+
+ def all(self):
+ """
+ Fetches all gitlab organizations in the CLA system.
+
+ :return: A list of GitlabOrg objects.
+ :rtype: [cla.models.model_interfaces.GitlabOrg]
+ """
+ raise NotImplementedError()
+
+
class GitHubOrg(object):
"""
Interface to the GitHubOrg model.
diff --git a/cla-backend/cla/tests/unit/test_gitlab_org_models.py b/cla-backend/cla/tests/unit/test_gitlab_org_models.py
new file mode 100644
index 000000000..e0c3ed85a
--- /dev/null
+++ b/cla-backend/cla/tests/unit/test_gitlab_org_models.py
@@ -0,0 +1,40 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+from cla.models.dynamo_models import GitlabOrg
+
+
+def test_gitlab_org_model():
+ gitlab_org = GitlabOrg(organization_name="GitlabOrg1")
+ assert gitlab_org.get_organization_id()
+ assert gitlab_org.get_organization_name() == "GitlabOrg1"
+ assert gitlab_org.get_organization_name_lower() == "gitlaborg1"
+ assert not gitlab_org.get_auto_enabled()
+ assert gitlab_org.get_enabled()
+ assert not gitlab_org.get_branch_protection_enabled()
+ assert not gitlab_org.get_project_sfid()
+ assert not gitlab_org.get_organization_sfid()
+
+ gitlab_org.set_organization_name("GitlabOrg2")
+ assert gitlab_org.get_organization_name() == "GitlabOrg2"
+ assert gitlab_org.get_organization_name_lower() == "gitlaborg2"
+
+ gitlab_org.set_enabled(False)
+ assert not gitlab_org.get_enabled()
+
+ gitlab_org.set_project_sfid("project_sfid_1")
+ assert gitlab_org.get_project_sfid() == "project_sfid_1"
+
+ gitlab_org.set_organization_sfid("organization_sfid_1")
+ assert gitlab_org.get_organization_sfid() == "organization_sfid_1"
+
+ gitlab_org.set_branch_protection_enabled(True)
+ assert gitlab_org.get_branch_protection_enabled()
+ gitlab_org.set_auto_enabled(True)
+ assert gitlab_org.get_auto_enabled()
+
+ gitlab_org_dict = gitlab_org.to_dict()
+ assert gitlab_org_dict["organization_id"] == gitlab_org.get_organization_id()
+ assert gitlab_org_dict["organization_name"] == "GitlabOrg2"
+ assert gitlab_org_dict["project_sfid"] == "project_sfid_1"
+ assert gitlab_org_dict["organization_sfid"] == "organization_sfid_1"
From 71377777784d9b431acf045501a8159dddbb8756 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 27 Jul 2021 15:30:47 -0700
Subject: [PATCH 0371/1276] Added Logic to Resolve Inconsistent Nil Slice In
API Response (#3065)
---
cla-backend-go/Makefile | 5 +-
.../github/branch_protection/interfaces.go | 2 +-
.../github/branch_protection/mock.go | 2 +-
.../branch_protection/protected_branch.go | 2 +-
.../protected_branch_limiter.go | 2 +-
cla-backend-go/github/client.go | 2 +-
cla-backend-go/github/github_installation.go | 8 +-
cla-backend-go/github/github_org.go | 2 +-
cla-backend-go/github/github_repository.go | 2 +-
cla-backend-go/github/github_user.go | 2 +-
cla-backend-go/github/handlers.go | 2 +-
cla-backend-go/go.mod | 32 +-
cla-backend-go/go.sum | 515 +++++++++++-------
cla-backend-go/signatures/converters.go | 8 +-
cla-backend-go/signatures/service.go | 2 +-
cla-backend-go/tests/utils_conversion_test.go | 28 +
cla-backend-go/utils/conversion.go | 9 +
cla-backend-go/v2/dynamo_events/autoenable.go | 2 +-
cla-backend-go/v2/github_activity/handlers.go | 2 +-
cla-backend-go/v2/github_activity/service.go | 2 +-
.../v2/github_activity/service_test.go | 2 +-
.../v2/gitlab_organizations/repository.go | 3 +-
.../v2/gitlab_organizations/service.go | 5 +-
23 files changed, 405 insertions(+), 236 deletions(-)
create mode 100644 cla-backend-go/tests/utils_conversion_test.go
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 406f757a2..89cfb5ab2 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -41,7 +41,8 @@ setup: $(LINT_TOOL) setup-dev setup-deploy
tool-setup:
@echo "Installing gobin for installing tools..."
@# gobin is the equivalent of 'go get' whilst in module-aware mode but this does not modify your go.mod
- GO111MODULE=off go get -u github.com/myitcv/gobin
+ #GO111MODULE=off go get -u github.com/myitcv/gobin
+ go get -u github.com/myitcv/gobin
setup_dev: setup-dev
setup-dev: tool-setup
@@ -199,7 +200,7 @@ run:
deps:
go env -w GOPRIVATE=github.com/LF-Engineering/*
- go mod download
+ go mod download -x
build: build-linux
build-linux: deps
diff --git a/cla-backend-go/github/branch_protection/interfaces.go b/cla-backend-go/github/branch_protection/interfaces.go
index 847b3d250..ee97e3947 100644
--- a/cla-backend-go/github/branch_protection/interfaces.go
+++ b/cla-backend-go/github/branch_protection/interfaces.go
@@ -6,7 +6,7 @@ package branch_protection
import (
"context"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
"github.com/shurcooL/githubv4"
)
diff --git a/cla-backend-go/github/branch_protection/mock.go b/cla-backend-go/github/branch_protection/mock.go
index 456925c0e..b0ac6cd4d 100644
--- a/cla-backend-go/github/branch_protection/mock.go
+++ b/cla-backend-go/github/branch_protection/mock.go
@@ -13,7 +13,7 @@ import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
- github "github.com/google/go-github/v33/github"
+ github "github.com/google/go-github/v37/github"
githubv4 "github.com/shurcooL/githubv4"
)
diff --git a/cla-backend-go/github/branch_protection/protected_branch.go b/cla-backend-go/github/branch_protection/protected_branch.go
index ea56ef5a3..5ba835f20 100644
--- a/cla-backend-go/github/branch_protection/protected_branch.go
+++ b/cla-backend-go/github/branch_protection/protected_branch.go
@@ -14,7 +14,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- githubpkg "github.com/google/go-github/v33/github"
+ githubpkg "github.com/google/go-github/v37/github"
)
const (
diff --git a/cla-backend-go/github/branch_protection/protected_branch_limiter.go b/cla-backend-go/github/branch_protection/protected_branch_limiter.go
index 7166e9de8..ead673382 100644
--- a/cla-backend-go/github/branch_protection/protected_branch_limiter.go
+++ b/cla-backend-go/github/branch_protection/protected_branch_limiter.go
@@ -8,7 +8,7 @@ import (
"fmt"
"github.com/communitybridge/easycla/cla-backend-go/github"
- githubpkg "github.com/google/go-github/v33/github"
+ githubpkg "github.com/google/go-github/v37/github"
"github.com/shurcooL/githubv4"
"go.uber.org/ratelimit"
"golang.org/x/time/rate"
diff --git a/cla-backend-go/github/client.go b/cla-backend-go/github/client.go
index 84070b97e..e57e5749c 100644
--- a/cla-backend-go/github/client.go
+++ b/cla-backend-go/github/client.go
@@ -13,7 +13,7 @@ import (
"github.com/shurcooL/githubv4"
"github.com/bradleyfalzon/ghinstallation"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
"golang.org/x/oauth2"
)
diff --git a/cla-backend-go/github/github_installation.go b/cla-backend-go/github/github_installation.go
index e609694c2..d8f2b9fe2 100644
--- a/cla-backend-go/github/github_installation.go
+++ b/cla-backend-go/github/github_installation.go
@@ -11,7 +11,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
)
@@ -40,15 +40,15 @@ func GetInstallationRepositories(ctx context.Context, installationID int64) ([]*
}
for {
- repos, resp, err := client.Apps.ListRepos(ctx, opts)
+ listReposResponse, resp, err := client.Apps.ListRepos(ctx, opts)
if err != nil {
msg := fmt.Sprintf("error while getting repositories associated for installation, error: %+v", err)
log.WithFields(f).WithError(err).Warn(msg)
return nil, errors.New(msg)
}
- log.WithFields(f).Debugf("fetched %d records...", len(repos))
- allRepos = append(allRepos, repos...)
+ log.WithFields(f).Debugf("fetched %d records...", len(listReposResponse.Repositories))
+ allRepos = append(allRepos, listReposResponse.Repositories...)
if resp.NextPage == 0 {
break
}
diff --git a/cla-backend-go/github/github_org.go b/cla-backend-go/github/github_org.go
index fb8df7581..9bcc4be1e 100644
--- a/cla-backend-go/github/github_org.go
+++ b/cla-backend-go/github/github_org.go
@@ -12,7 +12,7 @@ import (
"github.com/sirupsen/logrus"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
)
// errors
diff --git a/cla-backend-go/github/github_repository.go b/cla-backend-go/github/github_repository.go
index 268da0859..7025efd3d 100644
--- a/cla-backend-go/github/github_repository.go
+++ b/cla-backend-go/github/github_repository.go
@@ -13,7 +13,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
)
// errors
diff --git a/cla-backend-go/github/github_user.go b/cla-backend-go/github/github_user.go
index 32a18d728..78745611e 100644
--- a/cla-backend-go/github/github_user.go
+++ b/cla-backend-go/github/github_user.go
@@ -9,7 +9,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
)
// GetUserDetails return github users details
diff --git a/cla-backend-go/github/handlers.go b/cla-backend-go/github/handlers.go
index 24784a1a8..4f7516366 100644
--- a/cla-backend-go/github/handlers.go
+++ b/cla-backend-go/github/handlers.go
@@ -20,7 +20,7 @@ import (
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/gofrs/uuid"
- ghLib "github.com/google/go-github/v33/github"
+ ghLib "github.com/google/go-github/v37/github"
"github.com/savaki/dynastore"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index 6e9d455f7..d584c184d 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
module github.com/communitybridge/easycla/cla-backend-go
-go 1.15
+go 1.16
replace github.com/awslabs/aws-lambda-go-api-proxy => github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2
@@ -19,7 +19,6 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fnproject/fdk-go v0.0.2
- github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gin-gonic/gin v1.7.2
github.com/go-openapi/errors v0.19.6
github.com/go-openapi/loads v0.19.5
@@ -31,50 +30,41 @@ require (
github.com/go-playground/validator/v10 v10.7.0 // indirect
github.com/go-resty/resty/v2 v2.3.0
github.com/gofrs/uuid v4.0.0+incompatible
- github.com/golang/mock v1.4.4
- github.com/golang/protobuf v1.5.2 // indirect
- github.com/google/go-github/v33 v33.0.0
+ github.com/golang/mock v1.6.0
+ github.com/google/go-github/v37 v37.0.0
github.com/google/uuid v1.1.4
github.com/gorilla/sessions v1.2.1 // indirect
github.com/imroc/req v0.3.0
github.com/jessevdk/go-flags v1.4.0
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0
- github.com/json-iterator/go v1.1.11 // indirect
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2
github.com/kr/pretty v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
- github.com/mitchellh/mapstructure v1.3.2
+ github.com/mitchellh/mapstructure v1.4.1
github.com/mozillazg/request v0.8.0 // indirect
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc
- github.com/pelletier/go-toml v1.8.0 // indirect
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
- github.com/sirupsen/logrus v1.7.0
- github.com/spf13/afero v1.3.0 // indirect
- github.com/spf13/cast v1.3.1 // indirect
+ github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1
- github.com/spf13/jwalterweatherman v1.1.0 // indirect
- github.com/spf13/viper v1.7.1
- github.com/stretchr/testify v1.6.1
+ github.com/spf13/viper v1.8.1
+ github.com/stretchr/testify v1.7.0
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8
github.com/ugorji/go v1.2.6 // indirect
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
github.com/xanzy/go-gitlab v0.50.1
go.uber.org/ratelimit v0.1.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
- golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
- golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
- golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
+ golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
+ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
+ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
+ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
- golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
- google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.27.1 // indirect
- gopkg.in/ini.v1 v1.57.0 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index e5bde5d75..2375188ca 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -4,25 +4,41 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM=
github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUamMO8SWS1m4ZKJGGeh9lT985U=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
@@ -30,27 +46,20 @@ github.com/LF-Engineering/lfx-kit v0.1.25 h1:Bb3Snc72ppBmbS5CMoLBGFg1Tt7ZhZktZLJ
github.com/LF-Engineering/lfx-kit v0.1.25/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
github.com/LF-Engineering/lfx-models v0.6.44 h1:a4/6+Hc05caUCzd9eQnZIioZUhWxtgpfgVRuf/M2SRY=
github.com/LF-Engineering/lfx-models v0.6.44/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
-github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
-github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -65,60 +74,59 @@ github.com/aws/aws-sdk-go v1.36.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.1.0/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
github.com/fnproject/fdk-go v0.0.2/go.mod h1:9m+nEyku9SqJAVJQsfZOZBQzFkCs+jvmbZJhvgDX4ts=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -127,16 +135,13 @@ github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e/go.mod h1:7cKuhb5qV2
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a h1:l4yNPeA/3kNJwE0uDBVXtFX8hfiHrlqkXBLPOrchWzk=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -217,171 +222,163 @@ github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
-github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
-github.com/gobuffalo/flect v0.1.3 h1:3GQ53z7E3o00C/yy7Ko8VXqQXoJGLkrTQCLTF1EjoXU=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
-github.com/gobuffalo/genny v0.1.1 h1:iQ0D6SpNXIxu52WESsD+KoQ7af2e3nCfnSBoSF/hKe0=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
-github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211 h1:mSVZ4vj4khv+oThUfS+SQU3UuFIZ5Zo6UNcvK8E8Mz8=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
-github.com/gobuffalo/gogen v0.1.1 h1:dLg+zb+uOyd/mKeQUYIbwbNmfRsr9hd/WtYWepmayhI=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
-github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
-github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
-github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
-github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZCj6A=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
-github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/golang/dep v0.5.4 h1:WfV5qbGwsBNUDhk+pfI6emWm7SdDFsnSWkqCMNG3BRs=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
-github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
-github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
+github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3bOQ/tM=
+github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979 h1:UsXWMy9j+GSCN/I1/Oyc4wGaeW2CDYqeqAkEvWPu+cs=
github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
-github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ=
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94=
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -390,7 +387,6 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
-github.com/jmank88/nuts v0.4.0 h1:3rHp+7YcvtkTPohGBA++MwneB9OlX/rpORvleiRivMQ=
github.com/jmank88/nuts v0.4.0/go.mod h1:TKOSbm0p73pdAzgQ7lcZheG2cinZiXqy60KM5ooL3j8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -398,76 +394,61 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
-github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
-github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180128142709-bca911dae073/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f h1:a3Vd00a20dTKLpyS2hdUafNG5zxQdTw5KhDMK5C0a8U=
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f/go.mod h1:+7K7MqWi5xWI+s1LyB2g0Di71jZo27y+XOlmhNtV1Y0=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2 h1:McU3wXjBrKfJcOt2Pali5qEir9NLrqOh4EECzdWHknM=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2/go.mod h1:3mJ64RiWU2x9U6IigvcoVLra6LZQTOwMuHpk02OtOJc=
-github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
-github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
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/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
-github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
+github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
-github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -476,123 +457,97 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9rNM=
github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI=
-github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd h1:b2wg8HW/u55DT7Y/vamdEn/jdvtsGkxzl+0+iHa5YmE=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.3.0 h1:yPHEatyQC4jN3vdfvqJXG7O9vfC6LhaAV1NEdYpP+h0=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc h1:JI2yIEkVFpe4eYIM/fTNtlIayTiGj4m+iku5JLx8uOY=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc/go.mod h1:3wwz3xi60q88WM0kKZeOJvdQ4YgW4Og7whEiodseWs8=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
-github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
-github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
+github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
+github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
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/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wkb8NefNCTRDEodfo6MtfH9BaO8ncMA=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429/go.mod h1:fK0DIsn9VGLYVur3nQ54Yz4LSLLCyDil0gzq5Y8Yzls=
-github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62Wd/ZOea4XYqE+y1sf2pdm+VItsc0c=
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.3.0 h1:Ysnmjh1Di8EaWaBv40CYR4IdaIsBc5996Gh1oZzCBKk=
-github.com/spf13/afero v1.3.0/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
+github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
+github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@@ -606,24 +561,24 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
+github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8 h1:xp/21gmSPTeWIkalsgXw2njIh3zZyrRRcuCgQfOPLLU=
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8/go.mod h1:K3DbqPpP2WE/9MWokWWzgFZcbgtMb9Wd5CYk9AAbEN8=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v0.0.0-20180129160544-d2b24cf3d3b4/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -632,44 +587,47 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
-github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
-github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862 h1:eg5xqGZGatsyRpVnFJkdeUWSFk46lDgkXLvOryv5ySg=
github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA=
github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ=
github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
-github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
-github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
-go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -689,28 +647,41 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
+golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -728,20 +699,49 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -750,8 +750,11 @@ golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -772,22 +775,52 @@ golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -809,6 +842,7 @@ golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -819,26 +853,78 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200424195722-358506031216 h1:tP48fLPdUK6KA51XXU9OBvvjWPzKOwEldOl7Ab4Z+8U=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200424195722-358506031216/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -847,39 +933,88 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
-gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
+gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -887,12 +1022,16 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index e7f2d979b..c78593f62 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -81,10 +81,10 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
ProjectID: dbSignature.SignatureProjectID,
Created: dbSignature.DateCreated,
Modified: dbSignature.DateModified,
- EmailApprovalList: dbSignature.EmailApprovalList,
- DomainApprovalList: dbSignature.EmailDomainApprovalList,
- GithubUsernameApprovalList: dbSignature.GitHubUsernameApprovalList,
- GithubOrgApprovalList: dbSignature.GitHubOrgApprovalList,
+ EmailApprovalList: utils.GetNilSliceIfEmpty(dbSignature.EmailApprovalList),
+ DomainApprovalList: utils.GetNilSliceIfEmpty(dbSignature.EmailDomainApprovalList),
+ GithubUsernameApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitHubUsernameApprovalList),
+ GithubOrgApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitHubOrgApprovalList),
UserName: dbSignature.UserName,
UserLFID: dbSignature.UserLFUsername,
UserGHID: dbSignature.UserGithubUsername,
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index 9bf4e84c5..fd293811c 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -26,7 +26,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
- githubpkg "github.com/google/go-github/v33/github"
+ githubpkg "github.com/google/go-github/v37/github"
"golang.org/x/oauth2"
)
diff --git a/cla-backend-go/tests/utils_conversion_test.go b/cla-backend-go/tests/utils_conversion_test.go
new file mode 100644
index 000000000..e18cd79a3
--- /dev/null
+++ b/cla-backend-go/tests/utils_conversion_test.go
@@ -0,0 +1,28 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+// TestValidCompanyName is a test for the GetNilSliceIfEmpty
+func TestGetNilSliceIfEmptyWithData(t *testing.T) {
+ slice := []string{"dog", "cat"}
+ assert.Equal(t, []string{"dog", "cat"}, utils.GetNilSliceIfEmpty(slice), "GetNilSliceIfEmpty - With Data")
+}
+
+// TestGetNilSliceIfEmptyWithEmptySlice is a test for the GetNilSliceIfEmpty
+func TestGetNilSliceIfEmptyWithEmptySlice(t *testing.T) {
+ var slice []string
+ assert.Nil(t, utils.GetNilSliceIfEmpty(slice), "GetNilSliceIfEmpty - Empty Slice")
+}
+
+// TestGetNilSliceIfEmptyWithNil is a test for the GetNilSliceIfEmpty
+func TestGetNilSliceIfEmptyWithNil(t *testing.T) {
+ assert.Nil(t, utils.GetNilSliceIfEmpty(nil), "GetNilSliceIfEmpty - Nil")
+}
diff --git a/cla-backend-go/utils/conversion.go b/cla-backend-go/utils/conversion.go
index e51c740c1..973251cc1 100644
--- a/cla-backend-go/utils/conversion.go
+++ b/cla-backend-go/utils/conversion.go
@@ -41,3 +41,12 @@ func BoolValue(input *bool) bool {
func Bool(input bool) *bool {
return &input
}
+
+// GetNilSliceIfEmpty returns a nil reference is the specified slice is empty, otherwise returns a reference to the original slice
+func GetNilSliceIfEmpty(slice []string) []string {
+ if len(slice) == 0 {
+ return nil
+ }
+
+ return slice
+}
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index 6eb4a1cf4..1db0a8b03 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -20,7 +20,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/go-openapi/swag"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
"github.com/sirupsen/logrus"
)
diff --git a/cla-backend-go/v2/github_activity/handlers.go b/cla-backend-go/v2/github_activity/handlers.go
index 92027822b..e9f86fdb1 100644
--- a/cla-backend-go/v2/github_activity/handlers.go
+++ b/cla-backend-go/v2/github_activity/handlers.go
@@ -11,7 +11,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/google/go-github/v33/github" // with go modules enabled (GO111MODULE=on or outside GOPATH)0:w
+ "github.com/google/go-github/v37/github" // with go modules enabled (GO111MODULE=on or outside GOPATH)0:w
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index 3c79a5fa5..5214cca3a 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -26,7 +26,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/repositories"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
)
// Service is responsible for handling the github activity events
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index b667ee0eb..aa40e8352 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -13,7 +13,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
"github.com/communitybridge/easycla/cla-backend-go/repositories/mock"
"github.com/golang/mock/gomock"
- "github.com/google/go-github/v33/github"
+ "github.com/google/go-github/v37/github"
"github.com/stretchr/testify/assert"
)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 667726689..4495b961e 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -7,9 +7,10 @@ import (
"context"
"errors"
"fmt"
- "github.com/gofrs/uuid"
"strings"
+ "github.com/gofrs/uuid"
+
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 7ef3cf11a..740617ef4 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -6,12 +6,13 @@ package gitlab_organizations
import (
"context"
"fmt"
- "github.com/communitybridge/easycla/cla-backend-go/config"
- "github.com/go-openapi/strfmt"
"net/url"
"sort"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+ "github.com/go-openapi/strfmt"
+
v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
From 7c28b248857548018d6168d8fbacffbd2642f14d Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 28 Jul 2021 20:41:59 +0300
Subject: [PATCH 0372/1276] adding update and delete operations to gitlab
organizations (#3080)
Signed-off-by: makkalot
---
cla-backend-go/go.sum | 181 ++++++++++++++++++
cla-backend-go/swagger/cla.v2.yaml | 79 ++++++++
.../common/update-github-organization.yaml | 2 +-
.../v2/gitlab_organizations/handlers.go | 86 +++++++++
.../v2/gitlab_organizations/repository.go | 151 ++++++++++++++-
.../v2/gitlab_organizations/service.go | 52 ++++-
6 files changed, 541 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 2375188ca..23f30f8c3 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -17,28 +17,38 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM=
github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUamMO8SWS1m4ZKJGGeh9lT985U=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
@@ -46,20 +56,29 @@ github.com/LF-Engineering/lfx-kit v0.1.25 h1:Bb3Snc72ppBmbS5CMoLBGFg1Tt7ZhZktZLJ
github.com/LF-Engineering/lfx-kit v0.1.25/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
github.com/LF-Engineering/lfx-models v0.6.44 h1:a4/6+Hc05caUCzd9eQnZIioZUhWxtgpfgVRuf/M2SRY=
github.com/LF-Engineering/lfx-models v0.6.44/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -74,42 +93,63 @@ github.com/aws/aws-sdk-go v1.36.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/bketelsen/crypt v0.0.4 h1:w/jqZtC9YD4DS/Vp9GhWfWcCpuAL58oTnLoI8vE9YHU=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.1.0/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -119,14 +159,18 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
github.com/fnproject/fdk-go v0.0.2/go.mod h1:9m+nEyku9SqJAVJQsfZOZBQzFkCs+jvmbZJhvgDX4ts=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -135,13 +179,19 @@ github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e/go.mod h1:7cKuhb5qV2
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a h1:l4yNPeA/3kNJwE0uDBVXtFX8hfiHrlqkXBLPOrchWzk=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -222,41 +272,58 @@ github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3 h1:3GQ53z7E3o00C/yy7Ko8VXqQXoJGLkrTQCLTF1EjoXU=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1 h1:iQ0D6SpNXIxu52WESsD+KoQ7af2e3nCfnSBoSF/hKe0=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211 h1:mSVZ4vj4khv+oThUfS+SQU3UuFIZ5Zo6UNcvK8E8Mz8=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1 h1:dLg+zb+uOyd/mKeQUYIbwbNmfRsr9hd/WtYWepmayhI=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZCj6A=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/dep v0.5.4 h1:WfV5qbGwsBNUDhk+pfI6emWm7SdDFsnSWkqCMNG3BRs=
github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -286,8 +353,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -308,9 +377,12 @@ github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3
github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -322,8 +394,11 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -332,45 +407,68 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979 h1:UsXWMy9j+GSCN/I1/Oyc4wGaeW2CDYqeqAkEvWPu+cs=
github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ=
@@ -378,6 +476,7 @@ github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94=
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
@@ -387,6 +486,7 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
+github.com/jmank88/nuts v0.4.0 h1:3rHp+7YcvtkTPohGBA++MwneB9OlX/rpORvleiRivMQ=
github.com/jmank88/nuts v0.4.0/go.mod h1:TKOSbm0p73pdAzgQ7lcZheG2cinZiXqy60KM5ooL3j8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -394,7 +494,9 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180128142709-bca911dae073/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -402,6 +504,7 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -409,28 +512,40 @@ github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f h1:a3Vd00a20dTKLpyS2h
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f/go.mod h1:+7K7MqWi5xWI+s1LyB2g0Di71jZo27y+XOlmhNtV1Y0=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2 h1:McU3wXjBrKfJcOt2Pali5qEir9NLrqOh4EECzdWHknM=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2/go.mod h1:3mJ64RiWU2x9U6IigvcoVLra6LZQTOwMuHpk02OtOJc=
+github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
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/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
+github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
@@ -446,9 +561,12 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -457,13 +575,20 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -476,19 +601,25 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9rNM=
github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI=
+github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd h1:b2wg8HW/u55DT7Y/vamdEn/jdvtsGkxzl+0+iHa5YmE=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.3.0 h1:yPHEatyQC4jN3vdfvqJXG7O9vfC6LhaAV1NEdYpP+h0=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc h1:JI2yIEkVFpe4eYIM/fTNtlIayTiGj4m+iku5JLx8uOY=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc/go.mod h1:3wwz3xi60q88WM0kKZeOJvdQ4YgW4Og7whEiodseWs8=
@@ -501,37 +632,51 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
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/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wkb8NefNCTRDEodfo6MtfH9BaO8ncMA=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429/go.mod h1:fK0DIsn9VGLYVur3nQ54Yz4LSLLCyDil0gzq5Y8Yzls=
+github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62Wd/ZOea4XYqE+y1sf2pdm+VItsc0c=
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -543,7 +688,9 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
@@ -565,6 +712,7 @@ github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -579,6 +727,7 @@ github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8 h1:xp/21gmSP
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8/go.mod h1:K3DbqPpP2WE/9MWokWWzgFZcbgtMb9Wd5CYk9AAbEN8=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v0.0.0-20180129160544-d2b24cf3d3b4/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -587,25 +736,37 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
+github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862 h1:eg5xqGZGatsyRpVnFJkdeUWSFk46lDgkXLvOryv5ySg=
github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA=
github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ=
github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@@ -618,15 +779,18 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -652,6 +816,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -670,8 +835,10 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -681,6 +848,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -813,6 +981,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -888,6 +1057,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -915,6 +1085,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -965,6 +1136,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -985,6 +1157,7 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1000,16 +1173,20 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1031,7 +1208,11 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
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/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 27131ee98..ac0cafbae 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1679,6 +1679,82 @@ paths:
tags:
- gitlab-organizations
+ /project/{projectSFID}/gitlab/organizations/{orgName}/config:
+ put:
+ summary: Update Gitlab Organization Configuration
+ description: Endpoint to adjust the Gitlab Organization Configuration, such as toggling the auto-enable flag
+ operationId: updateProjectGitlabOrganizationConfig
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ - name: orgName
+ in: path
+ type: string
+ required: true
+ - in: body
+ name: body
+ schema:
+ $ref: '#/definitions/update-gitlab-organization'
+ required: true
+ responses:
+ '200':
+ description: 'Resource Updated'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ tags:
+ - gitlab-organizations
+
+ delete:
+ summary: Delete Gitlab organization in the project
+ description: Endpoint to delete the Gitlab organization for the project
+ operationId: deleteProjectGitlabOrganization
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ - name: orgName
+ in: path
+ type: string
+ required: true
+ responses:
+ '204':
+ description: 'Deleted'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ tags:
+ - gitlab-organizations
+
/cla-group/{claGroupID}/icla/signatures:
get:
summary: List individual signatures for CLA Group
@@ -4237,6 +4313,9 @@ definitions:
create-gitlab-organization:
$ref: './common/create-github-organization.yaml'
+ update-gitlab-organization:
+ $ref: './common/update-github-organization.yaml'
+
user:
$ref: './common/user.yaml'
diff --git a/cla-backend-go/swagger/common/update-github-organization.yaml b/cla-backend-go/swagger/common/update-github-organization.yaml
index 82ccc4339..440297615 100644
--- a/cla-backend-go/swagger/common/update-github-organization.yaml
+++ b/cla-backend-go/swagger/common/update-github-organization.yaml
@@ -13,5 +13,5 @@ properties:
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
branchProtectionEnabled:
type: boolean
- description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ description: Flag to indicate if this Organization is configured to automatically setup branch protection on CLA enabled repositories.
x-omitempty: true
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index ef77190ba..63f9684a8 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -5,9 +5,12 @@ package gitlab_organizations
import (
"context"
+ "errors"
"fmt"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
@@ -136,6 +139,89 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
return gitlab_organizations.NewAddProjectGitlabOrganizationOK().WithPayload(result)
})
+ api.GitlabOrganizationsUpdateProjectGitlabOrganizationConfigHandler = gitlab_organizations.UpdateProjectGitlabOrganizationConfigHandlerFunc(func(params gitlab_organizations.UpdateProjectGitlabOrganizationConfigParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ if params.Body.AutoEnabled == nil {
+ return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
+ Code: "400",
+ Message: "EasyCLA - 400 Bad Request - missing auto enable value in body",
+ })
+ }
+
+ if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
+ Code: "400",
+ Message: "EasyCLA - 400 Bad Request - AutoEnabledClaGroupID can't be empty when AutoEnabled",
+ })
+ }
+
+ err := service.UpdateGitlabOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ if err != nil {
+ if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
+ return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
+ }
+ return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, "updating gitlab org", err))
+ }
+
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.GitlabOrganizationUpdated,
+ ProjectSFID: params.ProjectSFID,
+ LfUsername: authUser.UserName,
+ UserName: authUser.UserName,
+ EventData: &events.GitlabOrganizationUpdatedEventData{
+ GitlabOrganizationName: params.OrgName,
+ AutoEnabled: *params.Body.AutoEnabled,
+ },
+ })
+
+ return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigOK()
+ })
+
+ api.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler = gitlab_organizations.DeleteProjectGitlabOrganizationHandlerFunc(func(params gitlab_organizations.DeleteProjectGitlabOrganizationParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "github_organization.handlers.GithubOrganizationsDeleteProjectGithubOrganizationHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "orgName": params.OrgName,
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("authUser %s does not have access to Delete Project GitHub Organizations with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewDeleteProjectGitlabOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ err := service.DeleteGitlabOrganization(ctx, params.ProjectSFID, params.OrgName)
+ if err != nil {
+ if strings.Contains(err.Error(), "getProjectNotFound") {
+ msg := fmt.Sprintf("project not found with given SFID: %s", params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewDeleteProjectGitlabOrganizationNotFound().WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ }
+ msg := fmt.Sprintf("problem deleting Gitlab Organization with project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewDeleteProjectGitlabOrganizationBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ LfUsername: authUser.UserName,
+ EventType: events.GitHubOrganizationDeleted,
+ ProjectSFID: params.ProjectSFID,
+ EventData: &events.GitlabOrganizationDeletedEventData{
+ GitlabOrganizationName: params.OrgName,
+ },
+ })
+
+ return gitlab_organizations.NewDeleteProjectGitlabOrganizationNoContent()
+ })
+
api.GitlabActivityGitlabOauthCallbackHandler = gitlab_activity.GitlabOauthCallbackHandlerFunc(func(params gitlab_activity.GitlabOauthCallbackParams) middleware.Responder {
f := logrus.Fields{
"functionName": "gitlab_organization.handlers.GitlabActivityGitlabOauthCallbackHandler",
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 4495b961e..2ba7b52ec 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -36,6 +36,8 @@ type RepositoryInterface interface {
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error
+ UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
+ DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
// Repository object/struct
@@ -98,7 +100,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
return nil, err
}
- enabled := false
+ enabled := true
gitlabOrg := &GitlabOrganization{
OrganizationID: organizationID.String(),
DateCreated: currentTime,
@@ -154,8 +156,8 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
condition := expression.Key("organization_sfid").Equal(expression.Value(projectSFID))
builder := expression.NewBuilder().WithKeyCondition(condition)
- //filter := expression.Name("enabled").Equal(expression.Value(true))
- //builder = builder.WithFilter(filter)
+ filter := expression.Name("enabled").Equal(expression.Value(true))
+ builder = builder.WithFilter(filter)
// Use the nice builder to create the expression
expr, err := builder.Build()
@@ -207,6 +209,8 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOr
"githubOrganizationName": githubOrganizationName,
}
+ githubOrganizationName = strings.ToLower(githubOrganizationName)
+
condition := expression.Key("organization_name_lower").Equal(expression.Value(strings.ToLower(githubOrganizationName)))
builder := expression.NewBuilder().WithKeyCondition(condition)
// Use the nice builder to create the expression
@@ -339,6 +343,147 @@ func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabO
return nil
}
+func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
+ f := logrus.Fields{
+ "functionName": "gitlab_organizations.repository.UpdateGitlabOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "organizationName": organizationName,
+ "autoEnabled": autoEnabled,
+ "autoEnabledClaGroupID": autoEnabledClaGroupID,
+ "branchProtectionEnabled": branchProtectionEnabled,
+ "tableName": repo.gitlabOrgTableName,
+ }
+
+ _, currentTime := utils.CurrentTime()
+ gitlabOrgs, lookupErr := repo.GetGitlabOrganizationByName(ctx, organizationName)
+ if lookupErr != nil {
+ log.WithFields(f).Warnf("error looking up Gitlab organization by name, error: %+v", lookupErr)
+ return lookupErr
+ }
+ if gitlabOrgs == nil || len(gitlabOrgs.List) == 0 {
+ lookupErr := errors.New("unable to lookup Gitlab organization by name")
+ log.WithFields(f).Warnf("error looking up Gitlab organization, error: %+v", lookupErr)
+ return lookupErr
+ }
+
+ gitlabOrg := gitlabOrgs.List[0]
+
+ expressionAttributeNames := map[string]*string{
+ "#A": aws.String("auto_enabled"),
+ "#C": aws.String("auto_enabled_cla_group_id"),
+ "#B": aws.String("branch_protection_enabled"),
+ "#M": aws.String("date_modified"),
+ }
+ expressionAttributeValues := map[string]*dynamodb.AttributeValue{
+ ":a": {
+ BOOL: aws.Bool(autoEnabled),
+ },
+ ":c": {
+ S: aws.String(autoEnabledClaGroupID),
+ },
+ ":b": {
+ BOOL: aws.Bool(branchProtectionEnabled),
+ },
+ ":m": {
+ S: aws.String(currentTime),
+ },
+ }
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m"
+
+ if enabled != nil {
+ expressionAttributeNames["#E"] = aws.String("enabled")
+ expressionAttributeValues[":e"] = &dynamodb.AttributeValue{
+ BOOL: aws.Bool(*enabled),
+ }
+ updateExpression = updateExpression + ", #E = :e "
+ }
+
+ input := &dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "organization_id": {
+ S: aws.String(gitlabOrg.OrganizationID),
+ },
+ },
+ ExpressionAttributeNames: expressionAttributeNames,
+ ExpressionAttributeValues: expressionAttributeValues,
+ UpdateExpression: &updateExpression,
+ TableName: aws.String(repo.gitlabOrgTableName),
+ }
+
+ log.WithFields(f).Debugf("updating gitlab organization record: %+v", input)
+ _, updateErr := repo.dynamoDBClient.UpdateItem(input)
+ if updateErr != nil {
+ log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v", updateErr)
+ return updateErr
+ }
+
+ return nil
+}
+
+func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
+ f := logrus.Fields{
+ "functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "githubOrgName": gitlabOrgName,
+ }
+
+ var gitlabOrganizationID string
+ orgs, orgErr := repo.GetGitlabOrganizations(ctx, projectSFID)
+ if orgErr != nil {
+ errMsg := fmt.Sprintf("gitlab organization is not found using projectSFID: %s, error: %+v", projectSFID, orgErr)
+ log.WithFields(f).Warn(errMsg)
+ return errors.New(errMsg)
+ }
+
+ for _, githubOrg := range orgs.List {
+ if strings.EqualFold(githubOrg.OrganizationName, gitlabOrgName) {
+ gitlabOrganizationID = githubOrg.OrganizationID
+ break
+ }
+ }
+
+ log.WithFields(f).Debug("Deleting GitHub organization...")
+ // Update enabled flag as false
+ _, currentTime := utils.CurrentTime()
+ note := fmt.Sprintf("Enabled set to false due to org deletion at %s ", currentTime)
+ _, err := repo.dynamoDBClient.UpdateItem(
+ &dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "organization_id": {
+ S: aws.String(gitlabOrganizationID),
+ },
+ },
+ ExpressionAttributeNames: map[string]*string{
+ "#E": aws.String("enabled"),
+ "#N": aws.String("note"),
+ "#D": aws.String("date_modified"),
+ },
+ ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
+ ":e": {
+ BOOL: aws.Bool(false),
+ },
+ ":n": {
+ S: aws.String(note),
+ },
+ ":d": {
+ S: aws.String(currentTime),
+ },
+ },
+ UpdateExpression: aws.String("SET #E = :e, #N = :n, #D = :d"),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ },
+ )
+ if err != nil {
+ errMsg := fmt.Sprintf("error deleting gitlab organization: %s - %+v", gitlabOrgName, err)
+ log.WithFields(f).Warnf(errMsg)
+ return errors.New(errMsg)
+ }
+
+ return nil
+}
+
func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*GitlabOrganization) []*models2.GitlabOrganization {
f := logrus.Fields{
"functionName": "buildGitlabOrganizationListModels",
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 740617ef4..0f23e7beb 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -29,19 +29,21 @@ type Service interface {
AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.CreateGitlabOrganization) (*models.GitlabOrganization, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
+ UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab.OauthSuccessResponse) error
+ DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
}
type service struct {
- repo RepositoryInterface
- projectsCLAGroupService projects_cla_groups.Repository
+ repo RepositoryInterface
+ claGroupRepository projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo RepositoryInterface, projectsCLAGroupService projects_cla_groups.Repository) Service {
+func NewService(repo RepositoryInterface, claGroupRepository projects_cla_groups.Repository) Service {
return service{
- repo: repo,
- projectsCLAGroupService: projectsCLAGroupService,
+ repo: repo,
+ claGroupRepository: claGroupRepository,
}
}
@@ -78,6 +80,17 @@ func (s service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganiz
}
+func (s service) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+ // check if valid cla group id is passed
+ if autoEnabledClaGroupID != "" {
+ if _, err := s.claGroupRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
+ return err
+ }
+ }
+
+ return s.repo.UpdateGitlabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
+}
+
func (s service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganization",
@@ -187,7 +200,7 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
autoEnabledCLAGroupName := ""
if org.AutoEnabledClaGroupID != "" {
log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitHub auth enabled CLA Group response", org.AutoEnabledClaGroupID)
- claGroupMode, claGroupLookupErr := s.projectsCLAGroupService.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
+ claGroupMode, claGroupLookupErr := s.claGroupRepository.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
if claGroupLookupErr != nil {
log.WithFields(f).WithError(claGroupLookupErr).Warnf("Unable to lookup CLA Group by ID: %s", org.AutoEnabledClaGroupID)
}
@@ -248,6 +261,33 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
return out, nil
}
+func (s service) DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error {
+ f := logrus.Fields{
+ "functionName": "DeleteGitHubOrganization",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "gitlabOrgName": gitlabOrgName,
+ }
+
+ // Lookup the parent
+ parentProjectSFID, projErr := v2ProjectService.GetClient().GetParentProject(projectSFID)
+ if projErr != nil {
+ log.WithFields(f).Warnf("problem fetching project parent SFID, error: %+v", projErr)
+ return projErr
+ }
+
+ log.WithFields(f).Debugf("retrieved parent of project sfid : %s -> %s", projectSFID, parentProjectSFID)
+
+ // Todo: Enable this when the repositories are implemented
+ //err := s.ghRepository.DisableRepositoriesOfGithubOrganization(ctx, parentProjectSFID, gitlabOrgName)
+ //if err != nil {
+ // log.WithFields(f).Warnf("problem disabling repositories for github organizations, error: %+v", projErr)
+ // return err
+ //}
+
+ return s.repo.DeleteGitlabOrganization(ctx, projectSFID, gitlabOrgName)
+}
+
func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {
base := "https://gitlab.com/oauth/authorize"
c := config.GetConfig()
From 6f893baa9b8132f4f8b4d4c2c8e66c991ec14fd1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 28 Jul 2021 18:23:28 -0700
Subject: [PATCH 0373/1276] Added Gitlab Approval List Support (#3081)
---
cla-backend-go/approval_list/service.go | 4 +-
cla-backend-go/cla_manager/handlers.go | 12 +-
cla-backend-go/cla_manager/service.go | 22 +-
cla-backend-go/cmd/server.go | 4 +-
cla-backend-go/company/service.go | 8 +-
cla-backend-go/events/event_data.go | 184 ++++++++++++
cla-backend-go/project/service.go | 2 +-
cla-backend-go/serverless.yml | 4 +-
cla-backend-go/signatures/constants.go | 28 ++
cla-backend-go/signatures/converters.go | 7 +-
cla-backend-go/signatures/dbmodels.go | 7 +-
cla-backend-go/signatures/models.go | 10 +-
cla-backend-go/signatures/projections.go | 15 +-
cla-backend-go/signatures/repository.go | 219 +++++++++++---
cla-backend-go/signatures/service.go | 124 +++++++-
.../swagger/common/icla-signature.yaml | 4 +
.../common/signature-approval-list.yaml | 41 ++-
cla-backend-go/swagger/common/signature.yaml | 22 +-
cla-backend-go/swagger/common/user.yaml | 26 +-
cla-backend-go/tests/utils_test.go | 189 ------------
cla-backend-go/tests/utils_validators_test.go | 272 ++++++++++++++++++
cla-backend-go/userSubscribeLambda/main.go | 6 +-
cla-backend-go/users/handlers.go | 4 +-
cla-backend-go/users/models.go | 4 +-
cla-backend-go/users/repository.go | 194 ++++++++++++-
cla-backend-go/users/service.go | 31 +-
cla-backend-go/utils/cla_user.go | 2 +-
cla-backend-go/utils/constants.go | 14 +-
cla-backend-go/utils/utils.go | 129 ---------
cla-backend-go/utils/validators.go | 171 +++++++++++
cla-backend-go/v2/cla_manager/service.go | 2 +-
cla-backend-go/v2/company/service.go | 2 +-
.../v2/dynamo_events/cla_manager.go | 2 +-
cla-backend-go/v2/signatures/handlers.go | 12 +-
cla-backend-go/v2/signatures/validators.go | 40 ++-
cla-backend-go/v2/user-service/client.go | 2 +-
cla-backend/serverless.yml | 4 +-
37 files changed, 1389 insertions(+), 434 deletions(-)
create mode 100644 cla-backend-go/signatures/constants.go
create mode 100644 cla-backend-go/tests/utils_validators_test.go
create mode 100644 cla-backend-go/utils/validators.go
diff --git a/cla-backend-go/approval_list/service.go b/cla-backend-go/approval_list/service.go
index 21ee64305..77d9e9353 100644
--- a/cla-backend-go/approval_list/service.go
+++ b/cla-backend-go/approval_list/service.go
@@ -336,7 +336,7 @@ func (s service) sendRequestSentEmail(companyModel *models.Company, claGroupMode
// Need to determine which email...
var whichEmail = ""
if manager.LfEmail != "" {
- whichEmail = manager.LfEmail
+ whichEmail = manager.LfEmail.String()
}
// If no LF Email try to grab the first other email in their email list
@@ -391,7 +391,7 @@ func (s service) sendRequestRejectedEmailToRecipient(emailParams emails.CommonEm
// Need to determine which email...
var whichEmail = ""
if manager.LfEmail != "" {
- whichEmail = manager.LfEmail
+ whichEmail = manager.LfEmail.String()
}
// If no LF Email try to grab the first other email in their email list
diff --git a/cla-backend-go/cla_manager/handlers.go b/cla-backend-go/cla_manager/handlers.go
index 8bea2b3ce..68c8da253 100644
--- a/cla-backend-go/cla_manager/handlers.go
+++ b/cla-backend-go/cla_manager/handlers.go
@@ -7,6 +7,8 @@ import (
"context"
"fmt"
+ "github.com/go-openapi/strfmt"
+
"github.com/LF-Engineering/lfx-kit/auth"
user_service "github.com/communitybridge/easycla/cla-backend-go/v2/user-service"
@@ -192,7 +194,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
sendRequestAccessEmailToCLAManagers(emailSvc, emails.RequestAccessToCLAManagersTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: manager.Username,
- RecipientAddress: manager.LfEmail,
+ RecipientAddress: manager.LfEmail.String(),
CompanyName: companyModel.CompanyName,
},
RequesterName: params.Body.UserName,
@@ -368,7 +370,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
sendRequestApprovedEmailToCLAManagers(emailSvc, emails.RequestApprovedToCLAManagersTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: manager.Username,
- RecipientAddress: manager.LfEmail,
+ RecipientAddress: manager.LfEmail.String(),
CompanyName: companyModel.CompanyName,
},
RequesterName: request.UserName,
@@ -483,7 +485,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
sendRequestDeniedEmailToCLAManagers(emailSvc, emails.RequestDeniedToCLAManagersTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: manager.Username,
- RecipientAddress: manager.LfEmail,
+ RecipientAddress: manager.LfEmail.String(),
CompanyName: companyModel.CompanyName,
},
RequesterName: request.UserName,
@@ -654,7 +656,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
DateModified: nowStr,
Emails: userServiceClient.EmailsToSlice(sfdcUserObject),
GithubUsername: sfdcUserObject.GithubID, //this is the github username
- LfEmail: userServiceClient.GetPrimaryEmail(sfdcUserObject),
+ LfEmail: strfmt.Email(userServiceClient.GetPrimaryEmail(sfdcUserObject)),
LfUsername: sfdcUserObject.Username,
Note: "created from SF record",
UserExternalID: sfdcUserObject.ID,
@@ -759,7 +761,7 @@ func Configure(api *operations.ClaAPI, service IService, companyService company.
DateModified: nowStr,
Emails: userServiceClient.EmailsToSlice(sfdcUserObject),
GithubUsername: sfdcUserObject.GithubID, //this is the github username
- LfEmail: userServiceClient.GetPrimaryEmail(sfdcUserObject),
+ LfEmail: strfmt.Email(userServiceClient.GetPrimaryEmail(sfdcUserObject)),
LfUsername: sfdcUserObject.Username,
Note: "created from SF record",
UserExternalID: sfdcUserObject.ID,
diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go
index 07b18574e..e76e1bc5a 100644
--- a/cla-backend-go/cla_manager/service.go
+++ b/cla-backend-go/cla_manager/service.go
@@ -191,7 +191,7 @@ func (s service) DeleteRequest(requestID string) error {
return nil
}
-// AddClaManager Adds LFID to Signature Access Control List list
+// AddClaManager Adds LFID to Signature Access Control list
func (s service) AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) {
f := logrus.Fields{
@@ -249,17 +249,17 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company
sendClaManagerAddedEmailToCLAManagers(s.emailTemplateService, emails.ClaManagerAddedToCLAManagersTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: manager.Username,
- RecipientAddress: manager.LfEmail,
+ RecipientAddress: manager.LfEmail.String(),
CompanyName: companyModel.CompanyName,
},
Name: userModel.Username,
- Email: userModel.LfEmail,
+ Email: userModel.LfEmail.String(),
}, claGroupModel)
}
// Notify the added user
sendClaManagerAddedEmailToUser(s.emailTemplateService, emails.CommonEmailParams{
RecipientName: userModel.Username,
- RecipientAddress: userModel.LfEmail,
+ RecipientAddress: userModel.LfEmail.String(),
CompanyName: companyModel.CompanyName,
}, claGroupModel)
@@ -279,7 +279,7 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company
CompanyName: companyModel.CompanyName,
ProjectName: claGroupModel.ProjectName,
UserName: userModel.Username,
- UserEmail: userModel.LfEmail,
+ UserEmail: userModel.LfEmail.String(),
UserLFID: userModel.LfUsername,
},
})
@@ -368,18 +368,18 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp
sendClaManagerDeleteEmailToCLAManagers(s.emailTemplateService, emails.ClaManagerDeletedToCLAManagersTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: manager.Username,
- RecipientAddress: manager.LfEmail,
+ RecipientAddress: manager.LfEmail.String(),
CompanyName: companyModel.CompanyName,
},
Name: userModel.LfUsername,
- Email: userModel.LfEmail,
+ Email: userModel.LfEmail.String(),
}, claGroupModel)
}
// Notify the removed manager
sendRemovedClaManagerEmailToRecipient(s.emailTemplateService, emails.CommonEmailParams{
RecipientName: userModel.LfUsername,
- RecipientAddress: userModel.LfEmail,
+ RecipientAddress: userModel.LfEmail.String(),
CompanyName: companyModel.CompanyName,
}, claGroupModel, claManagers)
@@ -399,7 +399,7 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp
CompanyName: companyModel.CompanyName,
ProjectName: claGroupModel.ProjectName,
UserName: userModel.LfUsername,
- UserEmail: userModel.LfEmail,
+ UserEmail: userModel.LfEmail.String(),
UserLFID: LFID,
},
})
@@ -452,7 +452,7 @@ func sendClaManagerAddedEmailToCLAManagers(emailSvc emails.EmailTemplateService,
func sendRemovedClaManagerEmailToRecipient(emailSvc emails.EmailTemplateService, emailParams emails.CommonEmailParams, claGroupModel *models.ClaGroup, claManagers []models.User) {
projectName := claGroupModel.ProjectName
- emailCLAManagerParams := []emails.ClaManagerInfoParams{}
+ var emailCLAManagerParams []emails.ClaManagerInfoParams
log.Debugf("CLA Managers found: %+v", claManagers)
@@ -462,7 +462,7 @@ func sendRemovedClaManagerEmailToRecipient(emailSvc emails.EmailTemplateService,
// Need to determine which email...
var whichEmail = ""
if companyAdmin.LfEmail != "" {
- whichEmail = companyAdmin.LfEmail
+ whichEmail = companyAdmin.LfEmail.String()
log.Debugf("Found email : %s ", whichEmail)
}
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 148d0d181..d143fb378 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -14,6 +14,8 @@ import (
"strconv"
"strings"
+ "github.com/go-openapi/strfmt"
+
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
@@ -651,7 +653,7 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
// Attempt to create the user
newUser := &models.User{
- LfEmail: claUser.LFEmail,
+ LfEmail: strfmt.Email(claUser.LFEmail),
LfUsername: claUser.LFUsername,
Username: claUser.Name,
}
diff --git a/cla-backend-go/company/service.go b/cla-backend-go/company/service.go
index ab513e0d4..aa3ec938b 100644
--- a/cla-backend-go/company/service.go
+++ b/cla-backend-go/company/service.go
@@ -9,6 +9,8 @@ import (
"sort"
"strings"
+ "github.com/go-openapi/strfmt"
+
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/users"
@@ -617,10 +619,10 @@ func (s service) getPreferredNameAndEmail(ctx context.Context, lfid string) (str
userEmail := userModel.LfEmail
if userEmail == "" && userModel.Emails != nil && len(userModel.Emails) > 0 {
- userEmail = userModel.Emails[0]
+ userEmail = strfmt.Email(userModel.Emails[0])
}
- return userName, userEmail, nil
+ return userName, userEmail.String(), nil
}
func (s service) GetCompanyByExternalID(ctx context.Context, companySFID string) (*models.Company, error) {
@@ -866,7 +868,7 @@ func getCompanyAdmin(ctx context.Context, companySFID string) (*models.User, err
for _, rs := range usc.RoleScopes {
if rs.RoleName == "company-admin" {
companyAdmin := &models.User{
- LfEmail: usc.Contact.EmailAddress,
+ LfEmail: strfmt.Email(usc.Contact.EmailAddress),
LfUsername: usc.Contact.Username,
UserExternalID: usc.Contact.ID,
Username: usc.Contact.Name,
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 47954707b..130fc63fa 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -327,6 +327,26 @@ type CLAApprovalListRemoveGitHubOrgData struct {
ApprovalListGitHubOrg string
}
+// CLAApprovalListAddGitlabUsernameData data model
+type CLAApprovalListAddGitlabUsernameData struct {
+ ApprovalListGitlabUsername string
+}
+
+// CLAApprovalListRemoveGitlabUsernameData data model
+type CLAApprovalListRemoveGitlabUsernameData struct {
+ ApprovalListGitlabUsername string
+}
+
+// CLAApprovalListAddGitlabOrgData data model
+type CLAApprovalListAddGitlabOrgData struct {
+ ApprovalListGitlabOrg string
+}
+
+// CLAApprovalListRemoveGitlabOrgData data model
+type CLAApprovalListRemoveGitlabOrgData struct {
+ ApprovalListGitlabOrg string
+}
+
// ApprovalListGitHubOrganizationAddedEventData data model
type ApprovalListGitHubOrganizationAddedEventData struct {
GitHubOrganizationName string
@@ -988,6 +1008,94 @@ func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventDetailsString(args *LogEve
return data, true
}
+// GetEventDetailsString returns the details string for this event
+func (ed *CLAApprovalListAddGitlabUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab username %s was added to the approval list", ed.ApprovalListGitlabUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString returns the details string for this event
+func (ed *CLAApprovalListRemoveGitlabUsernameData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab username %s was removed from the approval list", ed.ApprovalListGitlabUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString returns the details string for this event
+func (ed *CLAApprovalListAddGitlabOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was added to the approval list", ed.ApprovalListGitlabOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventDetailsString returns the details string for this event
+func (ed *CLAApprovalListRemoveGitlabOrgData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was removed from the approval list", ed.ApprovalListGitlabOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventDetailsString returns the details string for this event
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The CCLA Approval Request was created for the Project: %s, Company: %s with Request ID: %s",
@@ -1964,6 +2072,82 @@ func (ed *CLAApprovalListRemoveGitHubOrgData) GetEventSummaryString(args *LogEve
return data, true
}
+// GetEventSummaryString returns the summary string for this event
+func (ed *CLAApprovalListAddGitlabUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab username %s was added to the approval list", ed.ApprovalListGitlabUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString returns the summary string for this event
+func (ed *CLAApprovalListRemoveGitlabUsernameData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab username %s was removed from the approval list", ed.ApprovalListGitlabUsername)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString returns the summary string for this event
+func (ed *CLAApprovalListAddGitlabOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was added to the approval list", ed.ApprovalListGitlabOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
+// GetEventSummaryString returns the summary string for this event
+func (ed *CLAApprovalListRemoveGitlabOrgData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := fmt.Sprintf("The Gitlab organization %s was removed from the approval list", ed.ApprovalListGitlabOrg)
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if args.ProjectName != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ }
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the CLA Manager %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventSummaryString returns the summary string for this event
func (ed *CCLAApprovalListRequestCreatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The user %s created a CCLA Approval Request", args.UserName)
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index f998eec85..a861aa0ba 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -376,7 +376,7 @@ func (s service) GetCLAManagers(ctx context.Context, claGroupID string) ([]*mode
return nil, err
}
managers = append(managers, &models.ClaManagerUser{
- UserEmail: u.LfEmail,
+ UserEmail: u.LfEmail.String(),
UserLFID: u.LfUsername,
UserName: u.Username,
})
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index 60cf10ac0..efbae4f06 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -141,8 +141,10 @@ provider:
Resource:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-ccla-whitelist-requests/index/company-id-project-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-ccla-whitelist-requests/index/ccla-approval-list-request-project-id-index"
- - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-user-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-username-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/gitlab-id-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/gitlab-username-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-user-external-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/lf-username-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/lf-email-index"
diff --git a/cla-backend-go/signatures/constants.go b/cla-backend-go/signatures/constants.go
new file mode 100644
index 000000000..5e1cd5f42
--- /dev/null
+++ b/cla-backend-go/signatures/constants.go
@@ -0,0 +1,28 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package signatures
+
+// SignatureEmailApprovalListColumn is the name of the signature column for the email approval list
+const SignatureEmailApprovalListColumn = "email_whitelist" // TODO: rename column to email_approval_list
+
+// SignatureDomainApprovalListColumn is the name of the signature column for the domain approval list
+const SignatureDomainApprovalListColumn = "domain_whitelist" // TODO: rename column to domain_approval_list
+
+// SignatureGitHubUsernameApprovalListColumn is the name of the signature column for the GitHub username approval list
+const SignatureGitHubUsernameApprovalListColumn = "github_whitelist" // TODO: rename column to github_username_approval_list
+
+// SignatureGitHubOrgApprovalListColumn is the name of the signature column for the GitHub organization approval list
+const SignatureGitHubOrgApprovalListColumn = "github_org_whitelist" // TODO: rename column to github_org_approval_list
+
+// SignatureGitlabUsernameApprovalListColumn is the name of the signature column for gitlab username approval lists
+const SignatureGitlabUsernameApprovalListColumn = "gitlab_username_approval_list"
+
+// SignatureGitlabOrgApprovalListColumn is the name of the signature column for gitlab organization approval lists
+const SignatureGitlabOrgApprovalListColumn = "gitlab_org_approval_list"
+
+// SignatureUserGitHubUsername is the name of the signature column for user gitlab username
+const SignatureUserGitHubUsername = "user_github_username"
+
+// SignatureUserGitlabUsername is the name of the signature column for user gitlab username
+const SignatureUserGitlabUsername = "user_gitlab_username"
diff --git a/cla-backend-go/signatures/converters.go b/cla-backend-go/signatures/converters.go
index c78593f62..c6cfa05b7 100644
--- a/cla-backend-go/signatures/converters.go
+++ b/cla-backend-go/signatures/converters.go
@@ -85,9 +85,14 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
DomainApprovalList: utils.GetNilSliceIfEmpty(dbSignature.EmailDomainApprovalList),
GithubUsernameApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitHubUsernameApprovalList),
GithubOrgApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitHubOrgApprovalList),
+ GitlabUsernameApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitlabUsernameApprovalList),
+ GitlabOrgApprovalList: utils.GetNilSliceIfEmpty(dbSignature.GitlabOrgApprovalList),
UserName: dbSignature.UserName,
UserLFID: dbSignature.UserLFUsername,
- UserGHID: dbSignature.UserGithubUsername,
+ UserGHID: dbSignature.UserGithubID,
+ UserGHUsername: dbSignature.UserGithubUsername,
+ UserGitlabID: dbSignature.UserGitlabID,
+ UserGitlabUsername: dbSignature.UserGitlabUsername,
SignedOn: signedOn,
SignatoryName: dbSignature.SignatoryName,
UserDocusignName: dbSignature.UserDocusignName,
diff --git a/cla-backend-go/signatures/dbmodels.go b/cla-backend-go/signatures/dbmodels.go
index 00056661d..f9579c703 100644
--- a/cla-backend-go/signatures/dbmodels.go
+++ b/cla-backend-go/signatures/dbmodels.go
@@ -23,10 +23,13 @@ type ItemSignature struct {
EmailDomainApprovalList []string `json:"domain_whitelist"`
GitHubUsernameApprovalList []string `json:"github_whitelist"`
GitHubOrgApprovalList []string `json:"github_org_whitelist"`
- GitLabUsernameApprovalList []string `json:"gitlab_username_approval_list"`
- GitLabGroupApprovalList []string `json:"gitlab_group_approval_list"`
+ GitlabUsernameApprovalList []string `json:"gitlab_username_approval_list"`
+ GitlabOrgApprovalList []string `json:"gitlab_org_approval_list"`
SignatureACL []string `json:"signature_acl"`
+ UserGithubID string `json:"user_github_id"`
UserGithubUsername string `json:"user_github_username"`
+ UserGitlabID string `json:"user_gitlab_id"`
+ UserGitlabUsername string `json:"user_gitlab_username"`
UserLFUsername string `json:"user_lf_username"`
UserName string `json:"user_name"`
UserEmail string `json:"user_email"`
diff --git a/cla-backend-go/signatures/models.go b/cla-backend-go/signatures/models.go
index db5a76043..0fd083496 100644
--- a/cla-backend-go/signatures/models.go
+++ b/cla-backend-go/signatures/models.go
@@ -20,6 +20,7 @@ type SignatureCompanyID struct {
type ApprovalCriteria struct {
UserEmail string
GitHubUsername string
+ GitlabUsername string
}
//ApprovalList data model
@@ -34,10 +35,11 @@ type ApprovalList struct {
EmailApprovals []string
DomainApprovals []string
GitHubUsernameApprovals []string
- GHUsernames []string
- GHOrgApprovals []string
- GitLabUsernameApprovals []string
- GitLabGroupApprovals []string
+ GitHubUsernames []string
+ GitHubOrgApprovals []string
+ GitlabUsernameApprovals []string
+ GitlabOrgApprovals []string
+ GitlabUsernames []string
GerritICLAECLAs []string
ICLAs []*models.IclaSignature
ECLAs []*models.Signature
diff --git a/cla-backend-go/signatures/projections.go b/cla-backend-go/signatures/projections.go
index 6bd143612..de7b0e1e5 100644
--- a/cla-backend-go/signatures/projections.go
+++ b/cla-backend-go/signatures/projections.go
@@ -24,13 +24,14 @@ func buildProjection() expression.ProjectionBuilder {
expression.Name("signature_signed"), // T/F
expression.Name("signature_type"), // ccla or cla
expression.Name("signature_user_ccla_company_id"), // reference to the company
- expression.Name("email_whitelist"),
- expression.Name("domain_whitelist"),
- expression.Name("github_whitelist"),
- expression.Name("github_org_whitelist"),
- expression.Name("gitlab_username_approval_list"), // added for GitLab support
- expression.Name("gitlab_project_approval_list"), // added for GitLab support
- expression.Name("user_github_username"),
+ expression.Name(SignatureEmailApprovalListColumn),
+ expression.Name(SignatureDomainApprovalListColumn),
+ expression.Name(SignatureGitHubUsernameApprovalListColumn),
+ expression.Name(SignatureGitHubOrgApprovalListColumn),
+ expression.Name(SignatureGitlabUsernameApprovalListColumn), // added for GitLab support
+ expression.Name(SignatureGitlabOrgApprovalListColumn), // added for GitLab support
+ expression.Name(SignatureUserGitHubUsername),
+ expression.Name(SignatureUserGitlabUsername),
expression.Name("user_lf_username"),
expression.Name("user_name"),
expression.Name("user_email"),
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index f937e432d..777a195e2 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -1169,7 +1169,7 @@ func (repo repository) CreateProjectSummaryReport(ctx context.Context, params si
}, nil
}
-// GetProjectCompanySignature returns a the signature for the specified project and specified company with the other query flags
+// GetProjectCompanySignature returns the signature for the specified project and specified company with the other query flags
func (repo repository) GetProjectCompanySignature(ctx context.Context, companyID, projectID string, approved, signed *bool, nextKey *string, pageSize *int64) (*models.Signature, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.GetProjectCompanySignature",
@@ -1513,7 +1513,7 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
if criteria != nil && criteria.GitHubUsername != "" {
log.WithFields(f).Debugf("adding Githubusername criteria filter for :%s ", criteria.GitHubUsername)
- filter = addAndCondition(filter, expression.Name("user_github_username").Equal(expression.Value(criteria.GitHubUsername)), &filterAdded)
+ filter = addAndCondition(filter, expression.Name(SignatureUserGitHubUsername).Equal(expression.Value(criteria.GitHubUsername)), &filterAdded)
}
if criteria != nil && criteria.UserEmail != "" {
@@ -2108,10 +2108,12 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Keep track of existing company approvals
approvalList := ApprovalList{
+ EmailApprovals: cclaSignature.EmailApprovalList,
DomainApprovals: cclaSignature.DomainApprovalList,
- GHOrgApprovals: cclaSignature.GithubOrgApprovalList,
GitHubUsernameApprovals: cclaSignature.GithubUsernameApprovalList,
- EmailApprovals: cclaSignature.EmailApprovalList,
+ GitHubOrgApprovals: cclaSignature.GithubOrgApprovalList,
+ GitlabUsernameApprovals: cclaSignature.GitlabUsernameApprovalList,
+ GitlabOrgApprovals: cclaSignature.GitlabOrgApprovalList,
CLAManager: claManager,
ICLAs: make([]*models.IclaSignature, 0),
ECLAs: make([]*models.Signature, 0),
@@ -2131,7 +2133,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
authUser := auth.User{
- Email: claManager.LfEmail,
+ Email: claManager.LfEmail.String(),
UserName: claManager.LfUsername,
}
@@ -2165,7 +2167,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// If we have an add or remove email list...we need to run an update for this column
if (params.AddEmailApprovalList != nil && len(params.AddEmailApprovalList) > 0) || (params.RemoveEmailApprovalList != nil && len(params.RemoveEmailApprovalList) > 0) {
- columnName := "email_whitelist"
+ columnName := SignatureEmailApprovalListColumn
attrList := buildApprovalAttributeList(ctx, cclaSignature.EmailApprovalList, params.AddEmailApprovalList, params.RemoveEmailApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
@@ -2179,7 +2181,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
} else {
haveAdditions = true
- expressionAttributeNames["#E"] = aws.String("email_whitelist")
+ expressionAttributeNames["#E"] = aws.String(columnName)
expressionAttributeValues[":e"] = attrList
updateExpression = updateExpression + " #E = :e, "
}
@@ -2207,7 +2209,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
criteria := &ApprovalCriteria{
UserEmail: email,
}
- log.WithFields(f).Debugf("Updating signature records for emailApprovalList: %+v ", params.RemoveEmailApprovalList)
+ log.WithFields(f).Debugf("Updating signature records for email approval list: %+v ", params.RemoveEmailApprovalList)
signs, appErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
if appErr != nil {
log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", appErr)
@@ -2215,7 +2217,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if len(signs.Signatures) == 0 {
- log.WithFields(f).Debugf("company employee signatures do not exist for company:%s and project: %s ", companyID, projectID)
+ log.WithFields(f).Debugf("company employee signatures do not exist for company: %s and project: %s ", companyID, projectID)
}
if len(signs.Signatures) > 0 {
@@ -2240,12 +2242,12 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if utils.StringInSlice(user.LfUsername, gerritICLAECLAs) {
gerritIclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeICLA)
if gerritIclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ msg := fmt.Sprintf("unable to remove gerrit user: %s from group: %s", user.LfUsername, approvalList.ClaGroupID)
log.WithFields(f).WithError(gerritIclaErr).Warn(msg)
}
eclaErr := repo.gerritService.RemoveUserFromGroup(ctx, &authUser, approvalList.ClaGroupID, user.LfUsername, utils.ClaTypeECLA)
if eclaErr != nil {
- msg := fmt.Sprintf("unable to remove gerrit user:%s from group:%s", user.LfUsername, approvalList.ClaGroupID)
+ msg := fmt.Sprintf("unable to remove gerrit user: %s from group: %s", user.LfUsername, approvalList.ClaGroupID)
log.WithFields(f).WithError(eclaErr).Warn(msg)
}
}
@@ -2284,7 +2286,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
if (params.AddDomainApprovalList != nil && len(params.AddDomainApprovalList) > 0) || (params.RemoveDomainApprovalList != nil && len(params.RemoveDomainApprovalList) > 0) {
- columnName := "domain_whitelist"
+ columnName := SignatureDomainApprovalListColumn
attrList := buildApprovalAttributeList(ctx, cclaSignature.DomainApprovalList, params.AddDomainApprovalList, params.RemoveDomainApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
@@ -2342,7 +2344,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if (params.AddGithubUsernameApprovalList != nil && len(params.AddGithubUsernameApprovalList) > 0) || (params.RemoveGithubUsernameApprovalList != nil && len(params.RemoveGithubUsernameApprovalList) > 0) {
- columnName := "github_whitelist"
+ columnName := SignatureGitHubUsernameApprovalListColumn
attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubUsernameApprovalList, params.AddGithubUsernameApprovalList, params.RemoveGithubUsernameApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
@@ -2356,9 +2358,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
} else {
haveAdditions = true
- expressionAttributeNames["#G"] = aws.String(columnName)
- expressionAttributeValues[":g"] = attrList
- updateExpression = updateExpression + " #G = :g, "
+ expressionAttributeNames["#GHU"] = aws.String(columnName)
+ expressionAttributeValues[":ghu"] = attrList
+ updateExpression = updateExpression + " #GHU = :ghu, "
}
if params.RemoveGithubUsernameApprovalList != nil {
// if email removal update signature approvals
@@ -2381,7 +2383,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
criteria := &ApprovalCriteria{
GitHubUsername: ghUsername,
}
- log.WithFields(f).Debugf("Updating signature records for ghUsernameApporvalList: %+v ", params.RemoveGithubUsernameApprovalList)
+ log.WithFields(f).Debugf("Updating signature records for github username apporval list: %+v ", params.RemoveGithubUsernameApprovalList)
signs, ghUserErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
if ghUserErr != nil {
log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", ghUserErr)
@@ -2394,13 +2396,13 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Get ICLAs
claUser, claErr := repo.usersRepo.GetUserByGitHubUsername(ghUsername)
if claErr != nil {
- log.WithFields(f).Debugf("unable to get User by GH Username: %s ", ghUsername)
+ log.WithFields(f).Debugf("unable to get user by github username: %s ", ghUsername)
return
}
if claUser != nil {
icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID, &approved, &signed)
if iclaErr != nil || icla == nil {
- log.WithFields(f).Debugf("unable to get icla signature for user with ghUsername: %s ", ghUsername)
+ log.WithFields(f).Debugf("unable to get icla signature for user with github username: %s ", ghUsername)
}
if icla != nil {
// Convert to IclSignature instance to leverage invalidateSignatures helper function
@@ -2425,7 +2427,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
if (params.AddGithubOrgApprovalList != nil && len(params.AddGithubOrgApprovalList) > 0) || (params.RemoveGithubOrgApprovalList != nil && len(params.RemoveGithubOrgApprovalList) > 0) {
- columnName := "github_org_whitelist"
+ columnName := SignatureGitHubOrgApprovalListColumn
attrList := buildApprovalAttributeList(ctx, cclaSignature.GithubOrgApprovalList, params.AddGithubOrgApprovalList, params.RemoveGithubOrgApprovalList)
// If no entries after consolidating all the updates, we need to remove the column
if attrList == nil || attrList.L == nil {
@@ -2439,9 +2441,9 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
}
} else {
haveAdditions = true
- expressionAttributeNames["#GO"] = aws.String("github_org_whitelist")
- expressionAttributeValues[":go"] = attrList
- updateExpression = updateExpression + " #GO = :go, "
+ expressionAttributeNames["#GHO"] = aws.String(columnName)
+ expressionAttributeValues[":gho"] = attrList
+ updateExpression = updateExpression + " #GHO = :gho, "
}
if params.RemoveGithubOrgApprovalList != nil {
@@ -2452,14 +2454,14 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
// Get repositories by CLAGroup
repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
if getRepoByCLAGroupErr != nil {
- msg := fmt.Sprintf("unable to fetch repositories for claGroupID: %s ", projectID)
+ msg := fmt.Sprintf("unable to fetch repositories for cla group ID: %s ", projectID)
log.WithFields(f).WithError(getRepoByCLAGroupErr).Warn(msg)
return nil, errors.New(msg)
}
var ghOrgRepositories []*models.GithubRepository
var ghOrgs []*models.GithubOrganization
for _, repository := range repositories {
- // Check for matching organization name in repositories table against approvalList removal GH Orgs
+ // Check for matching organization name in repositories table against approvalList removal GitHub organizations
if utils.StringInSlice(repository.RepositoryOrganizationName, approvalList.ApprovalList) {
ghOrgRepositories = append(ghOrgRepositories, repository)
}
@@ -2479,18 +2481,169 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
for _, ghOrg := range ghOrgs {
ghOrgUsers, getOrgMembersErr := github.GetOrganizationMembers(ctx, ghOrg.OrganizationName, ghOrg.OrganizationInstallationID)
if getOrgMembersErr != nil {
- msg := fmt.Sprintf("unable to fetch ghOrgUsers for org: %s ", ghOrg.OrganizationName)
+ msg := fmt.Sprintf("unable to fetch github organization users for org: %s ", ghOrg.OrganizationName)
log.WithFields(f).WithError(getOrgMembersErr).Warnf(msg)
return nil, errors.New(msg)
}
ghUsernames = append(ghUsernames, ghOrgUsers...)
}
- approvalList.GHUsernames = utils.RemoveDuplicates(ghUsernames)
+ approvalList.GitHubUsernames = utils.RemoveDuplicates(ghUsernames)
repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
}
}
+ if (params.AddGitlabUsernameApprovalList != nil && len(params.AddGitlabUsernameApprovalList) > 0) || (params.RemoveGitlabUsernameApprovalList != nil && len(params.RemoveGitlabUsernameApprovalList) > 0) {
+ columnName := SignatureGitlabUsernameApprovalListColumn
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.GitlabUsernameApprovalList, params.AddGitlabUsernameApprovalList, params.RemoveGitlabUsernameApprovalList)
+ // If no entries after consolidating all the updates, we need to remove the column
+ if attrList == nil || attrList.L == nil {
+ var rmColErr error
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
+ if rmColErr != nil {
+ msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
+ columnName, companyID, projectID, true, true)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ } else {
+ haveAdditions = true
+ expressionAttributeNames["#GLU"] = aws.String(columnName)
+ expressionAttributeValues[":glu"] = attrList
+ updateExpression = updateExpression + " #GLU = :glu, "
+ }
+ if params.RemoveGitlabUsernameApprovalList != nil {
+ // if email removal update signature approvals
+ if params.RemoveGitlabUsernameApprovalList != nil {
+ approvalList.Criteria = utils.GitlabUsernameCriteria
+ approvalList.ApprovalList = params.RemoveGitlabUsernameApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ approvalList.ClaGroupID = projectID
+ approvalList.ClaGroupName = claGroupModel.ProjectName
+ approvalList.CompanyID = companyID
+ approvalList.Version = claGroupModel.Version
+
+ // Get ICLAs
+ var wg sync.WaitGroup
+ wg.Add(len(params.RemoveGitlabUsernameApprovalList))
+ for _, ghUsername := range params.RemoveGitlabUsernameApprovalList {
+ go func(gitLabUsername string) {
+ defer wg.Done()
+ var iclas []*models.IclaSignature
+ var eclas []*models.Signature
+
+ criteria := &ApprovalCriteria{
+ GitlabUsername: gitLabUsername,
+ }
+ log.WithFields(f).Debugf("Updating signature records for gitlab username apporval list: %+v ", params.RemoveGitlabUsernameApprovalList)
+ signs, ghUserErr := repo.GetProjectCompanyEmployeeSignatures(ctx, employeeSignatureParams, criteria, pageSize)
+ if ghUserErr != nil {
+ log.WithFields(f).Debugf("unable to get Company Employee signatures : %+v ", ghUserErr)
+ return
+ }
+ if signs.Signatures != nil {
+ approvalList.ECLAs = signs.Signatures
+ eclas = signs.Signatures
+ }
+
+ claUser, claErr := repo.usersRepo.GetUserByGitlabUsername(gitLabUsername)
+ if claErr != nil {
+ log.WithFields(f).Debugf("unable to get User by gitlab username: %s ", gitLabUsername)
+ return
+ }
+ if claUser != nil {
+ icla, iclaErr := repo.GetIndividualSignature(ctx, projectID, claUser.UserID, &approved, &signed)
+ if iclaErr != nil || icla == nil {
+ log.WithFields(f).Debugf("unable to get icla signature for user with gitlab username: %s ", gitLabUsername)
+ }
+ if icla != nil {
+ // Convert to IclSignature instance to leverage invalidateSignatures helper function
+ approvalList.ICLAs = []*models.IclaSignature{{
+ GitlabUsername: icla.UserGHUsername,
+ LfUsername: icla.UserLFID,
+ SignatureID: icla.SignatureID,
+ }}
+ }
+ }
+
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+
+ // Send Email
+ repo.sendEmail(ctx, getBestEmail(claUser), &approvalList, iclas, eclas)
+
+ }(ghUsername)
+ }
+ wg.Wait()
+ }
+ }
+ }
+
+ if (params.AddGitlabOrgApprovalList != nil && len(params.AddGitlabOrgApprovalList) > 0) || (params.RemoveGitlabOrgApprovalList != nil && len(params.RemoveGitlabOrgApprovalList) > 0) {
+ columnName := SignatureGitlabOrgApprovalListColumn
+ attrList := buildApprovalAttributeList(ctx, cclaSignature.GitlabOrgApprovalList, params.AddGitlabOrgApprovalList, params.RemoveGitlabOrgApprovalList)
+ // If no entries after consolidating all the updates, we need to remove the column
+ if attrList == nil || attrList.L == nil {
+ var rmColErr error
+ cclaSignature, rmColErr = repo.removeColumn(ctx, cclaSignature.SignatureID, columnName)
+ if rmColErr != nil {
+ msg := fmt.Sprintf("unable to remove column %s for signature for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
+ columnName, companyID, projectID, true, true)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ } else {
+ haveAdditions = true
+ expressionAttributeNames["#GLO"] = aws.String(columnName)
+ expressionAttributeValues[":glo"] = attrList
+ updateExpression = updateExpression + " #GLO = :glo, "
+ }
+
+ if params.RemoveGithubOrgApprovalList != nil {
+ approvalList.Criteria = utils.GitlabOrgCriteria
+ approvalList.ApprovalList = params.RemoveGitlabOrgApprovalList
+ approvalList.Action = utils.RemoveApprovals
+ approvalList.Version = claGroupModel.Version
+ // Get repositories by CLAGroup
+ repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
+ if getRepoByCLAGroupErr != nil {
+ msg := fmt.Sprintf("unable to fetch repositories for cla group ID: %s ", projectID)
+ log.WithFields(f).WithError(getRepoByCLAGroupErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ var gitLabOrgRepositories []*models.GithubRepository
+ var gitLabOrgs []*models.GithubOrganization
+ for _, repository := range repositories {
+ // Check for matching organization name in repositories table against approvalList removal gitlab organizations/groups
+ if utils.StringInSlice(repository.RepositoryOrganizationName, approvalList.ApprovalList) {
+ gitLabOrgRepositories = append(gitLabOrgRepositories, repository)
+ }
+ }
+
+ for _, gitLabOrgRepo := range gitLabOrgRepositories {
+ gitLabOrg, getGitlabOrgErr := repo.ghOrgRepo.GetGitHubOrganization(ctx, gitLabOrgRepo.RepositoryOrganizationName)
+ if getGitlabOrgErr != nil {
+ msg := fmt.Sprintf("unable to get gitlab organization by name: %s ", gitLabOrgRepo.RepositoryOrganizationName)
+ log.WithFields(f).WithError(getGitlabOrgErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ gitLabOrgs = append(gitLabOrgs, gitLabOrg)
+ }
+
+ var gitLabUsernames []string
+ for _, gitLabOrg := range gitLabOrgs {
+ gitLabOrgUsers, getOrgMembersErr := github.GetOrganizationMembers(ctx, gitLabOrg.OrganizationName, gitLabOrg.OrganizationInstallationID)
+ if getOrgMembersErr != nil {
+ msg := fmt.Sprintf("unable to fetch gitLabOrgUsers for org: %s ", gitLabOrg.OrganizationName)
+ log.WithFields(f).WithError(getOrgMembersErr).Warnf(msg)
+ return nil, errors.New(msg)
+ }
+ gitLabUsernames = append(gitLabUsernames, gitLabOrgUsers...)
+ }
+ approvalList.GitlabUsernames = utils.RemoveDuplicates(gitLabUsernames)
+
+ repo.invalidateSignatures(ctx, &approvalList, claManager, eventArgs)
+ }
+ }
// Ensure at least one value is set for us to update
if !haveAdditions {
log.WithFields(f).Debugf("no updates required to any of the approved list values company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t - expecting at least something to update",
@@ -2511,7 +2664,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
},
ExpressionAttributeNames: expressionAttributeNames,
ExpressionAttributeValues: expressionAttributeValues,
- UpdateExpression: aws.String(updateExpression), //aws.String("SET #L = :l"),
+ UpdateExpression: aws.String(updateExpression),
}
log.WithFields(f).Debugf("updating approval list for company ID: %s project ID: %s, type: ccla, signed: %t, approved: %t",
@@ -2721,7 +2874,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
email := getBestEmail(user)
authUser := auth.User{
- Email: claManager.LfEmail,
+ Email: claManager.LfEmail.String(),
UserName: claManager.LfUsername,
}
@@ -2757,7 +2910,7 @@ func (repo repository) verifyUserApprovals(ctx context.Context, userID, signatur
}
} else if approvalList.Criteria == utils.GitHubOrgCriteria {
// Handle GH Org Approvals
- if utils.StringInSlice(user.GithubUsername, approvalList.GHUsernames) {
+ if utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernames) {
if !utils.StringInSlice(getBestEmail(user), approvalList.EmailApprovals) && !utils.StringInSlice(user.GithubUsername, approvalList.GitHubUsernameApprovals) {
//Invalidate record
@@ -2869,7 +3022,7 @@ func (repo repository) AddUsersDetails(ctx context.Context, signatureID string,
}
var email string
if userModel.LfEmail != "" {
- email = userModel.LfEmail
+ email = userModel.LfEmail.String()
} else {
if len(userModel.Emails) > 0 {
email = userModel.Emails[0]
@@ -2885,7 +3038,7 @@ func (repo repository) AddUsersDetails(ctx context.Context, signatureID string,
},
}
ue := utils.NewDynamoUpdateExpression()
- ue.AddAttributeName("#gh_username", "user_github_username", userModel.GithubUsername != "")
+ ue.AddAttributeName("#gh_username", SignatureUserGitHubUsername, userModel.GithubUsername != "")
ue.AddAttributeName("#lf_username", "user_lf_username", userModel.LfUsername != "")
ue.AddAttributeName("#name", "user_name", userModel.Username != "")
ue.AddAttributeName("#email", "user_email", email != "")
@@ -2996,7 +3149,7 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID
log.WithFields(f).Debugf("adding search term filter for: '%s'", searchTermValue)
searchTermExpression := expression.Name("signature_reference_name_lower").Contains(strings.ToLower(searchTermValue)).
Or(expression.Name("user_email").Contains(strings.ToLower(searchTermValue))).
- Or(expression.Name("user_github_username").Contains(strings.ToLower(searchTermValue))).
+ Or(expression.Name(SignatureUserGitHubUsername).Contains(strings.ToLower(searchTermValue))).
Or(expression.Name("user_docusign_name").Contains(strings.ToLower(searchTermValue)))
filter = addAndCondition(filter, searchTermExpression, &filterAdded)
}
diff --git a/cla-backend-go/signatures/service.go b/cla-backend-go/signatures/service.go
index fd293811c..b78bf2b6e 100644
--- a/cla-backend-go/signatures/service.go
+++ b/cla-backend-go/signatures/service.go
@@ -527,6 +527,10 @@ func buildApprovalListSummary(approvalListChanges *models.ApprovalList) string {
approvalListSummary += appendList(approvalListChanges.RemoveGithubUsernameApprovalList, "Removed GitHub User:")
approvalListSummary += appendList(approvalListChanges.AddGithubOrgApprovalList, "Added GitHub Organization:")
approvalListSummary += appendList(approvalListChanges.RemoveGithubOrgApprovalList, "Removed GitHub Organization:")
+ approvalListSummary += appendList(approvalListChanges.AddGitlabUsernameApprovalList, "Added Gitlab User:")
+ approvalListSummary += appendList(approvalListChanges.RemoveGitlabUsernameApprovalList, "Removed Gitlab User:")
+ approvalListSummary += appendList(approvalListChanges.AddGitlabOrgApprovalList, "Added Gitlab Organization:")
+ approvalListSummary += appendList(approvalListChanges.RemoveGitlabOrgApprovalList, "Removed Gitlab Organization:")
approvalListSummary += ""
return approvalListSummary
}
@@ -627,25 +631,66 @@ func (s service) getRemoveGitHubContributors(approvalList *models.ApprovalList)
return userModelList
}
+
+// getAddGitlabContributors is a helper function to look up the Gitlab contributors impacted by the Approval List update
+func (s service) getAddGitlabContributors(approvalList *models.ApprovalList) []*models.User {
+ var userModelList []*models.User
+ for _, value := range approvalList.AddGitlabUsernameApprovalList {
+ userModel, err := s.usersService.GetUserByGitHubUsername(value)
+ if err != nil {
+ log.Warnf("unable to lookup user by Gitlab username: %s, error: %+v", value, err)
+ } else {
+ userModelList = append(userModelList, userModel)
+ }
+ }
+
+ return userModelList
+}
+
+// getRemoveGitlabContributors is a helper function to look up the Gitlab contributors impacted by the Approval List update
+func (s service) getRemoveGitlabContributors(approvalList *models.ApprovalList) []*models.User {
+ var userModelList []*models.User
+ for _, value := range approvalList.RemoveGitlabUsernameApprovalList {
+ userModel, err := s.usersService.GetUserByGitHubUsername(value)
+ if err != nil {
+ log.Warnf("unable to lookup user by Gitlab username: %s, error: %+v", value, err)
+ } else {
+ userModelList = append(userModelList, userModel)
+ }
+ }
+
+ return userModelList
+}
+
func (s service) sendRequestAccessEmailToContributors(authUser *auth.User, companyModel *models.Company, claGroupModel *models.ClaGroup, approvalList *models.ApprovalList) {
addEmailUsers := s.getAddEmailContributors(approvalList)
for _, user := range addEmailUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to",
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "added", "to",
fmt.Sprintf("you are authorized to contribute to %s on behalf of %s", claGroupModel.ProjectName, companyModel.CompanyName))
}
removeEmailUsers := s.getRemoveEmailContributors(approvalList)
for _, user := range removeEmailUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from",
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "removed", "from",
fmt.Sprintf("you are no longer authorized to contribute to %s on behalf of %s ", claGroupModel.ProjectName, companyModel.CompanyName))
}
addGitHubUsers := s.getAddGitHubContributors(approvalList)
for _, user := range addGitHubUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "added", "to",
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "added", "to",
fmt.Sprintf("you are authorized to contribute to %s on behalf of %s", claGroupModel.ProjectName, companyModel.CompanyName))
}
removeGitHubUsers := s.getRemoveGitHubContributors(approvalList)
for _, user := range removeGitHubUsers {
- sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail, "removed", "from",
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "removed", "from",
+ fmt.Sprintf("you are no longer authorized to contribute to %s on behalf of %s ", claGroupModel.ProjectName, companyModel.CompanyName))
+ }
+ addGitlabUsers := s.getAddGitlabContributors(approvalList)
+ for _, user := range addGitlabUsers {
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "added", "to",
+ fmt.Sprintf("you are authorized to contribute to %s on behalf of %s", claGroupModel.ProjectName, companyModel.CompanyName))
+ }
+ removeGitlabUsers := s.getRemoveGitlabContributors(approvalList)
+ for _, user := range removeGitlabUsers {
+ sendRequestAccessEmailToContributorRecipient(authUser, companyModel, claGroupModel, user.Username, user.LfEmail.String(), "removed", "from",
fmt.Sprintf("you are no longer authorized to contribute to %s on behalf of %s ", claGroupModel.ProjectName, companyModel.CompanyName))
}
}
@@ -788,6 +833,75 @@ func (s service) createEventLogEntries(ctx context.Context, companyModel *models
},
})
}
+ for _, value := range approvalList.AddGitlabUsernameApprovalList {
+ // Send an event
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ClaApprovalListUpdated,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ClaGroupModel: claGroupModel,
+ CompanyID: companyModel.CompanyID,
+ CompanyModel: companyModel,
+ LfUsername: userModel.LfUsername,
+ UserID: userModel.UserID,
+ UserModel: userModel,
+ ProjectSFID: claGroupModel.ProjectExternalID,
+ EventData: &events.CLAApprovalListAddGitlabUsernameData{
+ ApprovalListGitlabUsername: value,
+ },
+ })
+ }
+ for _, value := range approvalList.RemoveGitlabUsernameApprovalList {
+ // Send an event
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ClaApprovalListUpdated,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ClaGroupModel: claGroupModel,
+ CompanyID: companyModel.CompanyID,
+ CompanyModel: companyModel,
+ LfUsername: userModel.LfUsername,
+ UserID: userModel.UserID,
+ UserModel: userModel,
+ ProjectSFID: claGroupModel.ProjectExternalID,
+ EventData: &events.CLAApprovalListRemoveGitlabUsernameData{
+ ApprovalListGitlabUsername: value,
+ },
+ })
+ }
+ for _, value := range approvalList.AddGitlabOrgApprovalList {
+ // Send an event
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ClaApprovalListUpdated,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ClaGroupModel: claGroupModel,
+ CompanyID: companyModel.CompanyID,
+ CompanyModel: companyModel,
+ LfUsername: userModel.LfUsername,
+ UserID: userModel.UserID,
+ UserModel: userModel,
+ ProjectSFID: claGroupModel.ProjectExternalID,
+ EventData: &events.CLAApprovalListAddGitlabOrgData{
+ ApprovalListGitlabOrg: value,
+ },
+ })
+ }
+ for _, value := range approvalList.RemoveGitlabOrgApprovalList {
+ // Send an event
+ s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.ClaApprovalListUpdated,
+ CLAGroupID: claGroupModel.ProjectID,
+ ProjectID: claGroupModel.ProjectExternalID,
+ ClaGroupModel: claGroupModel,
+ CompanyID: companyModel.CompanyID,
+ CompanyModel: companyModel,
+ LfUsername: userModel.LfUsername,
+ UserID: userModel.UserID,
+ UserModel: userModel,
+ ProjectSFID: claGroupModel.ProjectExternalID,
+ EventData: &events.CLAApprovalListRemoveGitlabOrgData{
+ ApprovalListGitlabOrg: value,
+ },
+ })
+ }
}
func (s service) GetClaGroupICLASignatures(ctx context.Context, claGroupID string, searchTerm *string, approved, signed *bool, pageSize int64, nextKey string) (*models.IclaSignatures, error) {
@@ -853,7 +967,7 @@ func sendRequestAccessEmailToContributorRecipient(authUser *auth.User, companyMo
// getBestEmail is a helper function to return the best email address for the user model
func getBestEmail(userModel *models.User) string {
if userModel.LfEmail != "" {
- return userModel.LfEmail
+ return userModel.LfEmail.String()
}
for _, email := range userModel.Emails {
diff --git a/cla-backend-go/swagger/common/icla-signature.yaml b/cla-backend-go/swagger/common/icla-signature.yaml
index 58bde5232..39662700c 100644
--- a/cla-backend-go/swagger/common/icla-signature.yaml
+++ b/cla-backend-go/swagger/common/icla-signature.yaml
@@ -14,6 +14,10 @@ properties:
type: string
description: the user's github username
example: 'tomcruise'
+ gitlab_username:
+ type: string
+ description: the user's gitlab username
+ example: 'tomcruise'
lf_username:
type: string
description: the user's LF username
diff --git a/cla-backend-go/swagger/common/signature-approval-list.yaml b/cla-backend-go/swagger/common/signature-approval-list.yaml
index 1fc372e91..a9f80ad0f 100644
--- a/cla-backend-go/swagger/common/signature-approval-list.yaml
+++ b/cla-backend-go/swagger/common/signature-approval-list.yaml
@@ -7,50 +7,87 @@ description: A signature approval list for a project / company
properties:
AddEmailApprovalList:
type: array
+ title: Add User Email
description: a list of zero or more email addresses to be added to the approval list
x-nullable: true
items:
type: string
RemoveEmailApprovalList:
type: array
- description: a list of zero or more email addresses to be from to the approval list
+ title: Remove User Email
+ description: a list of zero or more email addresses to be removed from the approval list
x-nullable: true
items:
type: string
AddDomainApprovalList:
type: array
+ title: Add Domain Email
description: a list of zero or more domains to be added to the approval list
x-nullable: true
items:
type: string
+ example: 'linuxfoundation.org'
RemoveDomainApprovalList:
type: array
+ title: Remove Domain Email
description: a list of zero or more domains to be removed from the approval list
x-nullable: true
items:
type: string
+ example: 'linuxfoundation.org'
AddGithubUsernameApprovalList:
type: array
+ title: Add GitHub Username
description: a list of zero or more GitHub user name values to be added to the approval list
x-nullable: true
items:
type: string
RemoveGithubUsernameApprovalList:
type: array
+ title: Remove GitHub Username
description: a list of zero or more GitHub user name values to be removed from the approval list
x-nullable: true
items:
type: string
AddGithubOrgApprovalList:
type: array
+ title: Add GitHub Organization
description: a list of zero or more GitHub organization values to be added to the approval list
x-nullable: true
items:
type: string
RemoveGithubOrgApprovalList:
type: array
+ title: Remove GitHub Organization
description: a list of zero or more GitHub organization values to be removed from the approval list
x-nullable: true
items:
type: string
-
+ AddGitlabUsernameApprovalList:
+ type: array
+ title: Add Gitlab Username
+ description: a list of zero or more Gitlab user name values to be added to the approval list
+ x-nullable: true
+ items:
+ type: string
+ RemoveGitlabUsernameApprovalList:
+ type: array
+ title: Remove Gitlab Username
+ description: a list of zero or more Gitlab user name values to be removed from the approval list
+ x-nullable: true
+ items:
+ type: string
+ AddGitlabOrgApprovalList:
+ type: array
+ title: Add Gitlab Organization
+ description: a list of zero or more Gitlab organization values to be added to the approval list
+ x-nullable: true
+ items:
+ type: string
+ RemoveGitlabOrgApprovalList:
+ type: array
+ title: Remove Gitlab Organization
+ description: a list of zero or more Gitlab organization values to be removed from the approval list
+ x-nullable: true
+ items:
+ type: string
diff --git a/cla-backend-go/swagger/common/signature.yaml b/cla-backend-go/swagger/common/signature.yaml
index 0e61a9012..912ffca13 100644
--- a/cla-backend-go/swagger/common/signature.yaml
+++ b/cla-backend-go/swagger/common/signature.yaml
@@ -81,7 +81,15 @@ properties:
userGHUsername:
type: string
description: the user's GitHub username, when available
- example: linux-user
+ example: 'github-user'
+ userGitlabID:
+ type: string
+ description: the user's Gitlab ID, when available
+ example: '1864'
+ userGitlabUsername:
+ type: string
+ description: the user's Gitlab username, when available
+ example: 'gitlab-user'
userLFID:
type: string
description: the user's LF Login ID
@@ -132,6 +140,18 @@ properties:
x-nullable: true
items:
type: string
+ gitlabUsernameApprovalList:
+ type: array
+ description: a list of zero or more Gitlab user name values in the approval list
+ x-nullable: true
+ items:
+ type: string
+ gitlabOrgApprovalList:
+ type: array
+ description: a list of zero or more Gitlab organization values in the approval list
+ x-nullable: true
+ items:
+ type: string
userDocusignName:
type: string
description: full name used on docusign document
diff --git a/cla-backend-go/swagger/common/user.yaml b/cla-backend-go/swagger/common/user.yaml
index d4f332b0a..4e59f25c1 100644
--- a/cla-backend-go/swagger/common/user.yaml
+++ b/cla-backend-go/swagger/common/user.yaml
@@ -7,9 +7,11 @@ title: User
description: User model
properties:
userID:
- type: string
+ $ref: './common/properties/internal-id.yaml'
+ description: the user's internal/unique ID
userExternalID:
- type: string
+ $ref: './common/properties/external-id.yaml'
+ description: the user's external ID tied to SF
username:
type: string
dateCreated:
@@ -17,21 +19,37 @@ properties:
dateModified:
type: string
lfEmail:
- type: string
+ $ref: './common/properties/email.yaml'
lfUsername:
type: string
companyID:
- type: string
+ $ref: './common/properties/internal-id.yaml'
+ description: the user's optional company ID
githubID:
type: string
+ description: the user's github ID
+ example: '123434'
githubUsername:
type: string
+ description: the user's github username
+ example: 'grapes42'
+ gitlabID:
+ type: string
+ description: the user's gitlab ID
+ example: '123434'
+ gitlabUsername:
+ type: string
+ description: the user's gitlab username
+ example: 'orangejuice'
admin:
type: boolean
version:
type: string
+ description: the version identifier for this record
+ example: 'v1'
note:
type: string
+ description: an optional note for this user record
emails:
type: array
items:
diff --git a/cla-backend-go/tests/utils_test.go b/cla-backend-go/tests/utils_test.go
index a180ad0a3..235a82bd5 100644
--- a/cla-backend-go/tests/utils_test.go
+++ b/cla-backend-go/tests/utils_test.go
@@ -9,8 +9,6 @@ import (
"testing"
"time"
- "github.com/gofrs/uuid"
-
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/stretchr/testify/assert"
@@ -214,158 +212,6 @@ func TestTrimRemoveTrailingSpace(t *testing.T) {
}
-// TestValidEmail tests the email validator
-func TestValidEmail(t *testing.T) {
- validEmails := []string{
- "user@linuxfoundation.org",
- "user+test@linuxfoundation.org",
- }
- inValidEmails := []string{
- "user@linuxfoundation_org",
- "user/linuxfoundation.org",
- "userlinuxfoundation.org",
- }
-
- for _, email := range validEmails {
- assert.True(t, utils.ValidEmail(email), fmt.Sprintf("valid email %s", email))
- }
-
- for _, email := range inValidEmails {
- assert.False(t, utils.ValidEmail(email), fmt.Sprintf("invalid email %s", email))
- }
-}
-
-// TestValidDomain tests the domain validator
-func TestValidDomain(t *testing.T) {
- validDomains := []string{
- "linuxfoundation.org",
- "wikipedia.org",
- "google.com",
- "slack.com",
- "slack-domain-with-dash.com",
- }
-
- validWildcardDomains := []string{
- "linuxfoundation.org",
- "wikipedia.org",
- "google.com",
- "slack.com",
- "slack-domain-with-dash.com",
- "*.google.com",
- "*.us.google.com",
- }
-
- inValidDomains := []string{
- "*.google.com", // test case with allowWildcards = false
- "linuxfoundation_org",
- "*.linuxfoundation_org", // test case with allowWildcards = false
- "/linuxfoundation.org",
- "linuxfoundation+fun.org",
- "user_linuxfoundation.org",
- }
-
- inWildcardValidDomains := []string{
- "linuxfoundation_org",
- "/linuxfoundation.org",
- "linuxfoundation+fun.org",
- "*.linuxfoundation+fun.org",
- "user_linuxfoundation.org",
- "*.user_linuxfoundation.org",
- }
-
- for _, domain := range validDomains {
- msg, valid := utils.ValidDomain(domain, false)
- assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
- }
-
- for _, domain := range validWildcardDomains {
- msg, valid := utils.ValidDomain(domain, true)
- assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
- }
-
- for _, domain := range inValidDomains {
- msg, valid := utils.ValidDomain(domain, false)
- assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
- }
-
- for _, domain := range inWildcardValidDomains {
- msg, valid := utils.ValidDomain(domain, true)
- assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
- }
-}
-
-// TestGitHubUsername tests the GitHub username validator
-func TestGitHubUsername(t *testing.T) {
- validGitHubUsername := []string{
- "linuxfoundation",
- "user123",
- "user_123",
- "user_name_with_underscores",
- }
- inValidGitHubUsername := []string{
- "li", // too short
- "/linuxfoundation",
- "linuxfoundation+fun",
- "user&linuxfoundation",
- "user{linuxfoundation",
- "user}linuxfoundation",
- "user*linuxfoundation",
- "user@linuxfoundation",
- "user!linuxfoundation",
- "user^linuxfoundation",
- "++userlinuxfoundation",
- "\\userlinuxfoundation",
- }
-
- for _, username := range validGitHubUsername {
- msg, valid := utils.ValidGitHubUsername(username)
- assert.True(t, valid, fmt.Sprintf("valid GitHub Username %s %s", username, msg))
- }
-
- for _, username := range inValidGitHubUsername {
- msg, valid := utils.ValidGitHubUsername(username)
- assert.False(t, valid, fmt.Sprintf("invalid GitHub Username %s %s", username, msg))
- }
-}
-
-// TestGitHubOrg tests the GitHub username validator
-func TestGitHubOrg(t *testing.T) {
- validGitHubOrg := []string{
- "linuxfoundation",
- "linuxfoundation.org",
- "user123",
- "user-123",
- "user-123.org",
- "user-123.com",
- "user_123",
- "user_name_with_underscores",
- }
- inValidGitHubOrg := []string{
- "li", // too short
- "/linuxfoundation",
- "linuxfoundation+fun",
- "user&linuxfoundation",
- "user{linuxfoundation",
- "user}linuxfoundation",
- "user*linuxfoundation",
- "user@linuxfoundation",
- "user!linuxfoundation",
- "user^linuxfoundation",
- "++userlinuxfoundation",
- "\\userlinuxfoundation",
- }
-
- for _, org := range validGitHubOrg {
- msg, valid := utils.ValidGitHubOrg(org)
- assert.True(t, valid, fmt.Sprintf("valid GitHub Organization %s %s", org, msg))
- }
-
- for _, org := range inValidGitHubOrg {
- msg, valid := utils.ValidGitHubOrg(org)
- assert.False(t, valid, fmt.Sprintf("invalid GitHub Organization %s %s", org, msg))
- }
-}
-
// TestGetPathFromURL tests for getting the path for a URL
func TestGetPathFromURL(t *testing.T) {
input := "https://cla-signature-files-dev.s3.amazonaws.com/contract-group/66b97366-a298-4625-965e-0c292c39f9a2/template/ccla-2020-09-25T22-37-51Z.pdf"
@@ -374,38 +220,3 @@ func TestGetPathFromURL(t *testing.T) {
assert.Nil(t, err, "GetPathFromURL error is nil")
assert.Equal(t, expected, result)
}
-
-func TestIsUUIDv4True(t *testing.T) {
- v4, err := uuid.NewV4()
- assert.Nil(t, err, "NewV4 UUID is nil")
- assert.True(t, utils.IsUUIDv4(v4.String()), fmt.Sprintf("%s is a v4 UUID", v4.String()))
-}
-
-func TestIsUUIDv4LikeSFID(t *testing.T) {
- sfid := "0014100000TdznWAAR"
- assert.False(t, utils.IsUUIDv4(sfid), fmt.Sprintf("%s is not v4 UUID", sfid))
-}
-
-func TestIsSalesForceID(t *testing.T) {
- trueTestData := []string{
- "00117000015vpjX",
- "00117000015vpjXAAQ",
- }
- falseTestData := []string{
- "",
- "00117",
- "-00117",
- "00117000015vpj-",
- "0011700001-vpjXAAQ",
- "0011700001?vpjXAAQ",
- "0011700001&vpjXAAQ",
- "0011700001_vpjXAAQ",
- }
-
- for i := range trueTestData {
- assert.True(t, utils.IsSalesForceID(trueTestData[i]))
- }
- for i := range falseTestData {
- assert.False(t, utils.IsSalesForceID(falseTestData[i]))
- }
-}
diff --git a/cla-backend-go/tests/utils_validators_test.go b/cla-backend-go/tests/utils_validators_test.go
new file mode 100644
index 000000000..fab0ff3e8
--- /dev/null
+++ b/cla-backend-go/tests/utils_validators_test.go
@@ -0,0 +1,272 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/gofrs/uuid"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// TestValidEmail tests the email validator
+func TestValidEmail(t *testing.T) {
+ validEmails := []string{
+ "user@linuxfoundation.org",
+ "user+test@linuxfoundation.org",
+ }
+ inValidEmails := []string{
+ "user@linuxfoundation_org",
+ "user/linuxfoundation.org",
+ "userlinuxfoundation.org",
+ }
+
+ for _, email := range validEmails {
+ assert.True(t, utils.ValidEmail(email), fmt.Sprintf("valid email %s", email))
+ }
+
+ for _, email := range inValidEmails {
+ assert.False(t, utils.ValidEmail(email), fmt.Sprintf("invalid email %s", email))
+ }
+}
+
+// TestValidDomain tests the domain validator
+func TestValidDomain(t *testing.T) {
+ validDomains := []string{
+ "linuxfoundation.org",
+ "wikipedia.org",
+ "google.com",
+ "slack.com",
+ "slack-domain-with-dash.com",
+ }
+
+ validWildcardDomains := []string{
+ "linuxfoundation.org",
+ "wikipedia.org",
+ "google.com",
+ "slack.com",
+ "slack-domain-with-dash.com",
+ "*.google.com",
+ "*.us.google.com",
+ }
+
+ inValidDomains := []string{
+ "*.google.com", // test case with allowWildcards = false
+ "linuxfoundation_org",
+ "*.linuxfoundation_org", // test case with allowWildcards = false
+ "/linuxfoundation.org",
+ "linuxfoundation+fun.org",
+ "user_linuxfoundation.org",
+ }
+
+ inWildcardValidDomains := []string{
+ "linuxfoundation_org",
+ "/linuxfoundation.org",
+ "linuxfoundation+fun.org",
+ "*.linuxfoundation+fun.org",
+ "user_linuxfoundation.org",
+ "*.user_linuxfoundation.org",
+ }
+
+ for _, domain := range validDomains {
+ msg, valid := utils.ValidDomain(domain, false)
+ assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
+ }
+
+ for _, domain := range validWildcardDomains {
+ msg, valid := utils.ValidDomain(domain, true)
+ assert.True(t, valid, fmt.Sprintf("valid domain %s %s", domain, msg))
+ }
+
+ for _, domain := range inValidDomains {
+ msg, valid := utils.ValidDomain(domain, false)
+ assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
+ }
+
+ for _, domain := range inWildcardValidDomains {
+ msg, valid := utils.ValidDomain(domain, true)
+ assert.False(t, valid, fmt.Sprintf("invalid domain %s %s", domain, msg))
+ }
+}
+
+// TestGitHubUsername tests the GitHub username validator
+func TestGitHubUsername(t *testing.T) {
+ validGitHubUsername := []string{
+ "linuxfoundation",
+ "user123",
+ "user_123",
+ "user_name_with_underscores",
+ }
+ inValidGitHubUsername := []string{
+ "li", // too short
+ "/linuxfoundation",
+ "linuxfoundation+fun",
+ "user&linuxfoundation",
+ "user{linuxfoundation",
+ "user}linuxfoundation",
+ "user*linuxfoundation",
+ "user@linuxfoundation",
+ "user!linuxfoundation",
+ "user^linuxfoundation",
+ "++userlinuxfoundation",
+ "\\userlinuxfoundation",
+ }
+
+ for _, username := range validGitHubUsername {
+ msg, valid := utils.ValidGitHubUsername(username)
+ assert.True(t, valid, fmt.Sprintf("valid GitHub Username %s %s", username, msg))
+ }
+
+ for _, username := range inValidGitHubUsername {
+ msg, valid := utils.ValidGitHubUsername(username)
+ assert.False(t, valid, fmt.Sprintf("invalid GitHub Username %s %s", username, msg))
+ }
+}
+
+// TestGitHubOrg tests the GitHub username validator
+func TestGitHubOrg(t *testing.T) {
+ validGitHubOrg := []string{
+ "linuxfoundation",
+ "linuxfoundation.org",
+ "user123",
+ "user-123",
+ "user-123.org",
+ "user-123.com",
+ "user_123",
+ "user_name_with_underscores",
+ }
+ inValidGitHubOrg := []string{
+ "li", // too short
+ "/linuxfoundation",
+ "linuxfoundation+fun",
+ "user&linuxfoundation",
+ "user{linuxfoundation",
+ "user}linuxfoundation",
+ "user*linuxfoundation",
+ "user@linuxfoundation",
+ "user!linuxfoundation",
+ "user^linuxfoundation",
+ "++userlinuxfoundation",
+ "\\userlinuxfoundation",
+ }
+
+ for _, org := range validGitHubOrg {
+ msg, valid := utils.ValidGitHubOrg(org)
+ assert.True(t, valid, fmt.Sprintf("valid GitHub Organization %s %s", org, msg))
+ }
+
+ for _, org := range inValidGitHubOrg {
+ msg, valid := utils.ValidGitHubOrg(org)
+ assert.False(t, valid, fmt.Sprintf("invalid GitHub Organization %s %s", org, msg))
+ }
+}
+
+// TestGitlabUsername tests the Gitlab username validator
+func TestGitlabUsername(t *testing.T) {
+ validGitlabUsername := []string{
+ "linuxfoundationuser",
+ "user1234",
+ "user_1234",
+ "user_name_with_underscores_gitlab",
+ }
+ inValidGitlabUsername := []string{
+ "ii", // too short
+ "/linuxfoundationuser",
+ "linuxfoundationuser+fun",
+ "user&linuxfoundationuser",
+ "user{linuxfoundationuser",
+ "user}linuxfoundationuser",
+ "user*linuxfoundationuser",
+ "user@linuxfoundationuser",
+ "user!linuxfoundationuser",
+ "user^linuxfoundationuser",
+ "++userlinuxfoundationuser",
+ "\\userlinuxfoundationuser",
+ }
+
+ for _, username := range validGitlabUsername {
+ msg, valid := utils.ValidGitlabUsername(username)
+ assert.True(t, valid, fmt.Sprintf("valid Gitlab Username %s %s", username, msg))
+ }
+
+ for _, username := range inValidGitlabUsername {
+ msg, valid := utils.ValidGitlabUsername(username)
+ assert.False(t, valid, fmt.Sprintf("invalid Gitlab Username %s %s", username, msg))
+ }
+}
+
+// TestGitlabOrg tests the GitHub username validator
+func TestGitlabOrg(t *testing.T) {
+ validGitlabOrg := []string{
+ "linuxfoundationgrp",
+ "linuxfoundationgrp.org",
+ "user1234",
+ "user-1234",
+ "user-1234.org",
+ "user-1234.com",
+ "user_1234",
+ "user_name_with_underscores_gitlab",
+ }
+ inValidGitlabOrg := []string{
+ "hi", // too short
+ "/linuxfoundationgrp",
+ "linuxfoundationgrp+fun",
+ "user&linuxfoundationgrp",
+ "user{linuxfoundationgrp",
+ "user}linuxfoundationgrp",
+ "user*linuxfoundationgrp",
+ "user@linuxfoundationgrp",
+ "user!linuxfoundationgrp",
+ "user^linuxfoundationgrp",
+ "++userlinuxfoundationgrp",
+ "\\userlinuxfoundationgrp",
+ }
+
+ for _, org := range validGitlabOrg {
+ msg, valid := utils.ValidGitHubOrg(org)
+ assert.True(t, valid, fmt.Sprintf("valid GitHub Organization %s %s", org, msg))
+ }
+
+ for _, org := range inValidGitlabOrg {
+ msg, valid := utils.ValidGitHubOrg(org)
+ assert.False(t, valid, fmt.Sprintf("invalid GitHub Organization %s %s", org, msg))
+ }
+}
+func TestIsUUIDv4True(t *testing.T) {
+ v4, err := uuid.NewV4()
+ assert.Nil(t, err, "NewV4 UUID is nil")
+ assert.True(t, utils.IsUUIDv4(v4.String()), fmt.Sprintf("%s is a v4 UUID", v4.String()))
+}
+
+func TestIsUUIDv4LikeSFID(t *testing.T) {
+ sfid := "0014100000TdznWAAR"
+ assert.False(t, utils.IsUUIDv4(sfid), fmt.Sprintf("%s is not v4 UUID", sfid))
+}
+
+func TestIsSalesForceID(t *testing.T) {
+ trueTestData := []string{
+ "00117000015vpjX",
+ "00117000015vpjXAAQ",
+ }
+ falseTestData := []string{
+ "",
+ "00117",
+ "-00117",
+ "00117000015vpj-",
+ "0011700001-vpjXAAQ",
+ "0011700001?vpjXAAQ",
+ "0011700001&vpjXAAQ",
+ "0011700001_vpjXAAQ",
+ }
+
+ for i := range trueTestData {
+ assert.True(t, utils.IsSalesForceID(trueTestData[i]))
+ }
+ for i := range falseTestData {
+ assert.False(t, utils.IsSalesForceID(falseTestData[i]))
+ }
+}
diff --git a/cla-backend-go/userSubscribeLambda/main.go b/cla-backend-go/userSubscribeLambda/main.go
index 8d3730785..959026f76 100644
--- a/cla-backend-go/userSubscribeLambda/main.go
+++ b/cla-backend-go/userSubscribeLambda/main.go
@@ -9,6 +9,8 @@ import (
"os"
"runtime"
+ "github.com/go-openapi/strfmt"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
@@ -162,7 +164,7 @@ func Create(ctx context.Context, user event.Event) {
DateCreated: nowStr,
DateModified: nowStr,
Emails: emails,
- LfEmail: primaryEmail,
+ LfEmail: strfmt.Email(primaryEmail),
LfUsername: sfdcUserObject.Username,
Note: "Create via user-service event",
UserExternalID: sfdcUserObject.ID,
@@ -306,7 +308,7 @@ func createUserFromUpdatedModel(userModelUpdated *usersModels.UserUpdated) error
newUserModel := &models.User{
Emails: emails,
- LfEmail: primaryEmail,
+ LfEmail: strfmt.Email(primaryEmail),
LfUsername: sfdcUserObject.Username,
Note: "Update via user-service event",
UserExternalID: sfdcUserObject.ID,
diff --git a/cla-backend-go/users/handlers.go b/cla-backend-go/users/handlers.go
index ee0b84f28..3350a3391 100644
--- a/cla-backend-go/users/handlers.go
+++ b/cla-backend-go/users/handlers.go
@@ -6,6 +6,8 @@ package users
import (
"fmt"
+ "github.com/go-openapi/strfmt"
+
"github.com/sirupsen/logrus"
"github.com/communitybridge/easycla/cla-backend-go/events"
@@ -43,7 +45,7 @@ func Configure(api *operations.ClaAPI, service Service, eventsService events.Ser
}
newUser := &models.User{
- LfEmail: claUser.LFEmail,
+ LfEmail: strfmt.Email(claUser.LFEmail),
LfUsername: claUser.LFUsername,
Username: claUser.Name,
}
diff --git a/cla-backend-go/users/models.go b/cla-backend-go/users/models.go
index 30196061d..461c4944d 100644
--- a/cla-backend-go/users/models.go
+++ b/cla-backend-go/users/models.go
@@ -16,7 +16,9 @@ type DBUser struct {
Version string `json:"version"`
UserEmails []string `json:"user_emails"`
UserGithubID string `json:"user_github_id"`
- UserCompanyID string `json:"user_company_id"`
UserGithubUsername string `json:"user_github_username"`
+ UserGitlabID string `json:"user_gitlab_id"`
+ UserGitlabUsername string `json:"user_gitlab_username"`
+ UserCompanyID string `json:"user_company_id"`
Note string `json:"note"`
}
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 1d633f3c7..24123b118 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -9,6 +9,8 @@ import (
"strings"
"time"
+ "github.com/go-openapi/strfmt"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
@@ -39,7 +41,10 @@ type UserRepository interface {
GetUserByExternalID(userExternalID string) (*models.User, error)
GetUserByUserName(userName string, fullMatch bool) (*models.User, error)
GetUserByEmail(userEmail string) (*models.User, error)
+ GetUserByGitHubID(gitHubID string) (*models.User, error)
GetUserByGitHubUsername(gitHubUsername string) (*models.User, error)
+ GetUserByGitlabID(gitlabID string) (*models.User, error)
+ GetUserByGitlabUsername(gitlabUsername string) (*models.User, error)
SearchUsers(searchField string, searchTerm string, fullMatch bool) (*models.Users, error)
}
@@ -100,9 +105,21 @@ func (repo repository) CreateUser(user *models.User) (*models.User, error) {
}
}
+ if user.GitlabID != "" {
+ attributes["user_gitlab_id"] = &dynamodb.AttributeValue{
+ S: aws.String(user.GitlabID),
+ }
+ }
+
+ if user.GitlabUsername != "" {
+ attributes["user_gitlab_username"] = &dynamodb.AttributeValue{
+ S: aws.String(user.GitlabUsername),
+ }
+ }
+
if user.LfEmail != "" {
attributes["lf_email"] = &dynamodb.AttributeValue{
- S: aws.String(user.LfEmail),
+ S: aws.String(user.LfEmail.String()),
}
}
@@ -257,7 +274,7 @@ func (repo repository) Save(user *models.UserUpdate) (*models.User, error) {
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
updateExpression := "SET "
- if user.LfEmail != "" && oldUserModel.LfEmail != user.LfEmail {
+ if user.LfEmail != "" && oldUserModel.LfEmail.String() != user.LfEmail {
log.WithFields(f).Debugf("building query - adding lf_email: %s", user.LfEmail)
expressionAttributeNames["#E"] = aws.String("lf_email")
expressionAttributeValues[":e"] = &dynamodb.AttributeValue{S: aws.String(user.LfEmail)}
@@ -694,10 +711,64 @@ func (repo repository) GetUserByEmail(userEmail string) (*models.User, error) {
return convertDBUserModel(dbUserModels[0]), nil
}
+// GetUserByGitHubID fetches the user record by github ID
+func (repo repository) GetUserByGitHubID(gitHubID string) (*models.User, error) {
+ f := logrus.Fields{
+ "functionName": "users.repository.GetUserByGitHubID",
+ "gitHubID": gitHubID,
+ }
+ // This is the key we want to match
+ condition := expression.Key("user_github_id").Equal(expression.Value(gitHubID))
+
+ // These are the columns we want returned
+ projection := buildUserProjection()
+
+ // Use the nice builder to create the expression
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(projection).Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error building expression for user_github_id : %s, error: %v", gitHubID, err)
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ TableName: aws.String(repo.tableName),
+ IndexName: aws.String("github-id-index"),
+ }
+
+ // Make the DynamoDB Query API call
+ result, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving user by user_github_id: %s, error: %+v", gitHubID, err)
+ return nil, err
+ }
+
+ // The user model
+ var dbUserModels []DBUser
+
+ err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &dbUserModels)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling user record from database for user_github_id: %s, error: %+v", gitHubID, err)
+ return nil, err
+ }
+
+ if len(dbUserModels) == 0 {
+ return nil, errors.NotFound("user not found when searching by user_github_id: %s", gitHubID)
+ } else if len(dbUserModels) > 1 {
+ log.WithFields(f).WithError(err).Warnf("retrieved %d results for the user_github_id query when we should return 0 or 1", len(dbUserModels))
+ }
+
+ return convertDBUserModel(dbUserModels[0]), nil
+}
+
// GetUserByGitHubUsername fetches the user record by github username
func (repo repository) GetUserByGitHubUsername(gitHubUsername string) (*models.User, error) {
f := logrus.Fields{
- "functionName": "users.repository.GetUserByGitHubUsername",
+ "functionName": "users.repository.GetUserByGitlabUsername",
"gitHubUsername": gitHubUsername,
}
// This is the key we want to match
@@ -748,6 +819,114 @@ func (repo repository) GetUserByGitHubUsername(gitHubUsername string) (*models.U
return convertDBUserModel(dbUserModels[0]), nil
}
+// GetUserByGitlabID fetches the user record by gitlab ID
+func (repo repository) GetUserByGitlabID(gitlabID string) (*models.User, error) {
+ f := logrus.Fields{
+ "functionName": "users.repository.GetUserByGitlabID",
+ "gitlabID": gitlabID,
+ }
+ // This is the key we want to match
+ condition := expression.Key("user_gitlab_id").Equal(expression.Value(gitlabID))
+
+ // These are the columns we want returned
+ projection := buildUserProjection()
+
+ // Use the nice builder to create the expression
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(projection).Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error building expression for user_gitlab_id : %s, error: %v", gitlabID, err)
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ TableName: aws.String(repo.tableName),
+ IndexName: aws.String("gitlab-id-index"),
+ }
+
+ // Make the DynamoDB Query API call
+ result, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving user by user_gitlab_id: %s, error: %+v", gitlabID, err)
+ return nil, err
+ }
+
+ // The user model
+ var dbUserModels []DBUser
+
+ err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &dbUserModels)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling user record from database for user_gitlab_id: %s, error: %+v", gitlabID, err)
+ return nil, err
+ }
+
+ if len(dbUserModels) == 0 {
+ return nil, errors.NotFound("user not found when searching by user_gitlab_id: %s", gitlabID)
+ } else if len(dbUserModels) > 1 {
+ log.WithFields(f).WithError(err).Warnf("retrieved %d results for the user_gitlab_id query when we should return 0 or 1", len(dbUserModels))
+ }
+
+ return convertDBUserModel(dbUserModels[0]), nil
+}
+
+// GetUserByGitlabUsername fetches the user record by gitlab username
+func (repo repository) GetUserByGitlabUsername(gitlabUsername string) (*models.User, error) {
+ f := logrus.Fields{
+ "functionName": "users.repository.GetUserByGitlabUsername",
+ "gitlabUsername": gitlabUsername,
+ }
+ // This is the key we want to match
+ condition := expression.Key("user_gitlab_username").Equal(expression.Value(gitlabUsername))
+
+ // These are the columns we want returned
+ projection := buildUserProjection()
+
+ // Use the nice builder to create the expression
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(projection).Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error building expression for user_gitlab_username : %s, error: %v", gitlabUsername, err)
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ TableName: aws.String(repo.tableName),
+ IndexName: aws.String("gitlab-username-index"),
+ }
+
+ // Make the DynamoDB Query API call
+ result, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving user by user_gitlab_username: %s, error: %+v", gitlabUsername, err)
+ return nil, err
+ }
+
+ // The user model
+ var dbUserModels []DBUser
+
+ err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &dbUserModels)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling user record from database for user_gitlab_username: %s, error: %+v", gitlabUsername, err)
+ return nil, err
+ }
+
+ if len(dbUserModels) == 0 {
+ return nil, errors.NotFound("user not found when searching by user_gitlab_username: %s", gitlabUsername)
+ } else if len(dbUserModels) > 1 {
+ log.WithFields(f).WithError(err).Warnf("retrieved %d results for the user_gitlab_username query when we should return 0 or 1", len(dbUserModels))
+ }
+
+ return convertDBUserModel(dbUserModels[0]), nil
+}
+
func (repo repository) SearchUsers(searchField string, searchTerm string, fullMatch bool) (*models.Users, error) {
f := logrus.Fields{
"functionName": "users.repository.SearchUsers",
@@ -818,7 +997,6 @@ func (repo repository) SearchUsers(searchField string, searchTerm string, fullMa
users = append(users, userList...)
if results.LastEvaluatedKey["user_id"] != nil {
- //log.Debugf("LastEvaluatedKey: %+v", result.LastEvaluatedKey["signature_id"])
lastEvaluatedKey = *results.LastEvaluatedKey["user_id"].S
scanInput.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
"user_id": {
@@ -858,7 +1036,7 @@ func convertDBUserModel(user DBUser) *models.User {
UserID: user.UserID,
UserExternalID: user.UserExternalID,
Admin: user.Admin,
- LfEmail: user.LFEmail,
+ LfEmail: strfmt.Email(user.LFEmail),
LfUsername: user.LFUsername,
DateCreated: user.DateCreated,
DateModified: user.DateModified,
@@ -866,8 +1044,10 @@ func convertDBUserModel(user DBUser) *models.User {
Version: user.Version,
Emails: user.UserEmails,
GithubID: user.UserGithubID,
- CompanyID: user.UserCompanyID,
GithubUsername: user.UserGithubUsername,
+ GitlabID: user.UserGitlabID,
+ GitlabUsername: user.UserGitlabUsername,
+ CompanyID: user.UserCompanyID,
Note: user.Note,
}
}
@@ -885,6 +1065,8 @@ func buildUserProjection() expression.ProjectionBuilder {
expression.Name("user_emails"),
expression.Name("user_github_username"),
expression.Name("user_github_id"),
+ expression.Name("user_gitlab_username"),
+ expression.Name("user_gitlab_id"),
expression.Name("date_created"),
expression.Name("date_modified"),
expression.Name("version"),
diff --git a/cla-backend-go/users/service.go b/cla-backend-go/users/service.go
index 715519965..f2c2c0a20 100644
--- a/cla-backend-go/users/service.go
+++ b/cla-backend-go/users/service.go
@@ -20,7 +20,10 @@ type Service interface {
GetUserByLFUserName(lfUserName string) (*models.User, error)
GetUserByUserName(userName string, fullMatch bool) (*models.User, error)
GetUserByEmail(userEmail string) (*models.User, error)
- GetUserByGitHubUsername(gitHubUsername string) (*models.User, error)
+ GetUserByGitHubID(gitHubID string) (*models.User, error)
+ GetUserByGitHubUsername(gitlabUsername string) (*models.User, error)
+ GetUserByGitlabID(gitHubID string) (*models.User, error)
+ GetUserByGitlabUsername(gitlabUsername string) (*models.User, error)
SearchUsers(field string, searchTerm string, fullMatch bool) (*models.Users, error)
}
@@ -109,7 +112,7 @@ func (s service) GetUser(userID string) (*models.User, error) {
return s.repo.GetUser(userID)
}
-// GetuserByLFUserName returns the user record associated with the LF Username value
+// GetUserByLFUserName returns the user record associated with the LF Username value
func (s service) GetUserByLFUserName(lfUserName string) (*models.User, error) {
if lfUserName == "" {
return nil, errors.New("username is empty")
@@ -133,6 +136,14 @@ func (s service) GetUserByEmail(userEmail string) (*models.User, error) {
return s.repo.GetUserByEmail(userEmail)
}
+// GetUserByGitHubID fetches the user by GitHub ID
+func (s service) GetUserByGitHubID(gitHubID string) (*models.User, error) {
+ if gitHubID == "" {
+ return nil, errors.New("gitHubID is empty")
+ }
+ return s.repo.GetUserByGitHubID(gitHubID)
+}
+
// GetUserByGitHubUsername fetches the user by GitHub username
func (s service) GetUserByGitHubUsername(gitHubUsername string) (*models.User, error) {
if gitHubUsername == "" {
@@ -141,6 +152,22 @@ func (s service) GetUserByGitHubUsername(gitHubUsername string) (*models.User, e
return s.repo.GetUserByGitHubUsername(gitHubUsername)
}
+// GetUserByGitlabID fetches the user by Gitlab ID
+func (s service) GetUserByGitlabID(gitlabID string) (*models.User, error) {
+ if gitlabID == "" {
+ return nil, errors.New("gitlabID is empty")
+ }
+ return s.repo.GetUserByGitlabID(gitlabID)
+}
+
+// GetUserByGitlabUsername fetches the user by Gitlab username
+func (s service) GetUserByGitlabUsername(gitlabUsername string) (*models.User, error) {
+ if gitlabUsername == "" {
+ return nil, errors.New("gitlabUsername is empty")
+ }
+ return s.repo.GetUserByGitHubUsername(gitlabUsername)
+}
+
// SearchUsers attempts to locate the user by the searchField and searchTerm fields
func (s service) SearchUsers(searchField string, searchTerm string, fullMatch bool) (*models.Users, error) {
return s.repo.SearchUsers(searchField, searchTerm, fullMatch)
diff --git a/cla-backend-go/utils/cla_user.go b/cla-backend-go/utils/cla_user.go
index 7b5a462e5..3f9fe4543 100644
--- a/cla-backend-go/utils/cla_user.go
+++ b/cla-backend-go/utils/cla_user.go
@@ -29,7 +29,7 @@ func GetBestUsername(user *models.User) string {
// GetBestEmail is a helper function to return the best email address for the user model
func GetBestEmail(userModel *models.User) string {
if userModel.LfEmail != "" {
- return userModel.LfEmail
+ return userModel.LfEmail.String()
}
for _, email := range userModel.Emails {
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 03c9b233f..a95b42785 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -156,18 +156,24 @@ const EmailDomainCriteria = "Email Domain Criteria"
//EmailCriteria represents approvals based on email addresses
const EmailCriteria = "Email Criteria"
-//GitHubOrgCriteria represents approvals based on GH org membership
-const GitHubOrgCriteria = "GitHub Org Criteria"
-
//AddApprovals is an action for adding approvals
const AddApprovals = "AddApprovals"
//RemoveApprovals is an action for removing approvals
const RemoveApprovals = "RemoveApprovals"
-//GitHubUsernameCriteria represents criteria based on GH username
+//GitHubUsernameCriteria represents criteria based on GitHub username
const GitHubUsernameCriteria = "GitHubUsername"
+//GitHubOrgCriteria represents approvals based on GitHub org membership
+const GitHubOrgCriteria = "GitHub Org Criteria"
+
+//GitlabUsernameCriteria represents criteria based on gitlab username
+const GitlabUsernameCriteria = "GitHubUsername"
+
+//GitlabOrgCriteria represents approvals based on gitlab org group membership
+const GitlabOrgCriteria = "Gitlab Org Criteria"
+
// SignatureQueryDefaultAll the signature query default active value - A flag to indicate how a default signature
// query should return data - show only 'active' signatures or 'all' signatures when no other query signed/approved
// params are provided
diff --git a/cla-backend-go/utils/utils.go b/cla-backend-go/utils/utils.go
index 22a3e8e0c..9d0b82993 100644
--- a/cla-backend-go/utils/utils.go
+++ b/cla-backend-go/utils/utils.go
@@ -6,17 +6,13 @@ package utils
import (
"fmt"
"math"
- "regexp"
"strconv"
"strings"
"time"
- "unicode/utf8"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/sirupsen/logrus"
- "github.com/gofrs/uuid"
-
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
@@ -199,128 +195,3 @@ func SliceDifference(a, b []string) []string {
}
return diff
}
-
-// ValidEmail tests the specified email string, returns true if email is valid, returns false otherwise
-func ValidEmail(email string) bool {
- emailRegexp := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
- return emailRegexp.MatchString(strings.TrimSpace(email))
-}
-
-// ValidDomain tests the specified domain string, returns true if domain is valid, returns false otherwise
-func ValidDomain(domain string, allowWildcard bool) (string, bool) { // nolint
- domain = strings.TrimSpace(domain)
-
- switch {
- case len(domain) == 0:
- return "domain is empty", false
- case len(domain) > 255:
- return fmt.Sprintf("domain name length is %d, can't exceed 255", len(domain)), false
- }
- var l int
- for i := 0; i < len(domain); i++ {
- b := domain[i]
- if b == '.' {
- // check domain labels validity
- switch {
- case i == l:
- return fmt.Sprintf("invalid character '%c' at offset %d: label can't begin with a period", b, i), false
- case i-l > 63:
- return fmt.Sprintf("byte length of label '%s' is %d, can't exceed 63", domain[l:i], i-l), false
- case domain[l] == '-':
- return fmt.Sprintf("label '%s' at offset %d begins with a hyphen", domain[l:i], l), false
- case domain[i-1] == '-':
- return fmt.Sprintf("label '%s' at offset %d ends with a hyphen", domain[l:i], l), false
- }
- l = i + 1
- continue
- }
-
- // If wildcard domains are allowed, e.g. *.linuxfoundation.org
- if allowWildcard {
- // test label character validity, note: tests are ordered by decreasing validity frequency
- if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '*' || b >= 'A' && b <= 'Z') {
- // show the printable unicode character starting at byte offset i
- c, _ := utf8.DecodeRuneInString(domain[i:])
- if c == utf8.RuneError {
- return fmt.Sprintf("invalid character at offset %d", i), false
- }
- return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
- }
- } else {
- // test label character validity, note: tests are ordered by decreasing validity frequency
- if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b >= 'A' && b <= 'Z') {
- // show the printable unicode character starting at byte offset i
- c, _ := utf8.DecodeRuneInString(domain[i:])
- if c == utf8.RuneError {
- return fmt.Sprintf("invalid character at offset %d", i), false
- }
- return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
- }
- }
- }
-
- // check top level domain validity
- switch {
- case l == len(domain):
- return "missing top level domain, domain can't end with a period", false
- case len(domain)-l > 63:
- return fmt.Sprintf("byte length of top level domain '%s' is %d, can't exceed 63", domain[l:], len(domain)-l), false
- case domain[l] == '-':
- return fmt.Sprintf("top level domain '%s' at offset %d begins with a hyphen", domain[l:], l), false
- case domain[len(domain)-1] == '-':
- return fmt.Sprintf("top level domain '%s' at offset %d ends with a hyphen", domain[l:], l), false
- case domain[l] >= '0' && domain[l] <= '9':
- return fmt.Sprintf("top level domain '%s' at offset %d begins with a digit", domain[l:], l), false
- }
-
- return "", true
-}
-
-// ValidGitHubUsername tests the specified GitHub username string, returns true if valid, returns false otherwise
-func ValidGitHubUsername(githubUsername string) (string, bool) {
-
- if len(strings.TrimSpace(githubUsername)) <= 2 {
- return "github username must be 3 or more characters", false
- }
-
- // For now, we only allow alpha numeric values
- re := regexp.MustCompile("^[a-zA-Z0-9_-]*$")
- valid := re.MatchString(strings.TrimSpace(githubUsername))
- if !valid {
- return fmt.Sprintf("invalid GitHub username: %s", githubUsername), false
- }
-
- return "", true
-}
-
-// ValidGitHubOrg tests the specified GitHub Organization string, returns true if valid, returns false otherwise
-func ValidGitHubOrg(githubOrg string) (string, bool) {
-
- if len(strings.TrimSpace(githubOrg)) <= 2 {
- return "github organization must be 3 or more characters", false
- }
-
- re := regexp.MustCompile("^[a-zA-Z0-9._-]*$")
- valid := re.MatchString(strings.TrimSpace(githubOrg))
- if !valid {
- return fmt.Sprintf("invalid GitHub organization: %s", githubOrg), false
- }
-
- return "", true
-}
-
-// IsUUIDv4 returns true if the specified ID is in the UUIDv4 format, otherwise returns false
-func IsUUIDv4(id string) bool {
- value, err := uuid.FromString(id)
- if err != nil {
- return false
- }
-
- return value.Version() == uuid.V4
-}
-
-// IsSalesForceID returns true if the specified ID is a SalesForce formatted ID, otherwise returns false
-func IsSalesForceID(id string) bool {
- regExp := regexp.MustCompile("^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$")
- return regExp.MatchString(strings.TrimSpace(id))
-}
diff --git a/cla-backend-go/utils/validators.go b/cla-backend-go/utils/validators.go
new file mode 100644
index 000000000..a8a42312c
--- /dev/null
+++ b/cla-backend-go/utils/validators.go
@@ -0,0 +1,171 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/gofrs/uuid"
+)
+
+// ValidEmail tests the specified email string, returns true if email is valid, returns false otherwise
+func ValidEmail(email string) bool {
+ emailRegexp := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
+ return emailRegexp.MatchString(strings.TrimSpace(email))
+}
+
+// ValidDomain tests the specified domain string, returns true if domain is valid, returns false otherwise
+func ValidDomain(domain string, allowWildcard bool) (string, bool) { // nolint
+ domain = strings.TrimSpace(domain)
+
+ switch {
+ case len(domain) == 0:
+ return "domain is empty", false
+ case len(domain) > 255:
+ return fmt.Sprintf("domain name length is %d, can't exceed 255", len(domain)), false
+ }
+ var l int
+ for i := 0; i < len(domain); i++ {
+ b := domain[i]
+ if b == '.' {
+ // check domain labels validity
+ switch {
+ case i == l:
+ return fmt.Sprintf("invalid character '%c' at offset %d: label can't begin with a period", b, i), false
+ case i-l > 63:
+ return fmt.Sprintf("byte length of label '%s' is %d, can't exceed 63", domain[l:i], i-l), false
+ case domain[l] == '-':
+ return fmt.Sprintf("label '%s' at offset %d begins with a hyphen", domain[l:i], l), false
+ case domain[i-1] == '-':
+ return fmt.Sprintf("label '%s' at offset %d ends with a hyphen", domain[l:i], l), false
+ }
+ l = i + 1
+ continue
+ }
+
+ // If wildcard domains are allowed, e.g. *.linuxfoundation.org
+ if allowWildcard {
+ // test label character validity, note: tests are ordered by decreasing validity frequency
+ if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '*' || b >= 'A' && b <= 'Z') {
+ // show the printable unicode character starting at byte offset i
+ c, _ := utf8.DecodeRuneInString(domain[i:])
+ if c == utf8.RuneError {
+ return fmt.Sprintf("invalid character at offset %d", i), false
+ }
+ return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
+ }
+ } else {
+ // test label character validity, note: tests are ordered by decreasing validity frequency
+ if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b >= 'A' && b <= 'Z') {
+ // show the printable unicode character starting at byte offset i
+ c, _ := utf8.DecodeRuneInString(domain[i:])
+ if c == utf8.RuneError {
+ return fmt.Sprintf("invalid character at offset %d", i), false
+ }
+ return fmt.Sprintf("invalid character '%c' at offset %d", c, i), false
+ }
+ }
+ }
+
+ // check top level domain validity
+ switch {
+ case l == len(domain):
+ return "missing top level domain, domain can't end with a period", false
+ case len(domain)-l > 63:
+ return fmt.Sprintf("byte length of top level domain '%s' is %d, can't exceed 63", domain[l:], len(domain)-l), false
+ case domain[l] == '-':
+ return fmt.Sprintf("top level domain '%s' at offset %d begins with a hyphen", domain[l:], l), false
+ case domain[len(domain)-1] == '-':
+ return fmt.Sprintf("top level domain '%s' at offset %d ends with a hyphen", domain[l:], l), false
+ case domain[l] >= '0' && domain[l] <= '9':
+ return fmt.Sprintf("top level domain '%s' at offset %d begins with a digit", domain[l:], l), false
+ }
+
+ return "", true
+}
+
+// ValidGitHubUsername tests the specified GitHub username string, returns true if valid, returns false otherwise
+func ValidGitHubUsername(githubUsername string) (string, bool) {
+
+ if len(strings.TrimSpace(githubUsername)) <= 2 {
+ return "github username must be 3 or more characters", false
+ }
+
+ // For now, we only allow alphanumeric values
+ re := regexp.MustCompile("^[a-zA-Z0-9_-]*$")
+ valid := re.MatchString(strings.TrimSpace(githubUsername))
+ if !valid {
+ return fmt.Sprintf("invalid GitHub username: %s", githubUsername), false
+ }
+
+ return "", true
+}
+
+// ValidGitlabUsername tests the specified Gitlab username string, returns true if valid, returns false otherwise
+func ValidGitlabUsername(gitlabUsername string) (string, bool) {
+
+ if len(strings.TrimSpace(gitlabUsername)) <= 2 {
+ return "gitlab username must be 3 or more characters", false
+ }
+
+ // For now, we only allow alphanumeric values
+ re := regexp.MustCompile("^[a-zA-Z0-9_-]*$")
+ valid := re.MatchString(strings.TrimSpace(gitlabUsername))
+ if !valid {
+ return fmt.Sprintf("invalid Gitlab username: %s", gitlabUsername), false
+ }
+
+ return "", true
+}
+
+// ValidGitHubOrg tests the specified GitHub Organization string, returns true if valid, returns false otherwise
+func ValidGitHubOrg(githubOrg string) (string, bool) {
+
+ if len(strings.TrimSpace(githubOrg)) <= 2 {
+ return "github organization must be 3 or more characters", false
+ }
+
+ re := regexp.MustCompile("^[a-zA-Z0-9._-]*$")
+ valid := re.MatchString(strings.TrimSpace(githubOrg))
+ if !valid {
+ return fmt.Sprintf("invalid GitHub organization: %s", githubOrg), false
+ }
+
+ return "", true
+}
+
+// ValidGitlabOrg tests the specified Gitlab Organization string, returns true if valid, returns false otherwise
+func ValidGitlabOrg(gitlabOrg string) (string, bool) {
+
+ if len(strings.TrimSpace(gitlabOrg)) <= 2 {
+ return "gitlab organization must be 3 or more characters", false
+ }
+
+ re := regexp.MustCompile("^[a-zA-Z0-9._-]*$")
+ valid := re.MatchString(strings.TrimSpace(gitlabOrg))
+ if !valid {
+ return fmt.Sprintf("invalid Gitlab organization: %s", gitlabOrg), false
+ }
+
+ return "", true
+}
+
+// IsUUIDv4 returns true if the specified ID is in the UUIDv4 format, otherwise returns false
+func IsUUIDv4(id string) bool {
+ value, err := uuid.FromString(id)
+ if err != nil {
+ return false
+ }
+
+ return value.Version() == uuid.V4
+}
+
+// IsSalesForceID returns true if the specified ID is a SalesForce formatted ID, otherwise returns false
+func IsSalesForceID(id string) bool {
+ regExp := regexp.MustCompile("^[a-zA-Z0-9]{18}|[a-zA-Z0-9]{15}$")
+ return regExp.MatchString(strings.TrimSpace(id))
+}
diff --git a/cla-backend-go/v2/cla_manager/service.go b/cla-backend-go/v2/cla_manager/service.go
index 1cd59dbf1..505056e73 100644
--- a/cla-backend-go/v2/cla_manager/service.go
+++ b/cla-backend-go/v2/cla_manager/service.go
@@ -204,7 +204,7 @@ func (s *service) CreateCLAManager(ctx context.Context, authUser *auth.User, cla
claUserModel := &v1Models.User{
CompanyID: v1CompanyModel.CompanyID,
UserExternalID: v1CompanyModel.CompanyExternalID,
- LfEmail: *user.Emails[0].EmailAddress,
+ LfEmail: strfmt.Email(*user.Emails[0].EmailAddress),
Admin: true,
LfUsername: user.Username,
DateCreated: currentTimeString,
diff --git a/cla-backend-go/v2/company/service.go b/cla-backend-go/v2/company/service.go
index 127403fba..7d958b663 100644
--- a/cla-backend-go/v2/company/service.go
+++ b/cla-backend-go/v2/company/service.go
@@ -1128,7 +1128,7 @@ func (s *service) GetCompanyCLAGroupManagers(ctx context.Context, companyID, cla
// DB doesn't have approved_on value - just use sig created date/time
ApprovedOn: sigModel.SignatureCreated,
LfUsername: user.LfUsername,
- Email: strfmt.Email(user.LfEmail),
+ Email: user.LfEmail,
Name: user.Username,
UserSfid: user.UserExternalID,
ProjectID: sigModel.ProjectID,
diff --git a/cla-backend-go/v2/dynamo_events/cla_manager.go b/cla-backend-go/v2/dynamo_events/cla_manager.go
index bc17275c9..519e9d849 100644
--- a/cla-backend-go/v2/dynamo_events/cla_manager.go
+++ b/cla-backend-go/v2/dynamo_events/cla_manager.go
@@ -67,7 +67,7 @@ func (s *service) SetInitialCLAManagerACSPermissions(ctx context.Context, signat
log.WithFields(f).Debugf("searching user by email: %s", sig.SignatureACL[0].LfEmail)
if sig.SignatureACL[0].LfEmail != "" {
- claManager, err = userServiceClient.SearchUserByEmail(sig.SignatureACL[0].LfEmail)
+ claManager, err = userServiceClient.SearchUserByEmail(sig.SignatureACL[0].LfEmail.String())
if err != nil || claManager == nil {
log.WithFields(f).Warnf("unable to lookup user by email: %s, error: %+v",
sig.SignatureACL[0].LfEmail, err)
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index fc9791eff..819bd85ea 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -108,15 +108,11 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", params.CompanyID, err)
log.Warn(msg)
if _, ok := err.(*utils.CompanyNotFound); ok {
- return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
- Code: "404",
- })
+ return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("company not found - unable to locate company by ID: %s", params.CompanyID), err))
}
- return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
- Code: "400",
- })
+ return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, fmt.Sprintf("unable to locate company by ID: %s", params.CompanyID), err))
}
// Must be in the Project|Organization Scope to see this - signature ACL is double-checked in the service level when the signature is loaded
diff --git a/cla-backend-go/v2/signatures/validators.go b/cla-backend-go/v2/signatures/validators.go
index 47ba14750..862abcf66 100644
--- a/cla-backend-go/v2/signatures/validators.go
+++ b/cla-backend-go/v2/signatures/validators.go
@@ -31,7 +31,9 @@ func hasApprovalListUpdates(params signatures.UpdateApprovalListParams) bool {
if len(params.Body.AddEmailApprovalList) > 0 || len(params.Body.RemoveEmailApprovalList) > 0 ||
len(params.Body.AddDomainApprovalList) > 0 || len(params.Body.RemoveDomainApprovalList) > 0 ||
len(params.Body.AddGithubUsernameApprovalList) > 0 || len(params.Body.RemoveGithubUsernameApprovalList) > 0 ||
- len(params.Body.AddGithubOrgApprovalList) > 0 || len(params.Body.RemoveGithubOrgApprovalList) > 0 {
+ len(params.Body.AddGithubOrgApprovalList) > 0 || len(params.Body.RemoveGithubOrgApprovalList) > 0 ||
+ len(params.Body.AddGitlabUsernameApprovalList) > 0 || len(params.Body.RemoveGitlabUsernameApprovalList) > 0 ||
+ len(params.Body.AddGitlabOrgApprovalList) > 0 || len(params.Body.RemoveGitlabOrgApprovalList) > 0 {
return true
}
@@ -72,7 +74,7 @@ func entriesAreValid(params signatures.UpdateApprovalListParams) (string, bool)
}
}
- // Ensure the github usernames are valid
+ // Ensure the GitHub usernames are valid
for _, githubUsername := range params.Body.AddGithubUsernameApprovalList {
msg, valid := utils.ValidGitHubUsername(githubUsername)
if !valid {
@@ -88,7 +90,7 @@ func entriesAreValid(params signatures.UpdateApprovalListParams) (string, bool)
}
}
- // Ensure the github Organization values are valid
+ // Ensure the GitHub Organization values are valid
for _, githubOrg := range params.Body.AddGithubOrgApprovalList {
msg, valid := utils.ValidGitHubOrg(githubOrg)
if !valid {
@@ -104,5 +106,37 @@ func entriesAreValid(params signatures.UpdateApprovalListParams) (string, bool)
}
}
+ // Ensure the Gitlab usernames are valid
+ for _, githubUsername := range params.Body.AddGitlabUsernameApprovalList {
+ msg, valid := utils.ValidGitlabUsername(githubUsername)
+ if !valid {
+ isValid = false
+ listOfErrors = append(listOfErrors, fmt.Sprintf("invalid add approval list Gitlab Username %s - %s", githubUsername, msg))
+ }
+ }
+ for _, githubUsername := range params.Body.RemoveGitlabUsernameApprovalList {
+ msg, valid := utils.ValidGitlabUsername(githubUsername)
+ if !valid {
+ isValid = false
+ listOfErrors = append(listOfErrors, fmt.Sprintf("invalid remove approval list Gitlab Username %s - %s", githubUsername, msg))
+ }
+ }
+
+ // Ensure the Gitlab Organization values are valid
+ for _, githubOrg := range params.Body.AddGitlabOrgApprovalList {
+ msg, valid := utils.ValidGitlabOrg(githubOrg)
+ if !valid {
+ isValid = false
+ listOfErrors = append(listOfErrors, fmt.Sprintf("invalid add approval list Gitlab Org %s - %s", githubOrg, msg))
+ }
+ }
+ for _, githubOrg := range params.Body.RemoveGitlabOrgApprovalList {
+ msg, valid := utils.ValidGitlabOrg(githubOrg)
+ if !valid {
+ isValid = false
+ listOfErrors = append(listOfErrors, fmt.Sprintf("invalid remove approval list Gitlab Org %s - %s", githubOrg, msg))
+ }
+ }
+
return strings.Join(listOfErrors, ", "), isValid
}
diff --git a/cla-backend-go/v2/user-service/client.go b/cla-backend-go/v2/user-service/client.go
index 45b95930f..d6b494632 100644
--- a/cla-backend-go/v2/user-service/client.go
+++ b/cla-backend-go/v2/user-service/client.go
@@ -28,8 +28,8 @@ import (
"github.com/go-openapi/strfmt"
)
-// errors
var (
+ // ErrUserNotFound is an error for users not found
ErrUserNotFound = errors.New("user not found")
)
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index b216dcdc0..d35d1d310 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -230,8 +230,10 @@ provider:
Resource:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-ccla-whitelist-requests/index/company-id-project-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-ccla-whitelist-requests/index/ccla-approval-list-request-project-id-index"
- - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-user-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-username-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/gitlab-id-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/gitlab-username-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/github-user-external-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/lf-username-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users/index/lf-email-index"
From 2f1f45c9d5f155959039e4a2af5682eb84f906e7 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 29 Jul 2021 04:36:31 +0300
Subject: [PATCH 0374/1276] [Snyk] Security upgrade url-parse from 1.5.0 to
1.5.2 (#3072)
---
cla-frontend-project-console/src/package.json | 2 +-
cla-frontend-project-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-project-console/src/package.json b/cla-frontend-project-console/src/package.json
index 5fecf5de1..5e690d08e 100644
--- a/cla-frontend-project-console/src/package.json
+++ b/cla-frontend-project-console/src/package.json
@@ -54,7 +54,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.5.0",
+ "url-parse": "^1.5.2",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index ad1b1bdba..6fe68d0dd 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -4197,10 +4197,10 @@ url-join@^4.0.1:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
-url-parse@^1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
- integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
+url-parse@^1.5.2:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From 01c2a3406cad0778ee616a23d780151cd5535430 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 29 Jul 2021 05:08:24 +0300
Subject: [PATCH 0375/1276] [Snyk] Security upgrade url-parse from 1.4.7 to
1.5.2 (#3067)
---
cla-landing-page/package.json | 2 +-
cla-landing-page/yarn.lock | 10 +++++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/cla-landing-page/package.json b/cla-landing-page/package.json
index 6966dabe5..e19fa14e6 100644
--- a/cla-landing-page/package.json
+++ b/cla-landing-page/package.json
@@ -64,7 +64,7 @@
"serverless-plugin-tracing": "^2.0.0",
"serverless-pseudo-parameters": "^2.5.0",
"tslib": "^1.10.0",
- "url-parse": "^1.4.7",
+ "url-parse": "^1.5.2",
"zone.js": "~0.10.2"
},
"devDependencies": {
diff --git a/cla-landing-page/yarn.lock b/cla-landing-page/yarn.lock
index 06b3c8a96..59f47ffde 100644
--- a/cla-landing-page/yarn.lock
+++ b/cla-landing-page/yarn.lock
@@ -11909,7 +11909,7 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"
-url-parse@^1.4.3, url-parse@^1.4.7:
+url-parse@^1.4.3:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
@@ -11917,6 +11917,14 @@ url-parse@^1.4.3, url-parse@^1.4.7:
querystringify "^2.1.1"
requires-port "^1.0.0"
+url-parse@^1.5.2:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
From 0b82ed7f49c53b8d052299113162fa3a47c74d34 Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 29 Jul 2021 05:21:27 +0300
Subject: [PATCH 0376/1276] [Snyk] Security upgrade url-parse from 1.5.0 to
1.5.2 (#3057)
---
cla-frontend-contributor-console/src/package.json | 2 +-
cla-frontend-contributor-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-contributor-console/src/package.json b/cla-frontend-contributor-console/src/package.json
index b8d825f9f..db7f27eab 100644
--- a/cla-frontend-contributor-console/src/package.json
+++ b/cla-frontend-contributor-console/src/package.json
@@ -52,7 +52,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.5.0",
+ "url-parse": "^1.5.2",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index f40734a66..e1fc3cdc2 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -4043,10 +4043,10 @@ url-join@^4.0.1:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
-url-parse@^1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
- integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
+url-parse@^1.5.2:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From c5ef980ec53e5626b70b225220180d3b460e08be Mon Sep 17 00:00:00 2001
From: Snyk bot
Date: Thu, 29 Jul 2021 06:01:40 +0300
Subject: [PATCH 0377/1276] fix:
cla-frontend-corporate-console/src/package.json &
cla-frontend-corporate-console/src/yarn.lock to reduce vulnerabilities
(#3058)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-URLPARSE-1533425
---
cla-frontend-corporate-console/src/package.json | 2 +-
cla-frontend-corporate-console/src/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cla-frontend-corporate-console/src/package.json b/cla-frontend-corporate-console/src/package.json
index d6c33e8a6..407ed94e9 100644
--- a/cla-frontend-corporate-console/src/package.json
+++ b/cla-frontend-corporate-console/src/package.json
@@ -50,7 +50,7 @@
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"timsort": "^0.3.0",
- "url-parse": "^1.5.0",
+ "url-parse": "^1.5.2",
"zone.js": "0.8.18"
},
"devDependencies": {
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 9e2359380..111cea165 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -4053,10 +4053,10 @@ urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
-url-parse@^1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.0.tgz#90aba6c902aeb2d80eac17b91131c27665d5d828"
- integrity sha512-9iT6N4s93SMfzunOyDPe4vo4nLcSu1yq0IQK1gURmjm8tQNlM6loiuCRrKG1hHGXfB2EWd6H4cGi7tGdaygMFw==
+url-parse@^1.5.2:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
From 14085e83a43a05194aa6e10ea0821933408ff18d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 30 Jul 2021 09:13:57 -0700
Subject: [PATCH 0378/1276] Added Additional Debug for GitHub comments (#3084)
Signed-off-by: David Deal
---
cla-backend/cla/models/github_models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index 6101367b1..1165b56eb 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -322,7 +322,8 @@ def process_easycla_command_comment(self, data):
raise ValueError("missing comment body, ignoring the message")
if "/easycla" not in comment_str.split():
- raise ValueError("unsupported comment supplied, currently only /easycla command is supported")
+ raise ValueError(f'unsupported comment supplied: {comment_str.split()}, '
+ 'currently only the \'/easycla\' command is supported')
github_repository_id = data.get('repository', {}).get('id', None)
if not github_repository_id:
From e59b808480c1d53021834216269bdcce4fde824f Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 30 Jul 2021 11:59:39 -0700
Subject: [PATCH 0379/1276] Updated Project Parent Logic in Python (#3085)
- Updated is stanalone and parent checks for SF projects
Signed-off-by: David Deal
---
cla-backend/cla/config.py | 3 +++
cla-backend/cla/project_service.py | 30 +++++++++++++++++++++++-------
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/cla-backend/cla/config.py b/cla-backend/cla/config.py
index 8208f019d..0a58daeba 100644
--- a/cla-backend/cla/config.py
+++ b/cla-backend/cla/config.py
@@ -52,6 +52,9 @@ def get_ssm_key(region, key):
# The linux foundation is the parent for many SF projects
THE_LINUX_FOUNDATION = 'The Linux Foundation'
+# LF Projects LLC is the parent for many SF projects
+LF_PROJECTS_LLC = 'LF Projects, LLC'
+
# Base URL used for callbacks and OAuth2 redirects.
API_BASE_URL = os.environ.get('CLA_API_BASE', '')
diff --git a/cla-backend/cla/project_service.py b/cla-backend/cla/project_service.py
index dd9fa3895..9a012cb74 100644
--- a/cla-backend/cla/project_service.py
+++ b/cla-backend/cla/project_service.py
@@ -3,12 +3,13 @@
import datetime
import json
import os
+from typing import Optional
import requests
import cla
from cla import log
-from cla.config import THE_LINUX_FOUNDATION
+from cla.config import THE_LINUX_FOUNDATION, LF_PROJECTS_LLC
STAGE = os.environ.get('STAGE', '')
REGION = 'us-east-1'
@@ -35,9 +36,12 @@ def is_standalone(self, project_sfid) -> bool:
"""
project = self.get_project_by_id(project_sfid)
if project:
- parent_sf_id = project.get('Parent', None)
- if not self.has_parent(project) and (parent_sf_id is None or parent_sf_id == THE_LINUX_FOUNDATION):
+ parent_name = self.get_parent_name(project)
+ if parent_name is None or (parent_name == THE_LINUX_FOUNDATION or parent_name == LF_PROJECTS_LLC) \
+ and not project.get('Projects'):
return True
+ else:
+ return False
return False
def is_lf_supported(self, project_sfid) -> bool:
@@ -50,9 +54,10 @@ def is_lf_supported(self, project_sfid) -> bool:
"""
project = self.get_project_by_id(project_sfid)
if project:
+ parent_name = self.get_parent_name(project)
return (project.get('Funding', None) == 'Unfunded' or
project.get('Funding', None) == 'Supported By Parent') and \
- project.get('Parent', None) == THE_LINUX_FOUNDATION
+ (parent_name == THE_LINUX_FOUNDATION or parent_name == LF_PROJECTS_LLC)
return False
def has_parent(self, project) -> bool:
@@ -60,14 +65,25 @@ def has_parent(self, project) -> bool:
fn = 'project_service.has_parent'
try:
log.info(f"{fn} - Checking if {project['Name']} has parent project")
- parent = project['Parent']
- if parent:
+ if project and project['Foundation']['ID'] != '' and project['Foundation']['Name'] != '':
return True
except KeyError as err:
- log.debug(f"{fn} - Failed to find parent for {project['Name']} , error: {err}")
+ log.debug(f"{fn} - Failed to find parent for {project['Name']}, error: {err}")
return False
return False
+ def get_parent_name(self, project) -> Optional[str]:
+ """ returns the project parent name if exists, otherwise returns None """
+ fn = 'project_service.get_parent_name'
+ try:
+ log.info(f"{fn} - Checking if {project['Name']} has parent project")
+ if project and project['Foundation']['ID'] != '' and project['Foundation']['Name'] != '':
+ return project['Foundation']['Name']
+ except KeyError as err:
+ log.debug(f"{fn} - Failed to find parent for {project['Name']}, error: {err}")
+ return None
+ return None
+
def is_parent(self, project) -> bool:
"""
checks whether salesforce project is a parent
From 74863a69c25b66fea710dcb0fc8bff69f0dea726 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 30 Jul 2021 14:11:01 -0700
Subject: [PATCH 0380/1276] Resolved GitHub User Index Name Issue (#3086)
Signed-off-by: David Deal
---
cla-backend/cla/models/dynamo_models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index f50f85ff6..9c34d1897 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -95,7 +95,7 @@ class GitHubUserIndex(GlobalSecondaryIndex):
class Meta:
"""Meta class for GitHub User index."""
- index_name = "github-user-index"
+ index_name = "github-username-index"
write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
# All attributes are projected - not sure if this is necessary.
From 8990757900cf7e7241ca6f5314934be7467400dd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 30 Jul 2021 14:59:20 -0700
Subject: [PATCH 0381/1276] Resolved User Index Issue (#3087)
Signed-off-by: David Deal
---
cla-backend/cla/models/dynamo_models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 9c34d1897..3afc741b1 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -95,7 +95,7 @@ class GitHubUserIndex(GlobalSecondaryIndex):
class Meta:
"""Meta class for GitHub User index."""
- index_name = "github-username-index"
+ index_name = "github-id-index"
write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
# All attributes are projected - not sure if this is necessary.
From 73ae04b30e3e55d257edc1ddf06b82f4a83483da Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Mon, 2 Aug 2021 18:24:16 +0300
Subject: [PATCH 0382/1276] Feature/Ecla username update (#3089)
---
cla-backend-go/docraptor/client.go | 1 +
cla-backend-go/signatures/repository.go | 12 +++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/docraptor/client.go b/cla-backend-go/docraptor/client.go
index 16a0ed202..7b8503a54 100644
--- a/cla-backend-go/docraptor/client.go
+++ b/cla-backend-go/docraptor/client.go
@@ -72,5 +72,6 @@ func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
log.WithFields(f).WithError(err).Warn("problem with API call to docraptor")
return nil, err
}
+ defer resp.Body.Close()
return resp.Body, nil
}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 777a195e2..fc0e05047 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -3462,11 +3462,21 @@ func (repo repository) GetClaGroupCorporateContributors(ctx context.Context, cla
}
signatureVersion := fmt.Sprintf("v%s.%s", sig.SignatureDocumentMajorVersion, sig.SignatureDocumentMinorVersion)
+
+ sigName := sig.UserName
+ user, userErr := repo.usersRepo.GetUser(sig.SignatureReferenceID)
+ if userErr != nil {
+ log.WithFields(f).Warnf("unable to get user for id: %s, error: %v ", sig.SignatureReferenceID, userErr)
+ }
+ if user != nil && sigName == "" {
+ sigName = user.Username
+ }
+
out.List = append(out.List, &models.CorporateContributor{
SignatureID: sig.SignatureID,
GithubID: sig.UserGithubUsername,
LinuxFoundationID: sig.UserLFUsername,
- Name: sig.UserName,
+ Name: sigName,
SignatureVersion: signatureVersion,
Email: sig.UserEmail,
Timestamp: sigCreatedTime,
From e33f2d3a78280f499791d43db935d480a4f0f5a1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 2 Aug 2021 08:44:26 -0700
Subject: [PATCH 0383/1276] Forced GitHub ID to be Int (#3091)
- cast github ID to an integer value for the index query call -
previously, string was accepted, but after the index update it now only
accepts an integer value
Signed-off-by: David Deal
---
cla-backend-go/go.mod | 2 ++
cla-backend-go/go.sum | 6 ++++++
cla-backend-go/users/repository.go | 16 ++++++++--------
cla-backend-go/users/service.go | 7 ++-----
cla-backend/cla/models/dynamo_models.py | 4 ++--
5 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index d584c184d..c93e34994 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -45,7 +45,9 @@ require (
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mitchellh/mapstructure v1.4.1
github.com/mozillazg/request v0.8.0 // indirect
+ github.com/myitcv/gobin v0.0.14 // indirect
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc
+ github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 23f30f8c3..bf9282154 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -607,6 +607,8 @@ github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9r
github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/myitcv/gobin v0.0.14 h1:YkTUz0IeRspEJlly/+AXRBMA3GN7ArRVbsLJ1uYFwRk=
+github.com/myitcv/gobin v0.0.14/go.mod h1:GvHEiYCWroKI2KrMT+xQkHC3FC551wigVWeR4Sgg5P4=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
@@ -628,6 +630,7 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+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=
@@ -660,6 +663,9 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 24123b118..350e98781 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -43,7 +43,7 @@ type UserRepository interface {
GetUserByEmail(userEmail string) (*models.User, error)
GetUserByGitHubID(gitHubID string) (*models.User, error)
GetUserByGitHubUsername(gitHubUsername string) (*models.User, error)
- GetUserByGitlabID(gitlabID string) (*models.User, error)
+ GetUserByGitlabID(gitlabID int) (*models.User, error)
GetUserByGitlabUsername(gitlabUsername string) (*models.User, error)
SearchUsers(searchField string, searchTerm string, fullMatch bool) (*models.Users, error)
}
@@ -444,7 +444,7 @@ func (repo repository) GetUser(userID string) (*models.User, error) {
return convertDBUserModel(dbUserModels[0]), nil
}
-// GetuserByLFUserName returns the user record associated with the LF Username value
+// GetUserByLFUserName returns the user record associated with the LF Username value
func (repo repository) GetUserByLFUserName(lfUserName string) (*models.User, error) {
f := logrus.Fields{
"functionName": "users.repository.GetUserByLFUserName",
@@ -564,7 +564,7 @@ func (repo repository) GetUserByUserName(userName string, fullMatch bool) (*mode
var condition expression.KeyConditionBuilder
if strings.Contains(userName, "github:") {
- indexName = "github-user-index"
+ indexName = "github-id-index"
// Username for GitHub comes in as github:123456, so we want to remove the initial string
githubID, err := strconv.Atoi(strings.Replace(userName, "github:", "", 1))
if err != nil {
@@ -768,7 +768,7 @@ func (repo repository) GetUserByGitHubID(gitHubID string) (*models.User, error)
// GetUserByGitHubUsername fetches the user record by github username
func (repo repository) GetUserByGitHubUsername(gitHubUsername string) (*models.User, error) {
f := logrus.Fields{
- "functionName": "users.repository.GetUserByGitlabUsername",
+ "functionName": "users.repository.GetUserByGitHubUsername",
"gitHubUsername": gitHubUsername,
}
// This is the key we want to match
@@ -820,7 +820,7 @@ func (repo repository) GetUserByGitHubUsername(gitHubUsername string) (*models.U
}
// GetUserByGitlabID fetches the user record by gitlab ID
-func (repo repository) GetUserByGitlabID(gitlabID string) (*models.User, error) {
+func (repo repository) GetUserByGitlabID(gitlabID int) (*models.User, error) {
f := logrus.Fields{
"functionName": "users.repository.GetUserByGitlabID",
"gitlabID": gitlabID,
@@ -834,7 +834,7 @@ func (repo repository) GetUserByGitlabID(gitlabID string) (*models.User, error)
// Use the nice builder to create the expression
expr, err := expression.NewBuilder().WithKeyCondition(condition).WithProjection(projection).Build()
if err != nil {
- log.WithFields(f).WithError(err).Warnf("error building expression for user_gitlab_id : %s, error: %v", gitlabID, err)
+ log.WithFields(f).WithError(err).Warnf("error building expression for user_gitlab_id : %d, error: %v", gitlabID, err)
return nil, err
}
@@ -851,7 +851,7 @@ func (repo repository) GetUserByGitlabID(gitlabID string) (*models.User, error)
// Make the DynamoDB Query API call
result, err := repo.dynamoDBClient.Query(queryInput)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("error retrieving user by user_gitlab_id: %s, error: %+v", gitlabID, err)
+ log.WithFields(f).WithError(err).Warnf("error retrieving user by user_gitlab_id: %d, error: %+v", gitlabID, err)
return nil, err
}
@@ -860,7 +860,7 @@ func (repo repository) GetUserByGitlabID(gitlabID string) (*models.User, error)
err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &dbUserModels)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("error unmarshalling user record from database for user_gitlab_id: %s, error: %+v", gitlabID, err)
+ log.WithFields(f).WithError(err).Warnf("error unmarshalling user record from database for user_gitlab_id: %d, error: %+v", gitlabID, err)
return nil, err
}
diff --git a/cla-backend-go/users/service.go b/cla-backend-go/users/service.go
index f2c2c0a20..b245955c1 100644
--- a/cla-backend-go/users/service.go
+++ b/cla-backend-go/users/service.go
@@ -22,7 +22,7 @@ type Service interface {
GetUserByEmail(userEmail string) (*models.User, error)
GetUserByGitHubID(gitHubID string) (*models.User, error)
GetUserByGitHubUsername(gitlabUsername string) (*models.User, error)
- GetUserByGitlabID(gitHubID string) (*models.User, error)
+ GetUserByGitlabID(gitHubID int) (*models.User, error)
GetUserByGitlabUsername(gitlabUsername string) (*models.User, error)
SearchUsers(field string, searchTerm string, fullMatch bool) (*models.Users, error)
}
@@ -153,10 +153,7 @@ func (s service) GetUserByGitHubUsername(gitHubUsername string) (*models.User, e
}
// GetUserByGitlabID fetches the user by Gitlab ID
-func (s service) GetUserByGitlabID(gitlabID string) (*models.User, error) {
- if gitlabID == "" {
- return nil, errors.New("gitlabID is empty")
- }
+func (s service) GetUserByGitlabID(gitlabID int) (*models.User, error) {
return s.repo.GetUserByGitlabID(gitlabID)
}
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 3afc741b1..2b83cdaad 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -1735,13 +1735,13 @@ def get_user_by_email(self, user_email) -> Optional[List[User]]:
else:
return None
- def get_user_by_github_id(self, user_github_id) -> Optional[List[User]]:
+ def get_user_by_github_id(self, user_github_id: int) -> Optional[List[User]]:
if user_github_id is None:
cla.log.warning("Unable to lookup user by github id - id is empty")
return None
users = []
- for user_model in self.model.user_github_id_index.query(user_github_id):
+ for user_model in self.model.user_github_id_index.query(int(user_github_id)):
user = User()
user.model = user_model
users.append(user)
From 00eb1f5bda71c9919ad37a748a77e160e78c3329 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 2 Aug 2021 19:39:47 -0700
Subject: [PATCH 0384/1276] Added Additional Error Output for Approval List
Errors (#3098)
- Added error details to approval list errors
Signed-off-by: David Deal
---
cla-backend-go/v2/signatures/handlers.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 819bd85ea..0abcc1592 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -162,10 +162,10 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
if updateErr != nil || updatedSig == nil {
msg := fmt.Sprintf("unable to update signature approval list using CLA Group ID: %s", params.ClaGroupID)
log.WithFields(f).Warn(msg)
- if err, ok := err.(*signatureService.ForbiddenError); ok {
- return signatures.NewUpdateApprovalListForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbiddenWithError(reqID, msg, err))
+ if _, ok := err.(*signatureService.ForbiddenError); ok {
+ return signatures.NewUpdateApprovalListForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbiddenWithError(reqID, msg, updateErr))
}
- return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ return signatures.NewUpdateApprovalListBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, updateErr))
}
// Convert the v1 output model to a v2 response model
From 3d734e7d6560c0fbf34ad352cfe54c9fd55018ed Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 2 Aug 2021 19:55:03 -0700
Subject: [PATCH 0385/1276] Optimize Go Build (#3099)
- updated circleci configuration to build go backend once
Signed-off-by: David Deal
---
.circleci/config.yml | 80 ++++++++++++++++++++++----------------------
1 file changed, 40 insertions(+), 40 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3ffb4c24c..b325f41a9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -280,7 +280,7 @@ jobs:
- cla-backend-go/zipbuilder-lambda
- cla-backend-go/functional-tests
- buildGoBackendDev:
+ buildGoBackendCommon:
<<: *buildGoBackendAnchor
environment:
STAGE: dev
@@ -290,25 +290,25 @@ jobs:
AWS_REGION: us-east-1
DYNAMODB_AWS_REGION: us-east-1
- buildGoBackendStaging:
- <<: *buildGoBackendAnchor
- environment:
- STAGE: staging
- AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_STAGING
- AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_STAGING
- AWS_PROFILE: lf-cla
- AWS_REGION: us-east-1
- DYNAMODB_AWS_REGION: us-east-1
-
- buildGoBackendProd:
- <<: *buildGoBackendAnchor
- environment:
- STAGE: prod
- AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_PROD
- AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_PROD
- AWS_PROFILE: lf-cla
- AWS_REGION: us-east-1
- DYNAMODB_AWS_REGION: us-east-1
+# buildGoBackendStaging:
+# <<: *buildGoBackendAnchor
+# environment:
+# STAGE: staging
+# AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_STAGING
+# AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_STAGING
+# AWS_PROFILE: lf-cla
+# AWS_REGION: us-east-1
+# DYNAMODB_AWS_REGION: us-east-1
+#
+# buildGoBackendProd:
+# <<: *buildGoBackendAnchor
+# environment:
+# STAGE: prod
+# AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_PROD
+# AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_PROD
+# AWS_PROFILE: lf-cla
+# AWS_REGION: us-east-1
+# DYNAMODB_AWS_REGION: us-east-1
# Deploys
deployBackend: &deployBackendAnchor
@@ -971,7 +971,7 @@ workflows:
filters:
tags:
only: /.*/
- - buildGoBackendDev:
+ - buildGoBackendCommon:
filters:
tags:
only: /.*/
@@ -992,7 +992,7 @@ workflows:
- deployBackendDev:
requires:
- buildBackendDev
- - buildGoBackendDev
+ - buildGoBackendCommon
filters:
tags:
ignore: /.*/
@@ -1054,18 +1054,18 @@ workflows:
tags:
# see semver examples https://regex101.com/r/Ly7O1x/201/
only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
- - buildGoBackendStaging:
- filters:
- branches:
- ignore: /.*/
- tags:
- # see semver examples https://regex101.com/r/Ly7O1x/201/
- only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
+# - buildGoBackendStaging:
+# filters:
+# branches:
+# ignore: /.*/
+# tags:
+# # see semver examples https://regex101.com/r/Ly7O1x/201/
+# only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
- approve_staging:
type: approval
requires:
- buildBackendStaging
- - buildGoBackendStaging
+ - buildGoBackendCommon
filters:
branches:
ignore: /.*/
@@ -1076,7 +1076,7 @@ workflows:
requires:
- approve_staging
- buildBackendStaging
- - buildGoBackendStaging
+ - buildGoBackendCommon
filters:
branches:
ignore: /.*/
@@ -1128,18 +1128,18 @@ workflows:
tags:
# see semver examples https://regex101.com/r/Ly7O1x/201/
only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
- - buildGoBackendProd:
- filters:
- branches:
- ignore: /.*/
- tags:
- # see semver examples https://regex101.com/r/Ly7O1x/201/
- only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
+# - buildGoBackendProd:
+# filters:
+# branches:
+# ignore: /.*/
+# tags:
+# # see semver examples https://regex101.com/r/Ly7O1x/201/
+# only: /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
- approve_prod:
type: approval
requires:
- buildBackendProd
- - buildGoBackendProd
+ - buildGoBackendCommon
filters:
branches:
ignore: /.*/
@@ -1150,7 +1150,7 @@ workflows:
requires:
- approve_prod
- buildBackendProd
- - buildGoBackendProd
+ - buildGoBackendCommon
filters:
branches:
ignore: /.*/
From b8b43e25b1b66f1021e47ca40936eef00bf68a9c Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 3 Aug 2021 14:16:49 -0700
Subject: [PATCH 0386/1276] Resolved Concurrency Issue (#3102)
- Added a locking mutex around access to a shared cache/map used by
multiple go routines
Signed-off-by: David Deal
---
cla-backend-go/v2/project-service/client.go | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index ee3c5d353..87c7a9e9c 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"strings"
+ "sync"
"github.com/sirupsen/logrus"
@@ -37,6 +38,8 @@ type Client struct {
var (
projectServiceClient *Client
+ // mutex is an object to allow us to lock access to the shared project service map while used by multiple go routines
+ mutex = &sync.Mutex{}
// Short term cache - only for the lifetime of this lambda
projectServiceModels = make(map[string]*models.ProjectOutputDetailed)
apiGWHost string
@@ -78,7 +81,10 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
}
// Lookup in cache first
+ mutex.Lock() // exclusive lock to the shared project service model map
existingModel, exists := projectServiceModels[projectSFID]
+ mutex.Lock()
+
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
return existingModel, nil
@@ -100,8 +106,10 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
}
// Update our cache for next time
+ mutex.Lock() // exclusive lock to the shared project service model map
projectServiceModels[projectSFID] = projectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+ mutex.Unlock()
return projectModel, nil
}
@@ -169,7 +177,9 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
var existingParentModel *models.ProjectOutputDetailed
// Current project in the cache?
+ mutex.Lock() // exclusive lock to the shared project service model map
existingModel, exists = projectServiceModels[projectSFID]
+ mutex.Unlock()
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
@@ -187,7 +197,9 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
projectParentSFID := utils.GetProjectParentSFID(existingModel)
// Parent SFID in the cache?
+ mutex.Lock() // exclusive lock to the shared project service model map
existingParentModel, exists = projectServiceModels[projectParentSFID]
+ mutex.Unlock()
if exists {
return existingParentModel, nil
}
@@ -200,8 +212,10 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
}
// Save/Update our cache for next time
+ mutex.Lock() // exclusive lock to the shared project service model map
projectServiceModels[projectParentSFID] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+ mutex.Unlock()
return parentProjectModel, nil
}
@@ -217,8 +231,10 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
}
// Save/Update our cache for next time
+ mutex.Lock() // exclusive lock to the shared project service model map
projectServiceModels[projectSFID] = projectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+ mutex.Unlock()
// No parent
if !utils.IsProjectHaveParent(projectModel) {
@@ -235,7 +251,9 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
projectParentSFID := utils.GetProjectParentSFID(projectModel)
// Parent in the cache?
+ mutex.Lock() // exclusive lock to the shared project service model map
existingParentModel, exists = projectServiceModels[projectParentSFID]
+ mutex.Unlock() // exclusive lock to the shared project service model map
if exists {
return existingParentModel, nil
}
@@ -248,8 +266,10 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
}
// Save/Update our cache for next time
+ mutex.Lock() // exclusive lock to the shared project service model map
projectServiceModels[projectParentSFID] = parentProjectModel
log.WithFields(f).Debugf("added project model to cache - cache size: %d", len(projectServiceModels))
+ mutex.Unlock() // exclusive lock to the shared project service model map
return parentProjectModel, nil
}
From f43ffd665d0ca47b41b330119cfc03977f39480c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 Aug 2021 15:48:30 -0700
Subject: [PATCH 0387/1276] Bump tar from 4.4.13 to 4.4.15 in
/cla-frontend-contributor-console (#3103)
Bumps [tar](https://github.com/npm/node-tar) from 4.4.13 to 4.4.15.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v4.4.13...v4.4.15)
---
updated-dependencies:
- dependency-name: tar
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index 561c1188b..d5cee53c5 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -6124,9 +6124,9 @@ tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^4.0.2:
- version "4.4.13"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
- integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
+ version "4.4.15"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8"
+ integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
From f3e9cf6aad84bfc4f0eb24dc8da641f16b8a36b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 Aug 2021 15:48:41 -0700
Subject: [PATCH 0388/1276] Bump tar from 6.1.0 to 6.1.3 in /cla-backend
(#3104)
Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.3.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.3)
---
updated-dependencies:
- dependency-name: tar
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 19bc7ae7d..2112e51ab 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -5864,9 +5864,9 @@ tar-stream@^2.1.0, tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83"
- integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==
+ version "6.1.3"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.3.tgz#e44b97ee7d6cc7a4c574e8b01174614538291825"
+ integrity sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
From a625c5a4b9182570da474fdf78c68361fac8c38c Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 3 Aug 2021 15:49:23 -0700
Subject: [PATCH 0389/1276] Resolved Minor Lock/Unlock Issue (#3105)
Signed-off-by: David Deal
---
cla-backend-go/v2/project-service/client.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 87c7a9e9c..601627266 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -83,7 +83,7 @@ func (pmm *Client) GetProject(projectSFID string) (*models.ProjectOutputDetailed
// Lookup in cache first
mutex.Lock() // exclusive lock to the shared project service model map
existingModel, exists := projectServiceModels[projectSFID]
- mutex.Lock()
+ mutex.Unlock()
if exists {
log.WithFields(f).Debugf("cache hit - cache size: %d", len(projectServiceModels))
From 34c9e9e767ceca1cd207b8758475eb86f820f1f1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 Aug 2021 15:51:27 -0700
Subject: [PATCH 0390/1276] Bump tar from 6.1.0 to 6.1.4 in /cla-backend-go
(#3106)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index d9354dd2e..55e94ac56 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -4753,9 +4753,9 @@ tar-stream@^2.1.0, tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83"
- integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==
+ version "6.1.4"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.4.tgz#9f0722b772a5e00dba7d52e1923b37a7ec3799b3"
+ integrity sha512-kcPWrO8S5ABjuZ/v1xQHP8xCEvj1dQ1d9iAb6Qs4jLYzaAIYWwST2IQpz7Ud8VNYRI+fGhFjrnzRKmRggKWg3g==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
From 16c61450b453561a4eb75a797e275b805f62312f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 Aug 2021 15:52:00 -0700
Subject: [PATCH 0391/1276] Bump tar from 4.4.13 to 4.4.15 in
/cla-frontend-corporate-console (#3107)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 8064dde49..4094dd7cd 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -6087,9 +6087,9 @@ tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^4.0.2:
- version "4.4.13"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
- integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
+ version "4.4.15"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8"
+ integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
From 482cdd55ee9409f2b10500195ccb807eef6efd49 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 Aug 2021 15:53:28 -0700
Subject: [PATCH 0392/1276] Bump tar from 6.0.5 to 6.1.4 (#3108)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 97d085948..ecc42d4b0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4555,9 +4555,9 @@ tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^6.0.5:
- version "6.0.5"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f"
- integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==
+ version "6.1.4"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.4.tgz#9f0722b772a5e00dba7d52e1923b37a7ec3799b3"
+ integrity sha512-kcPWrO8S5ABjuZ/v1xQHP8xCEvj1dQ1d9iAb6Qs4jLYzaAIYWwST2IQpz7Ud8VNYRI+fGhFjrnzRKmRggKWg3g==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
From 546d52df935cbafba870a8b23e8dff044b7d8cf6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 4 Aug 2021 11:25:46 -0700
Subject: [PATCH 0393/1276] Resolved Template Preview Issue (#3109)
---
cla-backend-go/docraptor/client.go | 19 +++++++++++--
cla-backend-go/go.mod | 26 +++++++++++++++++-
cla-backend-go/go.sum | 43 ++++++++++++++++++++++++++++++
cla-backend-go/template/service.go | 26 +++++++++++-------
4 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/cla-backend-go/docraptor/client.go b/cla-backend-go/docraptor/client.go
index 7b8503a54..f7df123c0 100644
--- a/cla-backend-go/docraptor/client.go
+++ b/cla-backend-go/docraptor/client.go
@@ -51,6 +51,8 @@ func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
f := logrus.Fields{
"functionName": "v1.docraptor.client.CreatePDF",
"claType": claType,
+ "testMode": dc.testMode,
+ "url": dc.url,
}
document := map[string]interface{}{
@@ -69,9 +71,22 @@ func (dc Client) CreatePDF(html string, claType string) (io.ReadCloser, error) {
log.WithFields(f).Debug("Generating PDF using docraptor...")
resp, err := http.Post(dc.url, "application/json", bytes.NewBuffer(documentBytes))
if err != nil {
- log.WithFields(f).WithError(err).Warn("problem with API call to docraptor")
+ log.WithFields(f).WithError(err).Warnf("problem with API call to docraptor url: %s", dc.url)
return nil, err
}
- defer resp.Body.Close()
+ // Do not close - rely on the caller to close the reader otherwise we will get the read from Response.Body after Close error
+ //defer func() {
+ // closeErr := resp.Body.Close()
+ // if closeErr != nil {
+ // log.WithFields(f).WithError(closeErr).Warn("problem closing docraptor response")
+ // }
+ //}()
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unexpected http response code from docraptor url: %s, status code: %d", dc.url, resp.StatusCode)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ log.WithFields(f).Debugf("successful response from docraptor url: %s, status code: %d", dc.url, resp.StatusCode)
+
return resp.Body, nil
}
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index c93e34994..1c4bfd89f 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -16,10 +16,16 @@ require (
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/bradleyfalzon/ghinstallation v1.1.1
+ github.com/coreos/bbolt v1.3.2 // indirect
+ github.com/coreos/etcd v3.3.13+incompatible // indirect
+ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
+ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fnproject/fdk-go v0.0.2
github.com/gin-gonic/gin v1.7.2
+ github.com/go-delve/delve v1.7.0 // indirect
github.com/go-openapi/errors v0.19.6
github.com/go-openapi/loads v0.19.5
github.com/go-openapi/runtime v0.19.19
@@ -34,33 +40,50 @@ require (
github.com/google/go-github/v37 v37.0.0
github.com/google/uuid v1.1.4
github.com/gorilla/sessions v1.2.1 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
+ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/imroc/req v0.3.0
github.com/jessevdk/go-flags v1.4.0
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0
+ github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2
github.com/kr/pretty v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
+ github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
+ github.com/mattn/go-runewidth v0.0.13 // indirect
+ github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.1
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/mozillazg/request v0.8.0 // indirect
github.com/myitcv/gobin v0.0.14 // indirect
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc
+ github.com/peterh/liner v1.2.1 // indirect
+ github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0 // indirect
+ github.com/prometheus/client_golang v0.9.3 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
github.com/sirupsen/logrus v1.8.1
- github.com/spf13/cobra v1.1.1
+ github.com/soheilhy/cmux v0.1.4 // indirect
+ github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
github.com/ugorji/go v1.2.6 // indirect
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
github.com/xanzy/go-gitlab v0.50.1
+ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
+ go.etcd.io/bbolt v1.3.2 // indirect
+ go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a // indirect
go.uber.org/ratelimit v0.1.0
+ golang.org/x/arch v0.0.0-20210727222714-28578f966459 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
@@ -69,4 +92,5 @@ require (
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
google.golang.org/protobuf v1.27.1 // indirect
+ gopkg.in/resty.v1 v1.12.0 // indirect
)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index bf9282154..52c78f441 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -137,9 +137,15 @@ github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzA
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
+github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
+github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -183,6 +189,8 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7a
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a h1:l4yNPeA/3kNJwE0uDBVXtFX8hfiHrlqkXBLPOrchWzk=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-delve/delve v1.7.0 h1:MaWAD3LtvjE/LL98urSHPjaMT+OubpQ2sqF3R2Uj1rc=
+github.com/go-delve/delve v1.7.0/go.mod h1:2DpgGoHOW7r7MXyykmT7axp9IEEIc8EV/swa5m8rkbo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -371,6 +379,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-dap v0.5.0 h1:RMHAVn5xeunBakYk65ggHXttk6qjZVdbmi+xhAoL2wY=
+github.com/google/go-dap v0.5.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3bOQ/tM=
@@ -460,6 +470,8 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
@@ -530,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/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=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
@@ -565,14 +578,20 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSW
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -630,11 +649,15 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+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/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/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=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -656,6 +679,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzr
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -668,8 +693,12 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wkb8NefNCTRDEodfo6MtfH9BaO8ncMA=
@@ -688,6 +717,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@@ -704,12 +734,16 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
+github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
+github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -787,6 +821,9 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.starlark.net v0.0.0-20200821142938-949cc6f4b097/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
+go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a h1:wDtSCWGrX9tusypq2Qq9xzaA3Tf/+4D2KaWO+HQvGZE=
+go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -798,6 +835,9 @@ go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
+golang.org/x/arch v0.0.0-20210727222714-28578f966459 h1:ECTRghTMeoUryGydSc+nr1o4M2i73DwlP4LFEDJb3II=
+golang.org/x/arch v0.0.0-20210727222714-28578f966459/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -969,6 +1009,7 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1032,6 +1073,7 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1218,6 +1260,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/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=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
diff --git a/cla-backend-go/template/service.go b/cla-backend-go/template/service.go
index dd2a040f3..f364f7ea2 100644
--- a/cla-backend-go/template/service.go
+++ b/cla-backend-go/template/service.go
@@ -142,17 +142,25 @@ func (s Service) CreateTemplatePreview(ctx context.Context, claGroupFields *mode
return nil, errors.New("invalid value of template_for")
}
- pdf, err := s.docRaptorClient.CreatePDF(templateHTML, templateFor)
+ ioReader, err := s.docRaptorClient.CreatePDF(templateHTML, templateFor)
if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem with API call to docraptor service")
return nil, err
}
defer func() {
- closeErr := pdf.Close()
+ closeErr := ioReader.Close()
if closeErr != nil {
log.WithFields(f).WithError(closeErr).Warn("error closing PDF")
}
}()
- return ioutil.ReadAll(pdf)
+
+ bytes, err := ioutil.ReadAll(ioReader)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("error reading PDF bytes from the generated template")
+ return nil, err
+ }
+
+ return bytes, err
}
// CreateCLAGroupTemplate service method
@@ -201,19 +209,19 @@ func (s Service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
// Invoke the go routine - any errors will be handled below
eg.Go(func() error {
log.WithFields(f).Debugf("Creating PDF for %s", claTypeICLA)
- iclaPdf, iclaErr := s.docRaptorClient.CreatePDF(iclaTemplateHTML, claTypeICLA)
+ ioReader, iclaErr := s.docRaptorClient.CreatePDF(iclaTemplateHTML, claTypeICLA)
if iclaErr != nil {
log.WithFields(f).WithError(iclaErr).Warn("Problem generating ICLA template via docraptor client - returning empty template PDFs")
return err
}
defer func() {
- closeErr := iclaPdf.Close()
+ closeErr := ioReader.Close()
if closeErr != nil {
log.WithFields(f).WithError(closeErr).Warn("error closing ICLA PDF")
}
}()
iclaFileName := s.generateTemplateS3FilePath(claGroupID, claTypeICLA)
- iclaFileURL, err = s.SaveTemplateToS3(bucket, iclaFileName, iclaPdf)
+ iclaFileURL, err = s.SaveTemplateToS3(bucket, iclaFileName, ioReader)
if err != nil {
log.WithFields(f).WithError(err).Warnf("Problem uploading ICLA PDF: %s to s3 - returning empty template PDFs", iclaFileName)
return err
@@ -228,19 +236,19 @@ func (s Service) CreateCLAGroupTemplate(ctx context.Context, claGroupID string,
// Invoke the go routine - any errors will be handled below
eg.Go(func() error {
log.WithFields(f).Debugf("Creating PDF for %s", claTypeCCLA)
- cclaPdf, cclaErr := s.docRaptorClient.CreatePDF(cclaTemplateHTML, claTypeCCLA)
+ ioReader, cclaErr := s.docRaptorClient.CreatePDF(cclaTemplateHTML, claTypeCCLA)
if cclaErr != nil {
log.WithFields(f).WithError(cclaErr).Warn("Problem generating CCLA template via docraptor client - returning empty template PDFs")
return err
}
defer func() {
- closeErr := cclaPdf.Close()
+ closeErr := ioReader.Close()
if closeErr != nil {
log.WithFields(f).WithError(closeErr).Warn("error closing CCLA PDF")
}
}()
cclaFileName := s.generateTemplateS3FilePath(claGroupID, claTypeCCLA)
- cclaFileURL, err = s.SaveTemplateToS3(bucket, cclaFileName, cclaPdf)
+ cclaFileURL, err = s.SaveTemplateToS3(bucket, cclaFileName, ioReader)
if err != nil {
log.WithFields(f).Warnf("Problem uploading CCLA PDF: %s to s3, error: %v - returning empty template PDFs", cclaFileName, err)
return err
From b1fcf91d90a4510cbdea07810fc3df29ea7764a2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 6 Aug 2021 17:05:07 -0700
Subject: [PATCH 0394/1276] Bump tar from 4.4.10 to 4.4.15 in
/cla-frontend-project-console (#3110)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/yarn.lock | 62 +++++++++++---------------
1 file changed, 26 insertions(+), 36 deletions(-)
diff --git a/cla-frontend-project-console/yarn.lock b/cla-frontend-project-console/yarn.lock
index c58c94dd5..30230a8eb 100644
--- a/cla-frontend-project-console/yarn.lock
+++ b/cla-frontend-project-console/yarn.lock
@@ -1481,16 +1481,11 @@ chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.4.3:
optionalDependencies:
fsevents "~2.1.2"
-chownr@^1.0.1:
+chownr@^1.0.1, chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
-chownr@^1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
- integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
-
chownr@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@@ -2748,11 +2743,11 @@ fs-extra@^9.0.1:
universalify "^1.0.0"
fs-minipass@^1.2.5:
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
- integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
+ integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
- minipass "^2.2.1"
+ minipass "^2.6.0"
fs-minipass@^2.0.0:
version "2.1.0"
@@ -4177,15 +4172,15 @@ minimatch@^3.0.2, minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
-minimist@0.0.8, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
+minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
-minipass@^2.2.1, minipass@^2.3.5:
- version "2.3.5"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
- integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
+ integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
@@ -4198,11 +4193,11 @@ minipass@^3.0.0:
yallist "^4.0.0"
minizlib@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
- integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
+ integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
- minipass "^2.2.1"
+ minipass "^2.9.0"
minizlib@^2.1.1:
version "2.1.2"
@@ -4229,11 +4224,11 @@ mixin-object@^2.0.1:
is-extendable "^0.1.1"
mkdirp@^0.5.0, mkdirp@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
- minimist "0.0.8"
+ minimist "^1.2.5"
mkdirp@^1.0.3:
version "1.0.4"
@@ -5326,12 +5321,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-safe-buffer@>=5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
- integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
-
-safe-buffer@^5.0.1, safe-buffer@~5.2.0:
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -6022,13 +6012,13 @@ tar-stream@^2.1.4:
readable-stream "^3.1.1"
tar@^4, tar@^4.0.2:
- version "4.4.10"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
- integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+ version "4.4.15"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8"
+ integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
- minipass "^2.3.5"
+ minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
@@ -6539,9 +6529,9 @@ yallist@^2.1.2:
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
- integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yallist@^4.0.0:
version "4.0.0"
From 4145e9c9c46ce27039339a81e6f47397eb0c4395 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 07:14:54 -0700
Subject: [PATCH 0395/1276] Bump path-parse from 1.0.6 to 1.0.7 in
/cla-frontend-contributor-console/edge (#3133)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/edge/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/edge/yarn.lock b/cla-frontend-contributor-console/edge/yarn.lock
index 77b6a1c18..907fac2cf 100644
--- a/cla-frontend-contributor-console/edge/yarn.lock
+++ b/cla-frontend-contributor-console/edge/yarn.lock
@@ -2391,9 +2391,9 @@ path-key@^2.0.0:
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
- integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^1.0.0:
version "1.1.0"
From e959221bd78b7feeadd5a483763853ec27999f66 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 11 Aug 2021 20:11:40 +0300
Subject: [PATCH 0396/1276] gitlab activity callback (#3140)
---
cla-backend-go/cmd/server.go | 4 +
cla-backend-go/gitlab/mr.go | 162 ++++++++
cla-backend-go/gitlab/organization.go | 52 +++
cla-backend-go/gitlab/repository.go | 4 +
cla-backend-go/go.sum | 4 +
cla-backend-go/signatures/repository.go | 5 +
cla-backend-go/swagger/cla.v2.yaml | 38 +-
cla-backend-go/v2/gitlab-activity/handlers.go | 81 ++++
cla-backend-go/v2/gitlab-activity/service.go | 381 ++++++++++++++++++
.../v2/gitlab-activity/service_test.go | 104 +++++
.../v2/gitlab_organizations/handlers.go | 7 +-
.../v2/gitlab_organizations/repository.go | 1 +
12 files changed, 839 insertions(+), 4 deletions(-)
create mode 100644 cla-backend-go/gitlab/mr.go
create mode 100644 cla-backend-go/gitlab/repository.go
create mode 100644 cla-backend-go/v2/gitlab-activity/handlers.go
create mode 100644 cla-backend-go/v2/gitlab-activity/service.go
create mode 100644 cla-backend-go/v2/gitlab-activity/service_test.go
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index d143fb378..28c0dab40 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -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"
@@ -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)
@@ -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)
diff --git a/cla-backend-go/gitlab/mr.go b/cla-backend-go/gitlab/mr.go
new file mode 100644
index 000000000..0811613a1
--- /dev/null
+++ b/cla-backend-go/gitlab/mr.go
@@ -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 := `
+ `
+ failed := `
+ `
+
+ 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
+}
diff --git a/cla-backend-go/gitlab/organization.go b/cla-backend-go/gitlab/organization.go
index 5144bd4ad..f09b096dc 100644
--- a/cla-backend-go/gitlab/organization.go
+++ b/cla-backend-go/gitlab/organization.go
@@ -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{})
@@ -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
+}
diff --git a/cla-backend-go/gitlab/repository.go b/cla-backend-go/gitlab/repository.go
new file mode 100644
index 000000000..90f42708c
--- /dev/null
+++ b/cla-backend-go/gitlab/repository.go
@@ -0,0 +1,4 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 52c78f441..690e61600 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -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=
@@ -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=
@@ -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=
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index fc0e05047..8d6477723 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -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)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index ac0cafbae..523fbec3d 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -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
@@ -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
@@ -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:
diff --git a/cla-backend-go/v2/gitlab-activity/handlers.go b/cla-backend-go/v2/gitlab-activity/handlers.go
new file mode 100644
index 000000000..6f3723438
--- /dev/null
+++ b/cla-backend-go/v2/gitlab-activity/handlers.go
@@ -0,0 +1,81 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_activity
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/gofrs/uuid"
+ "github.com/sirupsen/logrus"
+ gitlabsdk "github.com/xanzy/go-gitlab"
+)
+
+func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+
+ api.GitlabActivityGitlabActivityHandler = gitlab_activity.GitlabActivityHandlerFunc(func(params gitlab_activity.GitlabActivityParams) middleware.Responder {
+ requestID, _ := uuid.NewV4()
+ reqID := requestID.String()
+ f := logrus.Fields{
+ "functionName": "gitlab_activity.handlers.GitlabActivityGitlabActivityHandler",
+ "requestID": reqID,
+ }
+ log.WithFields(f).Debugf("handling gitlab activity callback")
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID)
+
+ jsonData, err := params.GitlabActivityInput.MarshalJSON()
+ if err != nil {
+ msg := fmt.Sprintf("unmarshall event data failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ event, err := gitlabsdk.ParseWebhook(gitlabsdk.EventTypeMergeRequest, jsonData)
+ if err != nil {
+ msg := fmt.Sprintf("parsing gitlab merge event type failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ mergeEvent, ok := event.(*gitlabsdk.MergeEvent)
+ if !ok {
+ msg := fmt.Sprintf("parsing gitlab merge event typecast failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ if mergeEvent.ObjectAttributes.State != "opened" {
+ msg := fmt.Sprintf("parsing gitlab merge event failed, only opened accepted")
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ err = service.ProcessMergeOpenedActivity(ctx, mergeEvent)
+ if err != nil {
+ msg := fmt.Sprintf("processing gitlab merge event failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityInternalServerError().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ return gitlab_activity.NewGitlabActivityOK()
+ //return gitlab_activity.NewGitlabActivityOK().WithPayload(&models.SuccessResponse{
+ // Code: "200",
+ // Message: "oauth credentials stored successfully",
+ // XRequestID: reqID,
+ //})
+
+ })
+
+}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
new file mode 100644
index 000000000..8b2164cb6
--- /dev/null
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -0,0 +1,381 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_activity
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/communitybridge/easycla/cla-backend-go/company"
+ signatures1 "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/signatures"
+ "github.com/communitybridge/easycla/cla-backend-go/users"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+ "github.com/xanzy/go-gitlab"
+)
+
+type Service interface {
+ ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *gitlab.MergeEvent) error
+}
+
+type service struct {
+ usersRepository users.UserRepository
+ gitlabRepository gitlab_organizations.RepositoryInterface
+ githubRepositories repositories.Repository
+ signaturesRepository signatures.SignatureRepository
+ projectsCLAGroupsRepository projects_cla_groups.Repository
+ companyRepository company.IRepository
+ signatureRepository signatures.SignatureRepository
+}
+
+func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, githubRepositories repositories.Repository, usersRepository users.UserRepository, signaturesRepository signatures.SignatureRepository, projectsCLAGroupsRepository projects_cla_groups.Repository,
+ companyRepository company.IRepository, signatureRepository signatures.SignatureRepository) Service {
+ return &service{
+ usersRepository: usersRepository,
+ gitlabRepository: gitlabRepository,
+ githubRepositories: githubRepositories,
+ signaturesRepository: signaturesRepository,
+ projectsCLAGroupsRepository: projectsCLAGroupsRepository,
+ companyRepository: companyRepository,
+ signatureRepository: signatureRepository,
+ }
+}
+
+func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *gitlab.MergeEvent) error {
+ projectName := mergeEvent.Project.Name
+ projectID := mergeEvent.Project.ID
+ mergeID := mergeEvent.ObjectAttributes.IID
+ repositoryName := mergeEvent.Repository.Name
+ repositoryPath := mergeEvent.Project.PathWithNamespace
+ lastCommitSha := mergeEvent.ObjectAttributes.LastCommit.ID
+
+ f := logrus.Fields{
+ "functionName": "ProcessMergeOpenedActivity",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabProjectName": projectName,
+ "gitlabProjectID": projectID,
+ "repositoryName": repositoryName,
+ "repositoryPath": repositoryPath,
+ "mergeID": mergeID,
+ }
+
+ log.WithFields(f).Debugf("looking up for gitlab org in easycla records ...")
+ gitlabOrg, err := s.getGitlabOrganizationFromMergeEvent(ctx, mergeEvent)
+ if err != nil {
+ return fmt.Errorf("fetching internal gitlab org for following path : %s failed : %v", repositoryPath, err)
+ }
+
+ log.WithFields(f).Debugf("internal gitlab org : %s:%s is associated with external path : %s", gitlabOrg.OrganizationID, gitlabOrg.OrganizationName, repositoryPath)
+
+ gitlabClient, err := gitlab2.NewGitlabOauthClient(gitlabOrg.AuthInfo)
+ if err != nil {
+ return fmt.Errorf("initializing gitlab client : %v", err)
+ }
+
+ _, err = gitlab2.FetchMrInfo(gitlabClient, projectID, mergeID)
+ if err != nil {
+ return fmt.Errorf("fetching info for mr : %d and project : %d: %s, failed : %v", mergeID, projectID, projectName, err)
+ }
+
+ // try to find the repository via the external id
+ gitlabRepo, err := s.getGitlabRepoByExternalID(ctx, gitlabOrg.OrganizationName, strconv.Itoa(projectID))
+ if err != nil {
+ return fmt.Errorf("finding internal repository for gitlab org name failed : %v", err)
+ }
+
+ log.WithFields(f).Debugf("internal gitlab repository found with id : %s", gitlabRepo.RepositoryID)
+ participants, err := gitlab2.FetchMrParticipants(gitlabClient, projectID, mergeID, true)
+ if err != nil {
+ return fmt.Errorf("fetching mr participants : %v", err)
+ }
+
+ if len(participants) == 0 {
+ return fmt.Errorf("no participants found in gitlab mr : %d, and gitlab project : %d", mergeID, projectID)
+ }
+
+ claGroup, err := s.projectsCLAGroupsRepository.GetClaGroupIDForProject(ctx, gitlabOrg.ProjectSFID)
+ if err != nil {
+ return fmt.Errorf("fetching claGroup id for gitlabOrg project sfid : %s, failed : %v", gitlabOrg.ProjectSFID, err)
+ }
+ claGroupID := claGroup.ClaGroupID
+ log.WithFields(f).Debugf("gitlabOrg : %s is associated with cla group id : %s", gitlabOrg.OrganizationName, claGroupID)
+
+ log.WithFields(f).Debugf("found following participants for the MR : %d", len(participants))
+ missingUsersMsg := "missing users : "
+ var missingUsers []string
+ for _, gitlabUser := range participants {
+ if ok, err := s.hasUserSigned(ctx, claGroupID, gitlabUser); ok {
+ log.WithFields(f).Infof("gitlabUser : %d:%s has signed", gitlabUser.ID, gitlabUser.Username)
+ } else {
+ missingUsers = append(missingUsers, fmt.Sprintf("gitlabUser : %d:%s hasn't signed", gitlabUser.ID, gitlabUser.Username))
+ log.WithFields(f).Errorf("gitlabUser : %d:%s hasn't signed, err : %v", gitlabUser.ID, gitlabUser.Username, err)
+ }
+ }
+
+ if len(missingUsers) > 0 {
+ for _, missing := range missingUsers {
+ missingUsersMsg += missing
+ }
+ log.WithFields(f).Errorf("mr faild with following users : %s", missingUsersMsg)
+ if err := gitlab2.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Failed, missingUsersMsg); err != nil {
+ return fmt.Errorf("setting commit status failed : %v", err)
+ }
+
+ if err := gitlab2.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Failed, missingUsersMsg); err != nil {
+ return fmt.Errorf("setting comment failed : %v", err)
+ }
+
+ return nil
+ }
+ err = gitlab2.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Success, "all signed passing")
+ if err != nil {
+ return fmt.Errorf("setting commit status failed : %v", err)
+ }
+
+ if err := gitlab2.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Success, missingUsersMsg); err != nil {
+ return fmt.Errorf("setting comment failed : %v", err)
+ }
+ return err
+}
+
+func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*gitlab_organizations.GitlabOrganization, error) {
+ repositoryPath := mergeEvent.Project.PathWithNamespace
+ parts := strings.Split(repositoryPath, "/")
+ organizationName := parts[0]
+ gitlabOrgs, err := s.gitlabRepository.GetGitlabOrganizationByName(ctx, organizationName)
+ if err != nil || len(gitlabOrgs.List) == 0 {
+ // try getting it with project name as well
+ gitlabOrgs, err = s.gitlabRepository.GetGitlabOrganizationByName(ctx, mergeEvent.Project.Namespace)
+ if err != nil {
+ return nil, fmt.Errorf("gitlab org : %s doesn't exist : %v", organizationName, err)
+ }
+ }
+
+ if len(gitlabOrgs.List) == 0 {
+ return nil, fmt.Errorf("gitlab org : %s doesn't exist", organizationName)
+ }
+
+ orgID := gitlabOrgs.List[0].OrganizationID
+ gitlabOrg, err := s.gitlabRepository.GetGitlabOrganization(ctx, orgID)
+ if err != nil {
+ return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", orgID, err)
+ }
+
+ return gitlabOrg, nil
+}
+
+func (s service) getGitlabRepoByExternalID(ctx context.Context, orgName, gitlabRepoID string) (*models.GithubRepository, error) {
+ gitlabRepos, err := s.githubRepositories.GetRepositoriesByOrganizationName(ctx, orgName)
+ if err != nil {
+ return nil, fmt.Errorf("fetching gitlab repo for external id : %s, orgName : %s, failed : %v", gitlabRepoID, orgName, err)
+ }
+
+ if len(gitlabRepos) == 0 {
+ return nil, fmt.Errorf("no repositories found for orgName : %s", orgName)
+ }
+
+ for _, gitlabRepo := range gitlabRepos {
+ if gitlabRepo.RepositoryExternalID == gitlabRepoID && gitlabRepo.RepositoryType == "gitlab" {
+ return gitlabRepo, nil
+ }
+ }
+
+ return nil, fmt.Errorf("no repositories found for orgName : %s and gitlab external id : %s", orgName, gitlabRepoID)
+}
+
+func (s service) hasUserSigned(ctx context.Context, claGroupID string, gitlabUser *gitlab.User) (bool, error) {
+ f := logrus.Fields{
+ "functionName": "hasUserSigned",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabUserID": gitlabUser.ID,
+ "gitlabUserName": gitlabUser.Username,
+ "gitlabUserEmail": gitlabUser.Email,
+ }
+
+ userModel, b, err := s.findUserModelForGitlabUser(f, gitlabUser)
+ if err != nil {
+ return b, err
+ }
+
+ if userModel == nil {
+ msg := fmt.Sprintf("gitlab user : %d:%s not found in easycla records", gitlabUser.ID, gitlabUser.Username)
+ log.WithFields(f).Error(msg)
+ return false, fmt.Errorf(msg)
+ }
+ log.WithFields(f).Debugf("found following easyCLA user for gitlab record, userID: %s, lfusername : %s", userModel.UserID, userModel.LfUsername)
+
+ icla, err := s.signaturesRepository.GetIndividualSignature(ctx, claGroupID, userModel.UserID, aws.Bool(true), aws.Bool(true))
+ if err != nil {
+ return false, fmt.Errorf("fetching ICLS for gitlab user : %d:%s failed : %v", gitlabUser.ID, gitlabUser.Username, err)
+ }
+
+ if icla != nil {
+ log.WithFields(f).Infof("user has signed the following signature : %s, passing", icla.SignatureID)
+ return true, nil
+ }
+
+ if userModel.CompanyID == "" {
+ log.WithFields(f).Debugf("user does not have association with any company, can't continue")
+ return false, fmt.Errorf("user hasn't signed yet")
+ }
+
+ companyID := userModel.CompanyID
+ _, err = s.companyRepository.GetCompany(ctx, companyID)
+ if err != nil {
+ msg := fmt.Sprintf("can't load company record : %s for user : %s association : %v", companyID, userModel.UserID, err)
+ log.WithFields(f).Errorf(msg)
+ return false, fmt.Errorf(msg)
+ }
+
+ corporateSignature, err := s.signatureRepository.GetCorporateSignature(ctx, claGroupID, companyID, aws.Bool(true), aws.Bool(true))
+ if err != nil {
+ msg := fmt.Sprintf("can't load company signature record : %s for user : %s association : %v", companyID, userModel.UserID, err)
+ log.WithFields(f).Errorf(msg)
+ return false, fmt.Errorf(msg)
+ }
+ log.WithFields(f).Debugf("loaded signature id : %s for claGroupID : %s and companyID : %s", corporateSignature.SignatureID, claGroupID, companyID)
+
+ approvalCriteria := &signatures.ApprovalCriteria{}
+ if gitlabUser.Email != "" {
+ approvalCriteria.UserEmail = gitlabUser.Email
+ } else if gitlabUser.Username != "" {
+ approvalCriteria.GitlabUsername = gitlabUser.Username
+ } else {
+ msg := fmt.Sprintf("gitlabUser model doesn't have enough information to fetch the employee signatures for user : %s", userModel.UserID)
+ log.WithFields(f).Errorf(msg)
+ return false, fmt.Errorf(msg)
+ }
+
+ employeeSignatures, err := s.signaturesRepository.GetProjectCompanyEmployeeSignatures(ctx, signatures1.GetProjectCompanyEmployeeSignaturesParams{
+ CompanyID: companyID,
+ ProjectID: claGroupID,
+ }, approvalCriteria, 100)
+
+ if err != nil {
+ msg := fmt.Sprintf("can't load employee signature records : %s for user : %s association : %v", companyID, userModel.UserID, err)
+ log.WithFields(f).Errorf(msg)
+ return false, fmt.Errorf(msg)
+ }
+
+ if len(employeeSignatures.Signatures) == 0 {
+ msg := fmt.Sprintf("no employee signature records found for company : %s user : %s association", companyID, userModel.UserID)
+ log.WithFields(f).Errorf(msg)
+ return false, fmt.Errorf(msg)
+ }
+
+ if IsUserApprovedForSignature(f, corporateSignature, userModel, gitlabUser) {
+ log.WithFields(f).Debugf("user is approved in signature : %s", corporateSignature.SignatureID)
+ return true, nil
+ }
+
+ log.WithFields(f).Warnf("user not in one of the approval lists")
+ return false, fmt.Errorf("not signed")
+}
+
+func (s service) findUserModelForGitlabUser(f logrus.Fields, gitlabUser *gitlab.User) (*models.User, bool, error) {
+ log.WithFields(f).Debugf("Looking up Gitlab user via gitlabID")
+ userModel, err := s.usersRepository.GetUserByGitlabID(gitlabUser.ID)
+ if err != nil {
+ if !strings.Contains(err.Error(), "not found") {
+ return nil, false, fmt.Errorf("looking up gitlab user via gitlabID : %d failed : %v", gitlabUser.ID, err)
+ }
+ userModel = nil
+ }
+ if userModel == nil && gitlabUser.Username != "" {
+ log.WithFields(f).Debugf("Looking up Gitlab user via user gitlab username")
+ userModel, err = s.usersRepository.GetUserByGitlabUsername(gitlabUser.Username)
+ if !strings.Contains(err.Error(), "not found") {
+ return nil, false, fmt.Errorf("looking up gitlab user via gitlabUsername : %s failed : %v", gitlabUser.Username, err)
+ }
+ }
+
+ if userModel == nil && gitlabUser.Email != "" {
+ log.WithFields(f).Debugf("Looking up Gitlab user via user email")
+ userModel, err = s.usersRepository.GetUserByEmail(gitlabUser.Email)
+ if err != nil {
+ if !errors.Is(err, &utils.UserNotFound{}) {
+ return nil, false, fmt.Errorf("looking up gitlab user via email : %s failed : %v", gitlabUser.Email, err)
+ }
+ }
+ }
+ return userModel, false, nil
+}
+
+func IsUserApprovedForSignature(f logrus.Fields, corporateSignature *models.Signature, user *models.User, gitlabUser *gitlab.User) bool {
+ log.WithFields(f).Debugf("checking if user : %s is approved for corporate signature : %s", user.UserID, corporateSignature.SignatureID)
+ userEmails := user.Emails
+ if string(user.LfEmail) != "" {
+ userEmails = append(userEmails, string(user.LfEmail))
+ }
+
+ emailApprovalList := corporateSignature.EmailApprovalList
+ domainApprovalList := corporateSignature.DomainApprovalList
+ log.WithFields(f).Debugf("checking if user : %s is approved for corporate signature : %s, email approval list : %+v", user.UserID, corporateSignature.SignatureID, emailApprovalList)
+
+ if len(userEmails) > 0 && len(emailApprovalList) > 0 {
+ for _, email := range userEmails {
+ for _, approvalEmail := range emailApprovalList {
+ if email == approvalEmail {
+ log.WithFields(f).Debugf("found user email : %s in email approval list ", email)
+ return true
+ }
+ }
+ }
+ } else {
+ log.WithFields(f).Warnf("no match for user in signature email approval list")
+ }
+
+ if len(domainApprovalList) > 0 && len(userEmails) > 0 {
+ log.WithFields(f).Debugf("checking if emails : %+v are approved for corporate signature : %s, domain approval list : %+v", userEmails, corporateSignature.SignatureID, domainApprovalList)
+ for _, userEmail := range userEmails {
+ for _, domainApprovalPattern := range domainApprovalList {
+ if strings.HasPrefix(domainApprovalPattern, "*.") {
+ domainApprovalPattern = strings.Replace(domainApprovalPattern, "*.", ".*", 1)
+ } else if strings.HasPrefix(domainApprovalPattern, "*") {
+ domainApprovalPattern = strings.Replace(domainApprovalPattern, "*", ".*", 1)
+ } else if strings.HasPrefix(domainApprovalPattern, ".") {
+ domainApprovalPattern = strings.Replace(domainApprovalPattern, ".", ".*", 1)
+ }
+ regexpApprovalPattern := "^.*@" + domainApprovalPattern + "$"
+ if ok, err := regexp.MatchString(regexpApprovalPattern, userEmail); ok && err == nil {
+ log.WithFields(f).Debugf("found user email : %s in email approval list : %s", userEmail, domainApprovalPattern)
+ return true
+ }
+ }
+ }
+ }
+
+ gitlabUserName := gitlabUser.Username
+ gitlabUsernameApprovalList := corporateSignature.GitlabUsernameApprovalList
+ if gitlabUserName != "" && len(gitlabUsernameApprovalList) > 0 {
+ log.WithFields(f).Debugf("checking gitlab username : %s for gitlab approval list : %+v", gitlabUserName, gitlabUsernameApprovalList)
+ for _, gitlabApproval := range gitlabUsernameApprovalList {
+ if gitlabApproval == gitlabUserName {
+ log.WithFields(f).Debugf("found gitlab username : %s in gitlab approval list ", gitlabUserName)
+ return true
+ }
+ }
+
+ } else {
+ log.WithFields(f).Warnf("no match found for gitlabUser : %s in gitlab approval list : %+v", gitlabUserName, gitlabUsernameApprovalList)
+ }
+
+ // todo check user against the gitlab org approval list
+ log.WithFields(f).Errorf("unable to find user in any approval list")
+ return false
+
+}
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
new file mode 100644
index 000000000..74eef308a
--- /dev/null
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -0,0 +1,104 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_activity
+
+import (
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+ "github.com/xanzy/go-gitlab"
+ "testing"
+)
+
+func TestIsUserApprovedForSignature(t *testing.T) {
+ userModel := &models.User{
+ Emails: []string{
+ "one@example.com",
+ "two@bar.com",
+ },
+ }
+ gitlabUser := &gitlab.User{
+ Username: "one",
+ }
+
+ testCases := []struct{
+ name string
+ signature *models.Signature
+ expected bool
+ }{
+ {
+ name: "nothing matched",
+ signature : &models.Signature{},
+ },
+ {
+ name: "email approval list non empty no match",
+ signature : &models.Signature{
+ EmailApprovalList: []string{"three@example.com"},
+ },
+ },
+ {
+ name: "email approval list match",
+ signature : &models.Signature{
+ EmailApprovalList: []string{"one@example.com"},
+ },
+ expected: true,
+ },
+ {
+ name: "domain approval list match no match",
+ signature : &models.Signature{
+ DomainApprovalList: []string{"*.foo.com"},
+ },
+ expected: false,
+ },
+ {
+ name: "domain approval list match domain star",
+ signature : &models.Signature{
+ DomainApprovalList: []string{"*.example.com"},
+ },
+ expected: true,
+ },
+ {
+ name: "domain approval list match domain star globbing",
+ signature : &models.Signature{
+ DomainApprovalList: []string{"*example.com"},
+ },
+ expected: true,
+ },
+ {
+ name: "domain approval list match domain star dot",
+ signature : &models.Signature{
+ DomainApprovalList: []string{".example.com"},
+ },
+ expected: true,
+ },
+ {
+ name: "gitlab username approval list no match",
+ signature : &models.Signature{
+ GitlabUsernameApprovalList: []string{"two"},
+ },
+ expected: false,
+ },
+ {
+ name: "gitlab username approval list match",
+ signature : &models.Signature{
+ GitlabUsernameApprovalList: []string{"one"},
+ },
+ expected: true,
+ },
+ }
+
+ for _, tc := range testCases{
+ t.Run(tc.name, func(tt *testing.T) {
+ result := IsUserApprovedForSignature(logrus.Fields{}, tc.signature, userModel, gitlabUser)
+ if tc.expected{
+ assert.True(tt, result)
+
+ }else{
+ assert.False(tt, result)
+ }
+ })
+ }
+
+
+}
\ No newline at end of file
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 63f9684a8..0c15a6f3e 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -9,13 +9,13 @@ import (
"fmt"
"strings"
- "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
-
- "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
"github.com/gofrs/uuid"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/sirupsen/logrus"
@@ -289,4 +289,5 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
XRequestID: reqID,
})
})
+
}
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 2ba7b52ec..27e8e032b 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -35,6 +35,7 @@ type RepositoryInterface interface {
AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.CreateGitlabOrganization) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganizations, error)
UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
From 4ec4ee53b786e9349fa0d1c7c922b7992aa7fe74 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 11 Aug 2021 16:56:26 -0700
Subject: [PATCH 0397/1276] Added GitLab Repository Support (#3125)
* Added GitLab Repository Support
- Added GitLab repository v2 swagger API defintions
- Added GitLab repository swagger data models
- Added GitLab repository handler functions
- Added GitLab repository service layer
- Added GitLab repository database layer
- Updated a few GitHub/GitLab modeling conventions
- Added common error models
- Added common constants for table columns and indices
Signed-off-by: David Deal
* Updated GitLab Org Endpoints and Repo Impl
- Updated response swagger for add GitLab repo
- Added logic to return the newly created GitLab record with the installation URL
- Added GetGitlabOrganizationByName to the repo service definition
- Rebased changes from main branch
Signed-off-by: David Deal
* GitLab Swagger Cleanup
- Added separate common swagger fragments
- Renamed Project Gitlab Org/Repo to GitLab Project Org/Repo so that
names align with naming conventions and object/structure names
Signed-off-by: David Deal
* Minor Code Cleanup in SSM Config
Signed-off-by: David Deal
---
cla-backend-go/cmd/server.go | 23 +-
cla-backend-go/config/ssm.go | 1 -
cla-backend-go/github/github_repository.go | 2 +-
cla-backend-go/github_organizations/mock.go | 2 +-
.../github_organizations/repository.go | 4 +-
.../github_organizations/service.go | 12 +-
cla-backend-go/project/helpers.go | 2 +-
cla-backend-go/project/repository.go | 6 +-
cla-backend-go/project/service.go | 4 +-
cla-backend-go/repositories/constants.go | 49 ++
.../repositories/mock/mock_repository.go | 102 ++--
.../repositories/mock/mock_service.go | 52 +-
cla-backend-go/repositories/models.go | 27 +-
cla-backend-go/repositories/repository.go | 195 ++++----
cla-backend-go/repositories/service.go | 42 +-
cla-backend-go/signatures/repository.go | 8 +-
cla-backend-go/swagger/cla.v1.yaml | 14 +-
cla-backend-go/swagger/cla.v2.yaml | 254 +++++++---
...n.yaml => github-create-organization.yaml} | 0
...ies.yaml => github-list-repositories.yaml} | 0
.../swagger/common/github-repository.yaml | 62 ++-
...n.yaml => github-update-organization.yaml} | 0
.../swagger/common/gitlab-add-repository.yaml | 42 ++
.../common/gitlab-create-organization.yaml | 27 +
.../common/gitlab-list-repositories.yaml | 9 +
.../swagger/common/gitlab-organizations.yaml | 1 +
.../common/gitlab-project-organization.yaml | 44 ++
.../common/gitlab-project-organizations.yaml | 10 +
.../common/gitlab-project-repository.yaml | 41 ++
.../swagger/common/gitlab-repository.yaml | 57 +++
.../common/gitlab-update-organization.yaml | 17 +
cla-backend-go/utils/constants.go | 21 +
cla-backend-go/utils/errors.go | 120 +++++
cla-backend-go/v2/dynamo_events/autoenable.go | 22 +-
.../v2/dynamo_events/autoenable_test.go | 62 +--
.../v2/dynamo_events/github_repository.go | 4 +-
.../v2/dynamo_events/projects_cla_groups.go | 4 +-
cla-backend-go/v2/github_activity/service.go | 81 +--
.../v2/github_activity/service_test.go | 4 +-
.../v2/github_organizations/service.go | 47 +-
cla-backend-go/v2/gitlab-activity/service.go | 46 +-
.../v2/gitlab-activity/service_test.go | 38 +-
.../v2/gitlab_organizations/handlers.go | 6 +-
.../v2/gitlab_organizations/repository.go | 65 +--
.../v2/gitlab_organizations/service.go | 48 +-
.../v2/repositories/gitlab_services.go | 138 ++++++
cla-backend-go/v2/repositories/handlers.go | 195 +++++++-
cla-backend-go/v2/repositories/repository.go | 465 ++++++++++++++++++
cla-backend-go/v2/repositories/service.go | 141 +++---
49 files changed, 1983 insertions(+), 633 deletions(-)
create mode 100644 cla-backend-go/repositories/constants.go
rename cla-backend-go/swagger/common/{create-github-organization.yaml => github-create-organization.yaml} (100%)
rename cla-backend-go/swagger/common/{list-github-repositories.yaml => github-list-repositories.yaml} (100%)
rename cla-backend-go/swagger/common/{update-github-organization.yaml => github-update-organization.yaml} (100%)
create mode 100644 cla-backend-go/swagger/common/gitlab-add-repository.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-create-organization.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-list-repositories.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-project-organization.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-project-organizations.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-project-repository.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-repository.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-update-organization.yaml
create mode 100644 cla-backend-go/v2/repositories/gitlab_services.go
create mode 100644 cla-backend-go/v2/repositories/repository.go
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 28c0dab40..35e59ba7a 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -242,14 +242,15 @@ func server(localMode bool) http.Handler {
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
usersRepo := users.NewRepository(awsSession, stage)
- repositoriesRepo := repositories.NewRepository(awsSession, stage)
+ gitV1Repository := repositories.NewRepository(awsSession, stage)
+ gitV2Repository := v2Repositories.NewRepository(awsSession, stage)
gerritRepo := gerrits.NewRepository(awsSession, stage)
templateRepo := template.NewRepository(awsSession, stage)
approvalListRepo := approval_list.NewRepository(awsSession, stage)
v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
eventsRepo := events.NewRepository(awsSession, stage)
v1ProjectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
- v1CLAGroupRepo := project.NewRepository(awsSession, stage, repositoriesRepo, gerritRepo, v1ProjectClaGroupRepo)
+ v1CLAGroupRepo := project.NewRepository(awsSession, stage, gitV1Repository, gerritRepo, v1ProjectClaGroupRepo)
metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, v1ProjectClaGroupRepo)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
gitlabOrganizationRepo := gitlab_organizations.NewRepository(awsSession, stage)
@@ -272,7 +273,7 @@ func server(localMode bool) http.Handler {
})
// Signature repository handler
- signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, repositoriesRepo, githubOrganizationsRepo, gerritService)
+ signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, gitV1Repository, githubOrganizationsRepo, gerritService)
// Initialize the external platform services - these are external APIs that
// we download the swagger specification, generate the models, and have
@@ -286,7 +287,7 @@ func server(localMode bool) http.Handler {
usersService := users.NewService(usersRepo, eventsService)
healthService := health.New(Version, Commit, Branch, BuildDate)
templateService := template.NewService(stage, templateRepo, docraptorClient, awsSession)
- v1ProjectService := project.NewService(v1CLAGroupRepo, repositoriesRepo, gerritRepo, v1ProjectClaGroupRepo, usersRepo)
+ v1ProjectService := project.NewService(v1CLAGroupRepo, gitV1Repository, gerritRepo, v1ProjectClaGroupRepo, usersRepo)
emailTemplateService := emails.NewEmailTemplateService(v1CLAGroupRepo, v1ProjectClaGroupRepo, v1ProjectService, configFile.CorporateConsoleV1URL, configFile.CorporateConsoleV2URL)
emailService := emails.NewService(emailTemplateService, v1ProjectService)
v2ProjectService := v2Project.NewService(v1ProjectService, v1CLAGroupRepo, v1ProjectClaGroupRepo)
@@ -296,18 +297,18 @@ func server(localMode bool) http.Handler {
v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, v1ProjectClaGroupRepo, signaturesRepo, usersService)
v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, v1ProjectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
- v1RepositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, v1ProjectClaGroupRepo)
- v2RepositoriesService := v2Repositories.NewService(repositoriesRepo, v1ProjectClaGroupRepo, githubOrganizationsRepo)
+ v1RepositoriesService := repositories.NewService(gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo)
+ v2RepositoriesService := v2Repositories.NewService(gitV1Repository, gitV2Repository, v1ProjectClaGroupRepo, githubOrganizationsRepo)
v2ClaManagerService := v2ClaManager.NewService(emailTemplateService, v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, v1ProjectClaGroupRepo)
v1ApprovalListService := approval_list.NewService(approvalListRepo, v1ProjectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, v1CLAGroupRepo, signaturesRepo, emailTemplateService, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
v2MetricsService := metrics.NewService(metricsRepo, v1ProjectClaGroupRepo)
- githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo)
+ githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, gitV1Repository, 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)
+ gitlabActivityService := gitlab_activity.NewService(gitlabOrganizationRepo, gitV1Repository, gitV2Repository, usersRepo, signaturesRepo, v1ProjectClaGroupRepo, v1CompanyRepo, signaturesRepo)
+ v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, gitV1Repository, v1ProjectClaGroupRepo, githubOrganizationsService)
+ autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
+ v2GithubActivityService := v2GithubActivity.NewService(gitV1Repository, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, v1ProjectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 4d98e3f1e..d9c4ffeac 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -25,7 +25,6 @@ type configLookupResponse struct {
// getSSMString is a generic routine to fetch the specified key value
func getSSMString(ssmClient *ssm.SSM, key string) (string, error) {
- // log.Debugf("Loading SSM parameter: %s", key)
value, err := ssmClient.GetParameter(&ssm.GetParameterInput{
Name: aws.String(key),
WithDecryption: aws.Bool(false),
diff --git a/cla-backend-go/github/github_repository.go b/cla-backend-go/github/github_repository.go
index 7025efd3d..5783570cb 100644
--- a/cla-backend-go/github/github_repository.go
+++ b/cla-backend-go/github/github_repository.go
@@ -29,7 +29,7 @@ func GetRepositoryByExternalID(ctx context.Context, installationID, id int64) (*
}
org, resp, err := client.Repositories.GetByID(ctx, id)
if err != nil {
- logging.Warnf("GetRepository %v failed. error = %s", id, err.Error())
+ logging.Warnf("GitHubGetRepository %v failed. error = %s", id, err.Error())
if resp.StatusCode == 404 {
return nil, ErrGithubRepositoryNotFound
}
diff --git a/cla-backend-go/github_organizations/mock.go b/cla-backend-go/github_organizations/mock.go
index 12e1014e9..f68e6a778 100644
--- a/cla-backend-go/github_organizations/mock.go
+++ b/cla-backend-go/github_organizations/mock.go
@@ -40,7 +40,7 @@ func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
}
// AddGitHubOrganization mocks base method
-func (m *MockRepository) AddGitHubOrganization(arg0 context.Context, arg1, arg2 string, arg3 *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+func (m *MockRepository) AddGitHubOrganization(arg0 context.Context, arg1, arg2 string, arg3 *models.GithubCreateOrganization) (*models.GithubOrganization, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddGitHubOrganization", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*models.GithubOrganization)
diff --git a/cla-backend-go/github_organizations/repository.go b/cla-backend-go/github_organizations/repository.go
index 35c104c50..42d483a24 100644
--- a/cla-backend-go/github_organizations/repository.go
+++ b/cla-backend-go/github_organizations/repository.go
@@ -37,7 +37,7 @@ var (
// RepositoryInterface interface defines the functions for the github organizations data model
type RepositoryInterface interface {
- AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
+ AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error)
GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*models.GithubOrganization, error)
@@ -64,7 +64,7 @@ func NewRepository(awsSession *session.Session, stage string) Repository {
}
// AddGitHubOrganization add github organization logic
-func (repo Repository) AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+func (repo Repository) AddGitHubOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/github_organizations/service.go b/cla-backend-go/github_organizations/service.go
index bb865139e..630e5751a 100644
--- a/cla-backend-go/github_organizations/service.go
+++ b/cla-backend-go/github_organizations/service.go
@@ -21,7 +21,7 @@ import (
// ServiceInterface contains functions of GithubOrganizations service
type ServiceInterface interface {
- AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
+ AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error)
GetGitHubOrganizations(ctx context.Context, projectSFID string) (*models.GithubOrganizations, error)
GetGitHubOrganizationsByParent(ctx context.Context, parentProjectSFID string) (*models.GithubOrganizations, error)
GetGitHubOrganizationByName(ctx context.Context, githubOrgName string) (*models.GithubOrganization, error)
@@ -33,12 +33,12 @@ type ServiceInterface interface {
// Service object/struct
type Service struct {
repo RepositoryInterface
- ghRepository repositories.Repository
+ ghRepository repositories.RepositoryInterface
claRepository projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo RepositoryInterface, ghRepository repositories.Repository, claRepository projects_cla_groups.Repository) Service {
+func NewService(repo RepositoryInterface, ghRepository repositories.RepositoryInterface, claRepository projects_cla_groups.Repository) Service {
return Service{
repo: repo,
ghRepository: ghRepository,
@@ -46,8 +46,8 @@ func NewService(repo RepositoryInterface, ghRepository repositories.Repository,
}
}
-// AddGitHubOrganization adds the github organization for the specified project
-func (s Service) AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+// AddGitHubOrganization adds the GitHub organization for the specified project
+func (s Service) AddGitHubOrganization(ctx context.Context, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -190,7 +190,7 @@ func (s Service) DeleteGitHubOrganization(ctx context.Context, projectSFID strin
return projErr
}
- err := s.ghRepository.DisableRepositoriesOfGithubOrganization(ctx, parentProjectSFID, githubOrgName)
+ err := s.ghRepository.GitHubDisableRepositoriesOfOrganization(ctx, parentProjectSFID, githubOrgName)
if err != nil {
log.WithFields(f).Warnf("problem disabling repositories for github organizations, error: %+v", projErr)
return err
diff --git a/cla-backend-go/project/helpers.go b/cla-backend-go/project/helpers.go
index e09f3f4eb..98369f2f2 100644
--- a/cla-backend-go/project/helpers.go
+++ b/cla-backend-go/project/helpers.go
@@ -81,7 +81,7 @@ func (s service) fillRepoInfo(ctx context.Context, project *models.ClaGroup) {
go func() {
defer wg.Done()
var err error
- ghrepos, err = s.repositoriesRepo.GetCLAGroupRepositoriesGroupByOrgs(ctx, project.ProjectID, true)
+ ghrepos, err = s.repositoriesRepo.GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx, project.ProjectID, true)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get github repositories for cla group ID: %s", project.ProjectID)
return
diff --git a/cla-backend-go/project/repository.go b/cla-backend-go/project/repository.go
index 1250821ab..fa718ba9d 100644
--- a/cla-backend-go/project/repository.go
+++ b/cla-backend-go/project/repository.go
@@ -60,7 +60,7 @@ type ProjectRepository interface { //nolint
}
// NewRepository creates instance of project repository
-func NewRepository(awsSession *session.Session, stage string, ghRepo repositories.Repository, gerritRepo gerrits.Repository, projectClaGroupRepo projects_cla_groups.Repository) ProjectRepository {
+func NewRepository(awsSession *session.Session, stage string, ghRepo repositories.RepositoryInterface, gerritRepo gerrits.Repository, projectClaGroupRepo projects_cla_groups.Repository) ProjectRepository {
return &repo{
dynamoDBClient: dynamodb.New(awsSession),
stage: stage,
@@ -74,7 +74,7 @@ func NewRepository(awsSession *session.Session, stage string, ghRepo repositorie
type repo struct {
stage string
dynamoDBClient *dynamodb.DynamoDB
- ghRepo repositories.Repository
+ ghRepo repositories.RepositoryInterface
gerritRepo gerrits.Repository
projectClaGroupRepo projects_cla_groups.Repository
claGroupTable string
@@ -856,7 +856,7 @@ func (repo *repo) buildCLAGroupModel(ctx context.Context, dbModel DBProjectModel
go func() {
defer wg.Done()
var err error
- ghOrgs, err = repo.ghRepo.GetCLAGroupRepositoriesGroupByOrgs(ctx, dbModel.ProjectID, true)
+ ghOrgs, err = repo.ghRepo.GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx, dbModel.ProjectID, true)
if err != nil {
log.Warnf("buildPCLAGroupModel - unable to load GH organizations by project ID: %s, error: %+v",
dbModel.ProjectID, err)
diff --git a/cla-backend-go/project/service.go b/cla-backend-go/project/service.go
index a861aa0ba..757019c57 100644
--- a/cla-backend-go/project/service.go
+++ b/cla-backend-go/project/service.go
@@ -43,14 +43,14 @@ type Service interface {
// service
type service struct {
repo ProjectRepository
- repositoriesRepo repositories.Repository
+ repositoriesRepo repositories.RepositoryInterface
gerritRepo gerrits.Repository
projectCLAGroupRepo projects_cla_groups.Repository
usersRepo users.UserRepository
}
// NewService returns an instance of the project service
-func NewService(projectRepo ProjectRepository, repositoriesRepo repositories.Repository, gerritRepo gerrits.Repository, projectCLAGroupRepo projects_cla_groups.Repository, usersRepo users.UserRepository) Service {
+func NewService(projectRepo ProjectRepository, repositoriesRepo repositories.RepositoryInterface, gerritRepo gerrits.Repository, projectCLAGroupRepo projects_cla_groups.Repository, usersRepo users.UserRepository) Service {
return service{
repo: projectRepo,
repositoriesRepo: repositoriesRepo,
diff --git a/cla-backend-go/repositories/constants.go b/cla-backend-go/repositories/constants.go
new file mode 100644
index 000000000..a525fd0ad
--- /dev/null
+++ b/cla-backend-go/repositories/constants.go
@@ -0,0 +1,49 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package repositories
+
+// RepositoryNameColumn constant
+const RepositoryNameColumn = "repository_name"
+
+// RepositoryTypeColumn constant
+const RepositoryTypeColumn = "repository_type"
+
+// RepositoryProjectIDColumn constant
+const RepositoryProjectIDColumn = "project_sfid"
+
+// RepositoryCLAGroupIDColumn constant
+const RepositoryCLAGroupIDColumn = "repository_project_id"
+
+// RepositoryEnabledColumn constant
+const RepositoryEnabledColumn = "enabled"
+
+// RepositoryNoteColumn constant
+const RepositoryNoteColumn = "note"
+
+// RepositoryDateModifiedColumn constant
+const RepositoryDateModifiedColumn = "date_modified"
+
+// RepositoryEnabled constant
+const RepositoryEnabled = "enabled"
+
+// RepositoryDisabled constant
+const RepositoryDisabled = "disabled"
+
+// RepositoryProjectIndex constant
+const RepositoryProjectIndex = "project-repository-index"
+
+// RepositoryExternalIDIndex constant
+const RepositoryExternalIDIndex = "external-repository-index"
+
+// RepositoryProjectSFIDIndex constant
+const RepositoryProjectSFIDIndex = "project-sfid-repository-index"
+
+// RepositoryProjectSFIDOrganizationNameIndex constant
+const RepositoryProjectSFIDOrganizationNameIndex = "project-sfid-repository-organization-name-index"
+
+// RepositoryOrganizationNameIndex constant
+const RepositoryOrganizationNameIndex = "repository-organization-name-index"
+
+// RepositoryNameIndex constant
+const RepositoryNameIndex = "repository-name-index"
diff --git a/cla-backend-go/repositories/mock/mock_repository.go b/cla-backend-go/repositories/mock/mock_repository.go
index a41593d36..4fbdfe01a 100644
--- a/cla-backend-go/repositories/mock/mock_repository.go
+++ b/cla-backend-go/repositories/mock/mock_repository.go
@@ -40,9 +40,9 @@ func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
}
// AddGithubRepository mocks base method
-func (m *MockRepository) AddGithubRepository(ctx context.Context, externalProjectID, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
+func (m *MockRepository) GitHubAddRepository(ctx context.Context, externalProjectID, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddGithubRepository", ctx, externalProjectID, projectSFID, input)
+ ret := m.ctrl.Call(m, "GitHubAddRepository", ctx, externalProjectID, projectSFID, input)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -51,13 +51,13 @@ func (m *MockRepository) AddGithubRepository(ctx context.Context, externalProjec
// AddGithubRepository indicates an expected call of AddGithubRepository
func (mr *MockRepositoryMockRecorder) AddGithubRepository(ctx, externalProjectID, projectSFID, input interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubRepository", reflect.TypeOf((*MockRepository)(nil).AddGithubRepository), ctx, externalProjectID, projectSFID, input)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubAddRepository", reflect.TypeOf((*MockRepository)(nil).GitHubAddRepository), ctx, externalProjectID, projectSFID, input)
}
// UpdateGithubRepository mocks base method
-func (m *MockRepository) UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
+func (m *MockRepository) GitHubUpdateRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateGithubRepository", ctx, repositoryID, input)
+ ret := m.ctrl.Call(m, "GitHubUpdateRepository", ctx, repositoryID, input)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -66,13 +66,13 @@ func (m *MockRepository) UpdateGithubRepository(ctx context.Context, repositoryI
// UpdateGithubRepository indicates an expected call of UpdateGithubRepository
func (mr *MockRepositoryMockRecorder) UpdateGithubRepository(ctx, repositoryID, input interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGithubRepository", reflect.TypeOf((*MockRepository)(nil).UpdateGithubRepository), ctx, repositoryID, input)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubUpdateRepository", reflect.TypeOf((*MockRepository)(nil).GitHubUpdateRepository), ctx, repositoryID, input)
}
// UpdateClaGroupID mocks base method
-func (m *MockRepository) UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+func (m *MockRepository) GitHubUpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateClaGroupID", ctx, repositoryID, claGroupID)
+ ret := m.ctrl.Call(m, "GitHubUpdateClaGroupID", ctx, repositoryID, claGroupID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -80,13 +80,13 @@ func (m *MockRepository) UpdateClaGroupID(ctx context.Context, repositoryID, cla
// UpdateClaGroupID indicates an expected call of UpdateClaGroupID
func (mr *MockRepositoryMockRecorder) UpdateClaGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClaGroupID", reflect.TypeOf((*MockRepository)(nil).UpdateClaGroupID), ctx, repositoryID, claGroupID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubUpdateClaGroupID", reflect.TypeOf((*MockRepository)(nil).GitHubUpdateClaGroupID), ctx, repositoryID, claGroupID)
}
-// EnableRepository mocks base method
-func (m *MockRepository) EnableRepository(ctx context.Context, repositoryID string) error {
+// GitHubEnableRepository mocks base method
+func (m *MockRepository) GitHubEnableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "EnableRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubEnableRepository", ctx, repositoryID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -94,13 +94,13 @@ func (m *MockRepository) EnableRepository(ctx context.Context, repositoryID stri
// EnableRepository indicates an expected call of EnableRepository
func (mr *MockRepositoryMockRecorder) EnableRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepository", reflect.TypeOf((*MockRepository)(nil).EnableRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubEnableRepository", reflect.TypeOf((*MockRepository)(nil).GitHubEnableRepository), ctx, repositoryID)
}
// EnableRepositoryWithCLAGroupID mocks base method
-func (m *MockRepository) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+func (m *MockRepository) GitHubEnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "EnableRepositoryWithCLAGroupID", ctx, repositoryID, claGroupID)
+ ret := m.ctrl.Call(m, "GitHubEnableRepositoryWithCLAGroupID", ctx, repositoryID, claGroupID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -108,13 +108,13 @@ func (m *MockRepository) EnableRepositoryWithCLAGroupID(ctx context.Context, rep
// EnableRepositoryWithCLAGroupID indicates an expected call of EnableRepositoryWithCLAGroupID
func (mr *MockRepositoryMockRecorder) EnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockRepository)(nil).EnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubEnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockRepository)(nil).GitHubEnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
}
-// DisableRepository mocks base method
-func (m *MockRepository) DisableRepository(ctx context.Context, repositoryID string) error {
+// GitHubDisableRepository mocks base method
+func (m *MockRepository) GitHubDisableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DisableRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubDisableRepository", ctx, repositoryID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -122,13 +122,13 @@ func (m *MockRepository) DisableRepository(ctx context.Context, repositoryID str
// DisableRepository indicates an expected call of DisableRepository
func (mr *MockRepositoryMockRecorder) DisableRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableRepository", reflect.TypeOf((*MockRepository)(nil).DisableRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubDisableRepository", reflect.TypeOf((*MockRepository)(nil).GitHubDisableRepository), ctx, repositoryID)
}
// DisableRepositoriesByProjectID mocks base method
-func (m *MockRepository) DisableRepositoriesByProjectID(ctx context.Context, projectID string) error {
+func (m *MockRepository) GitHubDisableRepositoriesByProjectID(ctx context.Context, projectID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DisableRepositoriesByProjectID", ctx, projectID)
+ ret := m.ctrl.Call(m, "GitHubDisableRepositoriesByProjectID", ctx, projectID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -136,13 +136,13 @@ func (m *MockRepository) DisableRepositoriesByProjectID(ctx context.Context, pro
// DisableRepositoriesByProjectID indicates an expected call of DisableRepositoriesByProjectID
func (mr *MockRepositoryMockRecorder) DisableRepositoriesByProjectID(ctx, projectID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableRepositoriesByProjectID", reflect.TypeOf((*MockRepository)(nil).DisableRepositoriesByProjectID), ctx, projectID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubDisableRepositoriesByProjectID", reflect.TypeOf((*MockRepository)(nil).GitHubDisableRepositoriesByProjectID), ctx, projectID)
}
// DisableRepositoriesOfGithubOrganization mocks base method
-func (m *MockRepository) DisableRepositoriesOfGithubOrganization(ctx context.Context, externalProjectID, githubOrgName string) error {
+func (m *MockRepository) GitHubDisableRepositoriesOfOrganization(ctx context.Context, externalProjectID, githubOrgName string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DisableRepositoriesOfGithubOrganization", ctx, externalProjectID, githubOrgName)
+ ret := m.ctrl.Call(m, "GitHubDisableRepositoriesOfOrganization", ctx, externalProjectID, githubOrgName)
ret0, _ := ret[0].(error)
return ret0
}
@@ -150,13 +150,13 @@ func (m *MockRepository) DisableRepositoriesOfGithubOrganization(ctx context.Con
// DisableRepositoriesOfGithubOrganization indicates an expected call of DisableRepositoriesOfGithubOrganization
func (mr *MockRepositoryMockRecorder) DisableRepositoriesOfGithubOrganization(ctx, externalProjectID, githubOrgName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableRepositoriesOfGithubOrganization", reflect.TypeOf((*MockRepository)(nil).DisableRepositoriesOfGithubOrganization), ctx, externalProjectID, githubOrgName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubDisableRepositoriesOfOrganization", reflect.TypeOf((*MockRepository)(nil).GitHubDisableRepositoriesOfOrganization), ctx, externalProjectID, githubOrgName)
}
-// GetRepository mocks base method
-func (m *MockRepository) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
+// GitHubGetRepository mocks base method
+func (m *MockRepository) GitHubGetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubGetRepository", ctx, repositoryID)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -165,13 +165,13 @@ func (m *MockRepository) GetRepository(ctx context.Context, repositoryID string)
// GetRepository indicates an expected call of GetRepository
func (mr *MockRepositoryMockRecorder) GetRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepository", reflect.TypeOf((*MockRepository)(nil).GetRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepository", reflect.TypeOf((*MockRepository)(nil).GitHubGetRepository), ctx, repositoryID)
}
-// GetRepositoryByName mocks base method
-func (m *MockRepository) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
+// GitHubGetRepositoryByName mocks base method
+func (m *MockRepository) GitHubGetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoryByName", ctx, repositoryName)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoryByName", ctx, repositoryName)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -180,13 +180,13 @@ func (m *MockRepository) GetRepositoryByName(ctx context.Context, repositoryName
// GetRepositoryByName indicates an expected call of GetRepositoryByName
func (mr *MockRepositoryMockRecorder) GetRepositoryByName(ctx, repositoryName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByName", reflect.TypeOf((*MockRepository)(nil).GetRepositoryByName), ctx, repositoryName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoryByName", reflect.TypeOf((*MockRepository)(nil).GitHubGetRepositoryByName), ctx, repositoryName)
}
// GetRepositoryByGithubID mocks base method
-func (m *MockRepository) GetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
+func (m *MockRepository) GitHubGetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoryByGithubID", ctx, externalID, enabled)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoryByGithubID", ctx, externalID, enabled)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -195,13 +195,13 @@ func (m *MockRepository) GetRepositoryByGithubID(ctx context.Context, externalID
// GetRepositoryByGithubID indicates an expected call of GetRepositoryByGithubID
func (mr *MockRepositoryMockRecorder) GetRepositoryByGithubID(ctx, externalID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByGithubID", reflect.TypeOf((*MockRepository)(nil).GetRepositoryByGithubID), ctx, externalID, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoryByGithubID", reflect.TypeOf((*MockRepository)(nil).GitHubGetRepositoryByGithubID), ctx, externalID, enabled)
}
// GetRepositoriesByCLAGroup mocks base method
-func (m *MockRepository) GetRepositoriesByCLAGroup(ctx context.Context, claGroup string, enabled bool) ([]*models.GithubRepository, error) {
+func (m *MockRepository) GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroup string, enabled bool) ([]*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoriesByCLAGroup", ctx, claGroup, enabled)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoriesByCLAGroup", ctx, claGroup, enabled)
ret0, _ := ret[0].([]*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -210,13 +210,13 @@ func (m *MockRepository) GetRepositoriesByCLAGroup(ctx context.Context, claGroup
// GetRepositoriesByCLAGroup indicates an expected call of GetRepositoriesByCLAGroup
func (mr *MockRepositoryMockRecorder) GetRepositoriesByCLAGroup(ctx, claGroup, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoriesByCLAGroup", reflect.TypeOf((*MockRepository)(nil).GetRepositoriesByCLAGroup), ctx, claGroup, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoriesByCLAGroup", reflect.TypeOf((*MockRepository)(nil).GitHubGetRepositoriesByCLAGroup), ctx, claGroup, enabled)
}
// GetRepositoriesByOrganizationName mocks base method
-func (m *MockRepository) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
+func (m *MockRepository) GitHubGetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoriesByOrganizationName", ctx, gitHubOrgName)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoriesByOrganizationName", ctx, gitHubOrgName)
ret0, _ := ret[0].([]*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -225,13 +225,13 @@ func (m *MockRepository) GetRepositoriesByOrganizationName(ctx context.Context,
// GetRepositoriesByOrganizationName indicates an expected call of GetRepositoriesByOrganizationName
func (mr *MockRepositoryMockRecorder) GetRepositoriesByOrganizationName(ctx, gitHubOrgName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoriesByOrganizationName", reflect.TypeOf((*MockRepository)(nil).GetRepositoriesByOrganizationName), ctx, gitHubOrgName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoriesByOrganizationName", reflect.TypeOf((*MockRepository)(nil).GitHubGetRepositoriesByOrganizationName), ctx, gitHubOrgName)
}
// GetCLAGroupRepositoriesGroupByOrgs mocks base method
-func (m *MockRepository) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error) {
+func (m *MockRepository) GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetCLAGroupRepositoriesGroupByOrgs", ctx, projectID, enabled)
+ ret := m.ctrl.Call(m, "GitHubGetCLAGroupRepositoriesGroupByOrgs", ctx, projectID, enabled)
ret0, _ := ret[0].([]*models.GithubRepositoriesGroupByOrgs)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -240,14 +240,14 @@ func (m *MockRepository) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context,
// GetCLAGroupRepositoriesGroupByOrgs indicates an expected call of GetCLAGroupRepositoriesGroupByOrgs
func (mr *MockRepositoryMockRecorder) GetCLAGroupRepositoriesGroupByOrgs(ctx, projectID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCLAGroupRepositoriesGroupByOrgs", reflect.TypeOf((*MockRepository)(nil).GetCLAGroupRepositoriesGroupByOrgs), ctx, projectID, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetCLAGroupRepositoriesGroupByOrgs", reflect.TypeOf((*MockRepository)(nil).GitHubGetCLAGroupRepositoriesGroupByOrgs), ctx, projectID, enabled)
}
-// ListProjectRepositories mocks base method
-func (m *MockRepository) ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
+// GitHubListProjectRepositories mocks base method
+func (m *MockRepository) GitHubListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ListProjectRepositories", ctx, projectSFID, enabled)
- ret0, _ := ret[0].(*models.ListGithubRepositories)
+ ret := m.ctrl.Call(m, "GitHubListProjectRepositories", ctx, projectSFID, enabled)
+ ret0, _ := ret[0].(*models.GithubListRepositories)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -255,5 +255,5 @@ func (m *MockRepository) ListProjectRepositories(ctx context.Context, projectSFI
// ListProjectRepositories indicates an expected call of ListProjectRepositories
func (mr *MockRepositoryMockRecorder) ListProjectRepositories(ctx, projectSFID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectRepositories", reflect.TypeOf((*MockRepository)(nil).ListProjectRepositories), ctx, projectSFID, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubListProjectRepositories", reflect.TypeOf((*MockRepository)(nil).GitHubListProjectRepositories), ctx, projectSFID, enabled)
}
diff --git a/cla-backend-go/repositories/mock/mock_service.go b/cla-backend-go/repositories/mock/mock_service.go
index 5363e52a2..648d30f91 100644
--- a/cla-backend-go/repositories/mock/mock_service.go
+++ b/cla-backend-go/repositories/mock/mock_service.go
@@ -42,7 +42,7 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder {
// AddGithubRepository mocks base method
func (m *MockService) AddGithubRepository(ctx context.Context, externalProjectID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddGithubRepository", ctx, externalProjectID, input)
+ ret := m.ctrl.Call(m, "GitHubAddRepository", ctx, externalProjectID, input)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -51,13 +51,13 @@ func (m *MockService) AddGithubRepository(ctx context.Context, externalProjectID
// AddGithubRepository indicates an expected call of AddGithubRepository
func (mr *MockServiceMockRecorder) AddGithubRepository(ctx, externalProjectID, input interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddGithubRepository", reflect.TypeOf((*MockService)(nil).AddGithubRepository), ctx, externalProjectID, input)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubAddRepository", reflect.TypeOf((*MockService)(nil).AddGithubRepository), ctx, externalProjectID, input)
}
// EnableRepository mocks base method
func (m *MockService) EnableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "EnableRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubEnableRepository", ctx, repositoryID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -65,13 +65,13 @@ func (m *MockService) EnableRepository(ctx context.Context, repositoryID string)
// EnableRepository indicates an expected call of EnableRepository
func (mr *MockServiceMockRecorder) EnableRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepository", reflect.TypeOf((*MockService)(nil).EnableRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubEnableRepository", reflect.TypeOf((*MockService)(nil).EnableRepository), ctx, repositoryID)
}
// EnableRepositoryWithCLAGroupID mocks base method
func (m *MockService) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "EnableRepositoryWithCLAGroupID", ctx, repositoryID, claGroupID)
+ ret := m.ctrl.Call(m, "GitHubEnableRepositoryWithCLAGroupID", ctx, repositoryID, claGroupID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -79,13 +79,13 @@ func (m *MockService) EnableRepositoryWithCLAGroupID(ctx context.Context, reposi
// EnableRepositoryWithCLAGroupID indicates an expected call of EnableRepositoryWithCLAGroupID
func (mr *MockServiceMockRecorder) EnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockService)(nil).EnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubEnableRepositoryWithCLAGroupID", reflect.TypeOf((*MockService)(nil).EnableRepositoryWithCLAGroupID), ctx, repositoryID, claGroupID)
}
// DisableRepository mocks base method
func (m *MockService) DisableRepository(ctx context.Context, repositoryID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DisableRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubDisableRepository", ctx, repositoryID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -93,13 +93,13 @@ func (m *MockService) DisableRepository(ctx context.Context, repositoryID string
// DisableRepository indicates an expected call of DisableRepository
func (mr *MockServiceMockRecorder) DisableRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableRepository", reflect.TypeOf((*MockService)(nil).DisableRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubDisableRepository", reflect.TypeOf((*MockService)(nil).DisableRepository), ctx, repositoryID)
}
// UpdateClaGroupID mocks base method
func (m *MockService) UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateClaGroupID", ctx, repositoryID, claGroupID)
+ ret := m.ctrl.Call(m, "GitHubUpdateClaGroupID", ctx, repositoryID, claGroupID)
ret0, _ := ret[0].(error)
return ret0
}
@@ -107,14 +107,14 @@ func (m *MockService) UpdateClaGroupID(ctx context.Context, repositoryID, claGro
// UpdateClaGroupID indicates an expected call of UpdateClaGroupID
func (mr *MockServiceMockRecorder) UpdateClaGroupID(ctx, repositoryID, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClaGroupID", reflect.TypeOf((*MockService)(nil).UpdateClaGroupID), ctx, repositoryID, claGroupID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubUpdateClaGroupID", reflect.TypeOf((*MockService)(nil).UpdateClaGroupID), ctx, repositoryID, claGroupID)
}
// ListProjectRepositories mocks base method
-func (m *MockService) ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.ListGithubRepositories, error) {
+func (m *MockService) ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.GithubListRepositories, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ListProjectRepositories", ctx, externalProjectID, enabled)
- ret0, _ := ret[0].(*models.ListGithubRepositories)
+ ret := m.ctrl.Call(m, "GitHubListProjectRepositories", ctx, externalProjectID, enabled)
+ ret0, _ := ret[0].(*models.GithubListRepositories)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -122,13 +122,13 @@ func (m *MockService) ListProjectRepositories(ctx context.Context, externalProje
// ListProjectRepositories indicates an expected call of ListProjectRepositories
func (mr *MockServiceMockRecorder) ListProjectRepositories(ctx, externalProjectID, enabled interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectRepositories", reflect.TypeOf((*MockService)(nil).ListProjectRepositories), ctx, externalProjectID, enabled)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubListProjectRepositories", reflect.TypeOf((*MockService)(nil).ListProjectRepositories), ctx, externalProjectID, enabled)
}
// GetRepository mocks base method
func (m *MockService) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepository", ctx, repositoryID)
+ ret := m.ctrl.Call(m, "GitHubGetRepository", ctx, repositoryID)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -137,14 +137,14 @@ func (m *MockService) GetRepository(ctx context.Context, repositoryID string) (*
// GetRepository indicates an expected call of GetRepository
func (mr *MockServiceMockRecorder) GetRepository(ctx, repositoryID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepository", reflect.TypeOf((*MockService)(nil).GetRepository), ctx, repositoryID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepository", reflect.TypeOf((*MockService)(nil).GetRepository), ctx, repositoryID)
}
// GetRepositoryByProjectSFID mocks base method
-func (m *MockService) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
+func (m *MockService) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRepositoryByProjectSFID", ctx, projectSFID, enabled)
- ret0, _ := ret[0].(*models.ListGithubRepositories)
+ ret0, _ := ret[0].(*models.GithubListRepositories)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -158,7 +158,7 @@ func (mr *MockServiceMockRecorder) GetRepositoryByProjectSFID(ctx, projectSFID,
// GetRepositoryByName mocks base method
func (m *MockService) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoryByName", ctx, repositoryName)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoryByName", ctx, repositoryName)
ret0, _ := ret[0].(*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -167,13 +167,13 @@ func (m *MockService) GetRepositoryByName(ctx context.Context, repositoryName st
// GetRepositoryByName indicates an expected call of GetRepositoryByName
func (mr *MockServiceMockRecorder) GetRepositoryByName(ctx, repositoryName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoryByName", reflect.TypeOf((*MockService)(nil).GetRepositoryByName), ctx, repositoryName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoryByName", reflect.TypeOf((*MockService)(nil).GetRepositoryByName), ctx, repositoryName)
}
// DisableRepositoriesByProjectID mocks base method
func (m *MockService) DisableRepositoriesByProjectID(ctx context.Context, projectID string) (int, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DisableRepositoriesByProjectID", ctx, projectID)
+ ret := m.ctrl.Call(m, "GitHubDisableRepositoriesByProjectID", ctx, projectID)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -182,13 +182,13 @@ func (m *MockService) DisableRepositoriesByProjectID(ctx context.Context, projec
// DisableRepositoriesByProjectID indicates an expected call of DisableRepositoriesByProjectID
func (mr *MockServiceMockRecorder) DisableRepositoriesByProjectID(ctx, projectID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableRepositoriesByProjectID", reflect.TypeOf((*MockService)(nil).DisableRepositoriesByProjectID), ctx, projectID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubDisableRepositoriesByProjectID", reflect.TypeOf((*MockService)(nil).DisableRepositoriesByProjectID), ctx, projectID)
}
// GetRepositoriesByCLAGroup mocks base method
func (m *MockService) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoriesByCLAGroup", ctx, claGroupID)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoriesByCLAGroup", ctx, claGroupID)
ret0, _ := ret[0].([]*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -197,13 +197,13 @@ func (m *MockService) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID
// GetRepositoriesByCLAGroup indicates an expected call of GetRepositoriesByCLAGroup
func (mr *MockServiceMockRecorder) GetRepositoriesByCLAGroup(ctx, claGroupID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoriesByCLAGroup", reflect.TypeOf((*MockService)(nil).GetRepositoriesByCLAGroup), ctx, claGroupID)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoriesByCLAGroup", reflect.TypeOf((*MockService)(nil).GetRepositoriesByCLAGroup), ctx, claGroupID)
}
// GetRepositoriesByOrganizationName mocks base method
func (m *MockService) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetRepositoriesByOrganizationName", ctx, gitHubOrgName)
+ ret := m.ctrl.Call(m, "GitHubGetRepositoriesByOrganizationName", ctx, gitHubOrgName)
ret0, _ := ret[0].([]*models.GithubRepository)
ret1, _ := ret[1].(error)
return ret0, ret1
@@ -212,7 +212,7 @@ func (m *MockService) GetRepositoriesByOrganizationName(ctx context.Context, git
// GetRepositoriesByOrganizationName indicates an expected call of GetRepositoriesByOrganizationName
func (mr *MockServiceMockRecorder) GetRepositoriesByOrganizationName(ctx, gitHubOrgName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRepositoriesByOrganizationName", reflect.TypeOf((*MockService)(nil).GetRepositoriesByOrganizationName), ctx, gitHubOrgName)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GitHubGetRepositoriesByOrganizationName", reflect.TypeOf((*MockService)(nil).GetRepositoriesByOrganizationName), ctx, gitHubOrgName)
}
// MockGithubOrgRepo is a mock of GithubOrgRepo interface
diff --git a/cla-backend-go/repositories/models.go b/cla-backend-go/repositories/models.go
index 38398f3a4..ddde2b9e7 100644
--- a/cla-backend-go/repositories/models.go
+++ b/cla-backend-go/repositories/models.go
@@ -3,7 +3,12 @@
package repositories
-import "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+import (
+ "strconv"
+
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+)
// RepositoryDBModel represent repositories table
type RepositoryDBModel struct {
@@ -13,7 +18,7 @@ type RepositoryDBModel struct {
RepositoryID string `dynamodbav:"repository_id" json:"repository_id,omitempty"`
RepositoryName string `dynamodbav:"repository_name" json:"repository_name,omitempty"`
RepositoryOrganizationName string `dynamodbav:"repository_organization_name" json:"repository_organization_name,omitempty"`
- RepositoryProjectID string `dynamodbav:"repository_project_id" json:"repository_project_id,omitempty"`
+ RepositoryCLAGroupID string `dynamodbav:"repository_project_id" json:"repository_project_id,omitempty"`
RepositorySfdcID string `dynamodbav:"repository_sfdc_id" json:"repository_sfdc_id,omitempty"`
RepositoryType string `dynamodbav:"repository_type" json:"repository_type,omitempty"`
RepositoryURL string `dynamodbav:"repository_url" json:"repository_url,omitempty"`
@@ -26,25 +31,31 @@ type RepositoryDBModel struct {
func convertModels(dbModels []*RepositoryDBModel) []*models.GithubRepository {
var responseModels []*models.GithubRepository
for _, dbModel := range dbModels {
- responseModels = append(responseModels, dbModel.toModel())
+ responseModels = append(responseModels, dbModel.ToGitHubModel())
}
return responseModels
}
-func (gr *RepositoryDBModel) toModel() *models.GithubRepository {
+// ToGitHubModel returns the database model to a GitHub repository model suitable for marshalling to the client
+func (gr *RepositoryDBModel) ToGitHubModel() *models.GithubRepository {
+ gitLabExternalID, err := strconv.ParseInt(gr.RepositoryExternalID, 10, 64)
+ if err != nil {
+ log.WithError(err).Warnf("unable to convert repository external ID to an int64 value: %s", gr.RepositoryExternalID)
+ return nil
+ }
+
return &models.GithubRepository{
DateCreated: gr.DateCreated,
DateModified: gr.DateModified,
- RepositoryExternalID: gr.RepositoryExternalID,
+ RepositoryExternalID: gitLabExternalID,
RepositoryID: gr.RepositoryID,
RepositoryName: gr.RepositoryName,
RepositoryOrganizationName: gr.RepositoryOrganizationName,
- RepositoryProjectID: gr.RepositoryProjectID,
- RepositorySfdcID: gr.RepositorySfdcID,
+ RepositoryProjectSfid: gr.RepositorySfdcID,
RepositoryType: gr.RepositoryType,
RepositoryURL: gr.RepositoryURL,
- ProjectSFID: gr.ProjectSFID,
+ RepositoryClaGroupID: gr.RepositoryCLAGroupID,
Enabled: gr.Enabled,
Note: gr.Note,
Version: gr.Version,
diff --git a/cla-backend-go/repositories/repository.go b/cla-backend-go/repositories/repository.go
index 33212de84..3289d2a1c 100644
--- a/cla-backend-go/repositories/repository.go
+++ b/cla-backend-go/repositories/repository.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "strconv"
"strings"
"github.com/sirupsen/logrus"
@@ -30,60 +31,48 @@ import (
// index
const (
repositoryEnabledColumn = "enabled"
-
- // RepositoryEnabled flag
- RepositoryEnabled = "enabled"
-
- // RepositoryDisabled flag
- RepositoryDisabled = "disabled"
-
- ProjectRepositoryIndex = "project-repository-index"
- SFDCRepositoryIndex = "sfdc-repository-index"
- ExternalRepositoryIndex = "external-repository-index"
- ProjectSFIDRepositoryOrganizationNameIndex = "project-sfid-repository-organization-name-index"
- RepositoryOrganizationNameIndex = "repository-organization-name-index"
- RepositoryNameIndex = "repository-name-index"
)
// ErrRepositoryDoesNotExist ...
var ErrRepositoryDoesNotExist = errors.New("repository does not exist")
-// Repository defines functions of V3Repositories
-type Repository interface {
- AddGithubRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
- UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
- UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error
- EnableRepository(ctx context.Context, repositoryID string) error
- EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error
- DisableRepository(ctx context.Context, repositoryID string) error
- DisableRepositoriesByProjectID(ctx context.Context, projectID string) error
- DisableRepositoriesOfGithubOrganization(ctx context.Context, externalProjectID, githubOrgName string) error
- GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error)
- GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error)
- GetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error)
- GetRepositoriesByCLAGroup(ctx context.Context, claGroup string, enabled bool) ([]*models.GithubRepository, error)
- GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error)
- GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error)
- ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error)
+// RepositoryInterface contains functions of the repositories service
+type RepositoryInterface interface {
+ GitHubAddRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
+ GitHubUpdateRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error)
+ GitHubUpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error
+ GitHubEnableRepository(ctx context.Context, repositoryID string) error
+ GitHubEnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error
+ GitHubDisableRepository(ctx context.Context, repositoryID string) error
+ GitHubDisableRepositoriesByProjectID(ctx context.Context, projectID string) error
+ GitHubDisableRepositoriesOfOrganization(ctx context.Context, externalProjectID, githubOrgName string) error
+ GitHubGetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error)
+ GitHubGetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error)
+ GitHubGetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error)
+ GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroup string, enabled bool) ([]*models.GithubRepository, error)
+ GitHubGetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error)
+ GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error)
+ GitHubListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error)
}
// NewRepository create new Repository
-func NewRepository(awsSession *session.Session, stage string) Repository {
- return &repo{
+func NewRepository(awsSession *session.Session, stage string) *Repository {
+ return &Repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
repositoryTableName: fmt.Sprintf("cla-%s-repositories", stage),
}
}
-type repo struct {
+// Repository structure
+type Repository struct {
stage string
dynamoDBClient *dynamodb.DynamoDB
repositoryTableName string
}
-// AddGithubRepository adds the specified repository
-func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
+// GitHubAddRepository adds the specified repository
+func (r *Repository) GitHubAddRepository(ctx context.Context, externalProjectID string, projectSFID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v1.repositories.repository.AddGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -96,7 +85,7 @@ func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string,
}
// Check first to see if the repository already exists
- _, err := r.GetRepositoryByGithubID(ctx, utils.StringValue(input.RepositoryExternalID), true)
+ _, err := r.GitHubGetRepositoryByGithubID(ctx, utils.StringValue(input.RepositoryExternalID), true)
if err != nil {
// Expecting Not found - no issue if not found - all other error we throw
if _, ok := err.(*utils.GitHubRepositoryNotFound); !ok {
@@ -119,7 +108,7 @@ func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string,
RepositoryID: repoID.String(),
RepositoryName: utils.StringValue(input.RepositoryName),
RepositoryOrganizationName: utils.StringValue(input.RepositoryOrganizationName),
- RepositoryProjectID: utils.StringValue(input.RepositoryProjectID),
+ RepositoryCLAGroupID: utils.StringValue(input.RepositoryProjectID),
RepositorySfdcID: externalProjectID,
RepositoryType: utils.StringValue(input.RepositoryType),
RepositoryURL: utils.StringValue(input.RepositoryURL),
@@ -144,11 +133,11 @@ func (r repo) AddGithubRepository(ctx context.Context, externalProjectID string,
return nil, err
}
- return repository.toModel(), nil
+ return repository.ToGitHubModel(), nil
}
-// UpdateGithubRepository updates the repository record for given ID
-func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
+// GitHubUpdateRepository updates the repository record for given ID
+func (r *Repository) GitHubUpdateRepository(ctx context.Context, repositoryID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
externalID := utils.StringValue(input.RepositoryExternalID)
repositoryName := utils.StringValue(input.RepositoryName)
@@ -170,14 +159,14 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
log.WithFields(f).Debugf("updating CombinedRepository : %s... ", repositoryID)
- repoModel, repoErr := r.GetRepository(ctx, repositoryID)
+ repoModel, repoErr := r.GitHubGetRepository(ctx, repositoryID)
if repoErr != nil {
log.WithFields(f).Warnf("update error locating the repository ID : %s , error: %+v ", repositoryID, repoErr)
return nil, repoErr
}
if repoModel == nil {
- log.WithFields(f).Warnf("CombinedRepository does not exist for repo: %s ", repositoryID)
+ log.WithFields(f).Warnf("CombinedRepository does not exist for *Repository: %s ", repositoryID)
return nil, ErrRepositoryDoesNotExist
}
@@ -185,7 +174,9 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
expressionAttributeValues := map[string]*dynamodb.AttributeValue{}
updateExpression := "SET "
- if externalID != "" && repoModel.RepositoryExternalID != externalID {
+ // Convert the numeric value to a string for the DB
+ externalIDStr := strconv.FormatInt(repoModel.RepositoryExternalID, 10)
+ if externalID != "" && externalIDStr != externalID {
log.WithFields(f).Debugf("adding externalID : %s ", externalID)
expressionAttributeNames["#E"] = aws.String("repository_external_id")
expressionAttributeValues[":e"] = &dynamodb.AttributeValue{S: aws.String(externalID)}
@@ -271,30 +262,31 @@ func (r *repo) UpdateGithubRepository(ctx context.Context, repositoryID string,
return nil, updateErr
}
- return r.GetRepository(ctx, repositoryID)
+ return r.GitHubGetRepository(ctx, repositoryID)
}
-// UpdateClaGroupID updates the claGroupID of the repository
-func (r *repo) UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+// GitHubUpdateClaGroupID updates the claGroupID of the repository
+func (r *Repository) GitHubUpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
return r.setClaGroupIDGithubRepository(ctx, repositoryID, claGroupID)
}
-// EnableRepository enables the repository entry
-func (r *repo) EnableRepository(ctx context.Context, repositoryID string) error {
+// GitHubEnableRepository enables the repository entry
+func (r *Repository) GitHubEnableRepository(ctx context.Context, repositoryID string) error {
return r.enableGithubRepository(ctx, repositoryID)
}
-// EnableRepositoryWithCLAGroupID enables the repository entry with the specified CLA Group ID
-func (r *repo) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+// GitHubEnableRepositoryWithCLAGroupID enables the repository entry with the specified CLA Group ID
+func (r *Repository) GitHubEnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
return r.enableGithubRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID)
}
-// DisableRepository disables the repository entry (we don't delete)
-func (r *repo) DisableRepository(ctx context.Context, repositoryID string) error {
+// GitHubDisableRepository disables the repository entry (we don't delete)
+func (r *Repository) GitHubDisableRepository(ctx context.Context, repositoryID string) error {
return r.disableGithubRepository(ctx, repositoryID)
}
-func (r *repo) DisableRepositoriesByProjectID(ctx context.Context, projectID string) error {
+// GitHubDisableRepositoriesByProjectID disables the repository by the project ID
+func (r *Repository) GitHubDisableRepositoriesByProjectID(ctx context.Context, projectID string) error {
repoModels, err := r.getProjectRepositories(ctx, projectID, true)
if err != nil {
return err
@@ -302,7 +294,7 @@ func (r *repo) DisableRepositoriesByProjectID(ctx context.Context, projectID str
// For each model...
for _, repoModel := range repoModels {
- disableErr := r.DisableRepository(ctx, repoModel.RepositoryID)
+ disableErr := r.GitHubDisableRepository(ctx, repoModel.RepositoryID)
if disableErr != nil {
return disableErr
}
@@ -311,14 +303,14 @@ func (r *repo) DisableRepositoriesByProjectID(ctx context.Context, projectID str
return nil
}
-// DisableRepositoriesOfGithubOrganization disables the repositories under the GitHub organization
-func (r repo) DisableRepositoriesOfGithubOrganization(ctx context.Context, externalProjectID, githubOrgName string) error {
+// GitHubDisableRepositoriesOfOrganization disables the repositories under the GitHub organization
+func (r *Repository) GitHubDisableRepositoriesOfOrganization(ctx context.Context, projectSFID, githubOrgName string) error {
repoModels, err := r.getRepositoriesByGithubOrg(ctx, githubOrgName)
if err != nil {
return err
}
for _, repoModel := range repoModels {
- if repoModel.RepositoryExternalID == externalProjectID || repoModel.RepositorySfdcID == externalProjectID {
+ if repoModel.RepositoryProjectSfid == projectSFID {
err = r.disableGithubRepository(ctx, repoModel.RepositoryID)
if err != nil {
return err
@@ -328,10 +320,10 @@ func (r repo) DisableRepositoriesOfGithubOrganization(ctx context.Context, exter
return nil
}
-// GetRepository by repository id
-func (r *repo) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
+// GitHubGetRepository by repository id
+func (r *Repository) GitHubGetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "v1.repositories.repository.GetRepository",
+ "functionName": "v1.repositories.repository.GitHubGetRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryID": repositoryID,
}
@@ -362,13 +354,13 @@ func (r *repo) GetRepository(ctx context.Context, repositoryID string) (*models.
return nil, err
}
- return out.toModel(), nil
+ return out.ToGitHubModel(), nil
}
-// GetRepositoryByName fetches the repository by repository name
-func (r *repo) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
+// GitHubGetRepositoryByName fetches the repository by repository name
+func (r *Repository) GitHubGetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "v1.repositories.repository.GetRepositoryByName",
+ "functionName": "v1.repositories.repository.GitHubGetRepositoryByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"repositoryName": repositoryName,
}
@@ -412,15 +404,15 @@ func (r *repo) GetRepositoryByName(ctx context.Context, repositoryName string) (
return nil, err
}
- if len(repositories) > 0 {
+ if len(repositories) > 1 {
log.WithFields(f).Warn("multiple repositories records with the same repository name")
}
- return repositories[0].toModel(), nil
+ return repositories[0].ToGitHubModel(), nil
}
-// GetRepositoryByCLAGroup gets the list of repositories based on the CLA Group ID
-func (r *repo) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) ([]*models.GithubRepository, error) {
+// GitHubGetRepositoriesByCLAGroup gets the list of repositories based on the CLA Group ID
+func (r *Repository) GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) ([]*models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v1.repositories.repository.GetRepositoryByCLAGroup",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -444,7 +436,7 @@ func (r *repo) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string,
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(r.repositoryTableName),
- IndexName: aws.String(ProjectRepositoryIndex),
+ IndexName: aws.String(RepositoryProjectIndex),
}
results, err := r.dynamoDBClient.Query(queryInput)
@@ -471,9 +463,10 @@ func (r *repo) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string,
return convertModels(repositories), nil
}
-func (r *repo) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
+// GitHubGetRepositoriesByOrganizationName gets the repositories by organization name
+func (r *Repository) GitHubGetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "v1.repositories.repository.GetRepositoriesByOrganizationName",
+ "functionName": "v1.repositories.repository.GitHubGetRepositoriesByOrganizationName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitHubOrgName": gitHubOrgName,
}
@@ -522,8 +515,8 @@ func (r *repo) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgN
return convertModels(repositories), nil
}
-// GetCLAGroupRepositoriesGroupByOrgs returns a list of GH organizations by CLA Group - enabled flag indicates that we search the enabled repositories list
-func (r repo) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error) {
+// GitHubGetCLAGroupRepositoriesGroupByOrgs returns a list of GH organizations by CLA Group - enabled flag indicates that we search the enabled repositories list
+func (r *Repository) GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepositoriesGroupByOrgs, error) {
out := make([]*models.GithubRepositoriesGroupByOrgs, 0)
outMap := make(map[string]*models.GithubRepositoriesGroupByOrgs)
ghrepos, err := r.getProjectRepositories(ctx, projectID, enabled)
@@ -544,16 +537,16 @@ func (r repo) GetCLAGroupRepositoriesGroupByOrgs(ctx context.Context, projectID
return out, nil
}
-// List github repositories of project by external/salesforce project id
-func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
+// GitHubListProjectRepositories lists GitHub repositories of project by external/salesforce project id
+func (r *Repository) GitHubListProjectRepositories(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error) {
f := logrus.Fields{
- "functionName": "v1.repositories.repository.ListProjectRepositories",
+ "functionName": "v1.repositories.repository.GitHubListProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"enabled": utils.BoolValue(enabled),
}
- out := &models.ListGithubRepositories{
+ out := &models.GithubListRepositories{
List: make([]*models.GithubRepository, 0),
}
@@ -576,7 +569,7 @@ func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, e
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(r.repositoryTableName),
- IndexName: aws.String(ProjectSFIDRepositoryOrganizationNameIndex),
+ IndexName: aws.String(RepositoryProjectSFIDOrganizationNameIndex),
}
results, err := r.dynamoDBClient.Query(queryInput)
@@ -593,13 +586,13 @@ func (r repo) ListProjectRepositories(ctx context.Context, projectSFID string, e
return nil, err
}
for _, gr := range result {
- out.List = append(out.List, gr.toModel())
+ out.List = append(out.List, gr.ToGitHubModel())
}
return out, nil
}
// getProjectRepositories returns an array of GH repositories for the specified project ID
-func (r repo) getProjectRepositories(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepository, error) {
+func (r *Repository) getProjectRepositories(ctx context.Context, projectID string, enabled bool) ([]*models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v1.repositories.repository.getProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -624,7 +617,7 @@ func (r repo) getProjectRepositories(ctx context.Context, projectID string, enab
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(r.repositoryTableName),
- IndexName: aws.String(ProjectRepositoryIndex),
+ IndexName: aws.String(RepositoryProjectIndex),
}
results, err := r.dynamoDBClient.Query(queryInput)
@@ -641,13 +634,13 @@ func (r repo) getProjectRepositories(ctx context.Context, projectID string, enab
return nil, err
}
for _, gr := range result {
- out = append(out, gr.toModel())
+ out = append(out, gr.ToGitHubModel())
}
return out, nil
}
// getRepositoriesByGithubOrg returns an array of GH repositories for the specified project ID
-func (r repo) getRepositoriesByGithubOrg(ctx context.Context, githubOrgName string) ([]*models.GithubRepository, error) {
+func (r *Repository) getRepositoriesByGithubOrg(ctx context.Context, githubOrgName string) ([]*models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v1.repositories.repository.getRepositoriesByGitHubOrg",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -686,13 +679,13 @@ func (r repo) getRepositoriesByGithubOrg(ctx context.Context, githubOrgName stri
return nil, err
}
for _, gr := range result {
- out = append(out, gr.toModel())
+ out = append(out, gr.ToGitHubModel())
}
return out, nil
}
-// GetRepositoryByGithubID fetches the repository model by its external github id
-func (r repo) GetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
+// GitHubGetRepositoryByGithubID fetches the repository model by its external GitHub id
+func (r *Repository) GitHubGetRepositoryByGithubID(ctx context.Context, externalID string, enabled bool) (*models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v1.repositories.repository.GetRepositoryByGitHubID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -719,7 +712,7 @@ func (r repo) GetRepositoryByGithubID(ctx context.Context, externalID string, en
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(r.repositoryTableName),
- IndexName: aws.String(ExternalRepositoryIndex),
+ IndexName: aws.String(RepositoryExternalIDIndex),
}
results, err := r.dynamoDBClient.Query(queryInput)
@@ -739,23 +732,23 @@ func (r repo) GetRepositoryByGithubID(ctx context.Context, externalID string, en
return nil, err
}
- return result.toModel(), nil
+ return result.ToGitHubModel(), nil
}
-func (r repo) enableGithubRepository(ctx context.Context, repositoryID string) error {
+func (r *Repository) enableGithubRepository(ctx context.Context, repositoryID string) error {
return r.setEnabledGithubRepository(ctx, repositoryID, true)
}
-func (r repo) enableGithubRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
+func (r *Repository) enableGithubRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
return r.setEnabledGithubRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID, true)
}
-func (r repo) disableGithubRepository(ctx context.Context, repositoryID string) error {
+func (r *Repository) disableGithubRepository(ctx context.Context, repositoryID string) error {
return r.setEnabledGithubRepository(ctx, repositoryID, false)
}
// setEnabledGithubRepository updates the existing repository record by setting the enabled flag to false
-func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID string, enabled bool) error {
+func (r *Repository) setEnabledGithubRepository(ctx context.Context, repositoryID string, enabled bool) error {
f := logrus.Fields{
"functionName": "v1.repositories.repository.setEnabledGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -763,8 +756,8 @@ func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID strin
"enabled": enabled,
}
- // Load the existing model - need to fetch the old note value, if available
- existingModel, getErr := r.GetRepository(ctx, repositoryID)
+ // Load the existing model - need to fetch the old values, if available
+ existingModel, getErr := r.GitHubGetRepository(ctx, repositoryID)
if getErr != nil {
log.WithFields(f).WithError(getErr).Warn("unable to load repository by repository id")
return getErr
@@ -814,7 +807,7 @@ func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID strin
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case dynamodb.ErrCodeConditionalCheckFailedException:
- return errors.New("github repository entry does not exist or repository_sfdc_id does not match with specified project id")
+ return errors.New("github repository entry does not exist or *Repositorysitory_sfdc_id does not match with specified project id")
}
}
log.WithFields(f).WithError(err).Warn("error disabling github repository")
@@ -825,7 +818,7 @@ func (r repo) setEnabledGithubRepository(ctx context.Context, repositoryID strin
}
// setEnabledGithubRepositoryWithCLAGroupID updates the existing repository record by setting the enabled flag to false
-func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string, enabled bool) error {
+func (r *Repository) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string, enabled bool) error {
f := logrus.Fields{
"functionName": "v1.repositories.repository.setEnabledGitHubRepositoryWithCLAGroupID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -835,7 +828,7 @@ func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repo
}
// Load the existing model - need to fetch the old note value, if available
- existingModel, getErr := r.GetRepository(ctx, repositoryID)
+ existingModel, getErr := r.GitHubGetRepository(ctx, repositoryID)
if getErr != nil {
log.WithFields(f).WithError(getErr).Warn("unable to load repository by repository id")
return getErr
@@ -889,7 +882,7 @@ func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repo
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case dynamodb.ErrCodeConditionalCheckFailedException:
- return errors.New("github repository entry does not exist or repository_sfdc_id does not match with specified project id")
+ return errors.New("github repository entry does not exist or *Repositorysitory_sfdc_id does not match with specified project id")
}
}
log.WithFields(f).WithError(err).Warn("error disabling github repository")
@@ -900,7 +893,7 @@ func (r repo) setEnabledGithubRepositoryWithCLAGroupID(ctx context.Context, repo
}
// setEnabledGithubRepository updates the existing repository record by setting the enabled flag to false
-func (r repo) setClaGroupIDGithubRepository(ctx context.Context, repositoryID, claGroupID string) error {
+func (r *Repository) setClaGroupIDGithubRepository(ctx context.Context, repositoryID, claGroupID string) error {
f := logrus.Fields{
"functionName": "v1.repositories.repository.setClaGroupIDGitHubRepository",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -909,7 +902,7 @@ func (r repo) setClaGroupIDGithubRepository(ctx context.Context, repositoryID, c
}
// Load the existing model - need to fetch the old note value, if available
- existingModel, getErr := r.GetRepository(ctx, repositoryID)
+ existingModel, getErr := r.GitHubGetRepository(ctx, repositoryID)
if getErr != nil {
log.WithFields(f).WithError(getErr).Warn("unable to load repository by repository id")
return getErr
@@ -956,7 +949,7 @@ func (r repo) setClaGroupIDGithubRepository(ctx context.Context, repositoryID, c
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case dynamodb.ErrCodeConditionalCheckFailedException:
- return errors.New("github repository entry does not exist or repository_sfdc_id does not match with specified project id")
+ return errors.New("github repository entry does not exist or *Repositorysitory_sfdc_id does not match with specified project id")
}
}
log.WithFields(f).WithError(err).Warn("error disabling github repository")
diff --git a/cla-backend-go/repositories/service.go b/cla-backend-go/repositories/service.go
index ba971d2f9..285cc8446 100644
--- a/cla-backend-go/repositories/service.go
+++ b/cla-backend-go/repositories/service.go
@@ -27,9 +27,9 @@ type Service interface {
EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error
DisableRepository(ctx context.Context, repositoryID string) error
UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error
- ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.ListGithubRepositories, error)
+ ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.GithubListRepositories, error)
GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error)
- GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error)
+ GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error)
GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error)
DisableRepositoriesByProjectID(ctx context.Context, projectID string) (int, error)
GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*models.GithubRepository, error)
@@ -44,13 +44,13 @@ type GithubOrgRepo interface {
}
type service struct {
- repo Repository
+ repo RepositoryInterface
ghOrgRepo GithubOrgRepo
projectsClaGroupsRepo projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo Repository, ghOrgRepo GithubOrgRepo, pcgRepo projects_cla_groups.Repository) Service {
+func NewService(repo RepositoryInterface, ghOrgRepo GithubOrgRepo, pcgRepo projects_cla_groups.Repository) Service {
return &service{
repo: repo,
ghOrgRepo: ghOrgRepo,
@@ -60,7 +60,7 @@ func NewService(repo Repository, ghOrgRepo GithubOrgRepo, pcgRepo projects_cla_g
// UpdateClaGroupID updates the claGroupID
func (s *service) UpdateClaGroupID(ctx context.Context, repositoryID, claGroupID string) error {
- return s.repo.UpdateClaGroupID(ctx, repositoryID, claGroupID)
+ return s.repo.GitHubUpdateClaGroupID(ctx, repositoryID, claGroupID)
}
func (s *service) AddGithubRepository(ctx context.Context, externalProjectID string, input *models.GithubRepositoryInput) (*models.GithubRepository, error) {
@@ -132,36 +132,36 @@ func (s *service) AddGithubRepository(ctx context.Context, externalProjectID str
return nil, enableErr
}
- return s.repo.GetRepository(ctx, existingModel.RepositoryID)
+ return s.repo.GitHubGetRepository(ctx, existingModel.RepositoryID)
}
// Doesn't exist - create it
- return s.repo.AddGithubRepository(ctx, externalProjectID, projectSFID, input)
+ return s.repo.GitHubAddRepository(ctx, externalProjectID, projectSFID, input)
}
func (s *service) EnableRepository(ctx context.Context, repositoryID string) error {
- return s.repo.EnableRepository(ctx, repositoryID)
+ return s.repo.GitHubEnableRepository(ctx, repositoryID)
}
func (s *service) EnableRepositoryWithCLAGroupID(ctx context.Context, repositoryID, claGroupID string) error {
- return s.repo.EnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID)
+ return s.repo.GitHubEnableRepositoryWithCLAGroupID(ctx, repositoryID, claGroupID)
}
func (s *service) DisableRepository(ctx context.Context, repositoryID string) error {
- return s.repo.DisableRepository(ctx, repositoryID)
+ return s.repo.GitHubDisableRepository(ctx, repositoryID)
}
-func (s *service) ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.ListGithubRepositories, error) {
- return s.repo.ListProjectRepositories(ctx, externalProjectID, enabled)
+func (s *service) ListProjectRepositories(ctx context.Context, externalProjectID string, enabled *bool) (*models.GithubListRepositories, error) {
+ return s.repo.GitHubListProjectRepositories(ctx, externalProjectID, enabled)
}
func (s *service) GetRepository(ctx context.Context, repositoryID string) (*models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "v1.repository.GetRepository",
+ "functionName": "v1.repository.GitHubGetRepository",
"repositoryID": repositoryID,
}
log.WithFields(f).Debug("Searching for repository...")
- ghRepo, err := s.repo.GetRepository(ctx, repositoryID)
+ ghRepo, err := s.repo.GitHubGetRepository(ctx, repositoryID)
if err != nil || ghRepo != nil {
log.WithFields(f).WithError(err).Debug("unable to get repository")
return nil, err
@@ -172,20 +172,20 @@ func (s *service) GetRepository(ctx context.Context, repositoryID string) (*mode
return ghRepo, nil
}
-func (s *service) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.ListGithubRepositories, error) {
- return s.repo.ListProjectRepositories(ctx, projectSFID, enabled)
+func (s *service) GetRepositoryByProjectSFID(ctx context.Context, projectSFID string, enabled *bool) (*models.GithubListRepositories, error) {
+ return s.repo.GitHubListProjectRepositories(ctx, projectSFID, enabled)
}
// GetRepositoryByName returns the repository by name: project-level/cla-project
func (s *service) GetRepositoryByName(ctx context.Context, repositoryName string) (*models.GithubRepository, error) {
- return s.repo.GetRepositoryByName(ctx, repositoryName)
+ return s.repo.GitHubGetRepositoryByName(ctx, repositoryName)
}
// DisableRepositoriesByProjectID disables the repositories by project ID
func (s *service) DisableRepositoriesByProjectID(ctx context.Context, projectID string) (int, error) {
var deleteErr error
// Return the list of GitHub repositories by CLA Group for those that are currently enabled
- ghOrgs, err := s.repo.GetCLAGroupRepositoriesGroupByOrgs(ctx, projectID, true)
+ ghOrgs, err := s.repo.GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx, projectID, true)
if err != nil {
return 0, err
}
@@ -193,7 +193,7 @@ func (s *service) DisableRepositoriesByProjectID(ctx context.Context, projectID
log.Debugf("Deleting repositories for project :%s", projectID)
for _, ghOrg := range ghOrgs {
for _, item := range ghOrg.List {
- deleteErr = s.repo.DisableRepository(ctx, item.RepositoryID)
+ deleteErr = s.repo.GitHubDisableRepository(ctx, item.RepositoryID)
if deleteErr != nil {
log.Warnf("Unable to remove repository: %s for project :%s error :%v", item.RepositoryID, projectID, deleteErr)
}
@@ -207,10 +207,10 @@ func (s *service) DisableRepositoriesByProjectID(ctx context.Context, projectID
// GetRepositoriesByCLAGroup returns the list of repositories for the specified CLA Group
func (s *service) GetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*models.GithubRepository, error) {
// Return the list of github repositories that are enabled
- return s.repo.GetRepositoriesByCLAGroup(ctx, claGroupID, true)
+ return s.repo.GitHubGetRepositoriesByCLAGroup(ctx, claGroupID, true)
}
// GetRepositoriesByOrganizationName get repositories by organization name
func (s *service) GetRepositoriesByOrganizationName(ctx context.Context, gitHubOrgName string) ([]*models.GithubRepository, error) {
- return s.repo.GetRepositoriesByOrganizationName(ctx, gitHubOrgName)
+ return s.repo.GitHubGetRepositoriesByOrganizationName(ctx, gitHubOrgName)
}
diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go
index 8d6477723..87329aa03 100644
--- a/cla-backend-go/signatures/repository.go
+++ b/cla-backend-go/signatures/repository.go
@@ -107,14 +107,14 @@ type repository struct {
companyRepo company.IRepository
usersRepo users.UserRepository
eventsService events.Service
- repositoriesRepo repositories.Repository
+ repositoriesRepo repositories.RepositoryInterface
ghOrgRepo github_organizations.RepositoryInterface
gerritService gerrits.Service
signatureTableName string
}
// NewRepository creates a new instance of the signature repository service
-func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.Repository, ghOrgRepo github_organizations.RepositoryInterface, gerritService gerrits.Service) SignatureRepository {
+func NewRepository(awsSession *session.Session, stage string, companyRepo company.IRepository, usersRepo users.UserRepository, eventsService events.Service, repositoriesRepo repositories.RepositoryInterface, ghOrgRepo github_organizations.RepositoryInterface, gerritService gerrits.Service) SignatureRepository {
return repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
@@ -2457,7 +2457,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Action = utils.RemoveApprovals
approvalList.Version = claGroupModel.Version
// Get repositories by CLAGroup
- repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
+ repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GitHubGetRepositoriesByCLAGroup(ctx, projectID, true)
if getRepoByCLAGroupErr != nil {
msg := fmt.Sprintf("unable to fetch repositories for cla group ID: %s ", projectID)
log.WithFields(f).WithError(getRepoByCLAGroupErr).Warn(msg)
@@ -2609,7 +2609,7 @@ func (repo repository) UpdateApprovalList(ctx context.Context, claManager *model
approvalList.Action = utils.RemoveApprovals
approvalList.Version = claGroupModel.Version
// Get repositories by CLAGroup
- repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GetRepositoriesByCLAGroup(ctx, projectID, true)
+ repositories, getRepoByCLAGroupErr := repo.repositoriesRepo.GitHubGetRepositoriesByCLAGroup(ctx, projectID, true)
if getRepoByCLAGroupErr != nil {
msg := fmt.Sprintf("unable to fetch repositories for cla group ID: %s ", projectID)
log.WithFields(f).WithError(getRepoByCLAGroupErr).Warn(msg)
diff --git a/cla-backend-go/swagger/cla.v1.yaml b/cla-backend-go/swagger/cla.v1.yaml
index dd822172b..5b57cca9d 100644
--- a/cla-backend-go/swagger/cla.v1.yaml
+++ b/cla-backend-go/swagger/cla.v1.yaml
@@ -2068,7 +2068,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/create-github-organization'
+ $ref: '#/definitions/github-create-organization'
required: true
responses:
200:
@@ -2198,7 +2198,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/list-github-repositories'
+ $ref: '#/definitions/github-list-repositories'
400:
$ref: '#/responses/invalid-request'
401:
@@ -2857,7 +2857,7 @@ definitions:
$ref: './common/create-cla-group-template.yaml'
update-github-organization:
- $ref: './common/update-github-organization.yaml'
+ $ref: './common/github-update-organization.yaml'
template-pdfs:
$ref: './common/template-pdfs.yaml'
@@ -3103,8 +3103,8 @@ definitions:
github-organizations:
$ref: './common/github-organizations.yaml'
- create-github-organization:
- $ref: './common/create-github-organization.yaml'
+ github-create-organization:
+ $ref: './common/github-create-organization.yaml'
github-organization:
$ref: './common/github-organization.yaml'
@@ -3112,8 +3112,8 @@ definitions:
github-repository-info:
$ref: './common/github-repository-info.yaml'
- list-github-repositories:
- $ref: './common/list-github-repositories.yaml'
+ github-list-repositories:
+ $ref: './common/github-list-repositories.yaml'
org-list:
$ref: './common/org-list.yaml'
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 523fbec3d..93dd7f870 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1253,7 +1253,9 @@ paths:
tags:
- template
-
+ # ---------------------------------------------------------------------------
+ # GitHub Endpoint Definitions
+ # ---------------------------------------------------------------------------
/project/{projectSFID}/github/organizations:
post:
summary: Add new GitHub Oranization in the project
@@ -1271,7 +1273,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/create-github-organization'
+ $ref: '#/definitions/github-create-organization'
required: true
responses:
'200':
@@ -1350,7 +1352,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/update-github-organization'
+ $ref: '#/definitions/github-update-organization'
required: true
responses:
'200':
@@ -1433,7 +1435,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/list-github-repositories'
+ $ref: '#/definitions/github-list-repositories'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -1467,7 +1469,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/list-github-repositories'
+ $ref: '#/definitions/github-list-repositories'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -1604,6 +1606,9 @@ paths:
tags:
- github-repositories
+ # ---------------------------------------------------------------------------
+ # GitLab Endpoint Definitions
+ # ---------------------------------------------------------------------------
/project/{projectSFID}/gitlab/organizations:
post:
summary: Add new Gitlab Organization in the project
@@ -1621,7 +1626,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/create-gitlab-organization'
+ $ref: '#/definitions/gitlab-create-organization'
required: true
responses:
'200':
@@ -1631,7 +1636,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/gitlab-organization'
+ $ref: '#/definitions/gitlab-project-organizations'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -1665,7 +1670,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/project-gitlab-organizations'
+ $ref: '#/definitions/gitlab-project-organizations'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -1700,7 +1705,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/update-gitlab-organization'
+ $ref: '#/definitions/gitlab-update-organization'
required: true
responses:
'200':
@@ -1755,6 +1760,117 @@ paths:
tags:
- gitlab-organizations
+ /project/{projectSFID}/gitlab/repositories:
+ post:
+ summary: Add a GitLab repository to the project
+ description: Endpoint to add a GitLab repository for the project
+ operationId: addProjectGitLabRepository
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ - in: body
+ name: gitlab-add-repository
+ schema:
+ $ref: '#/definitions/gitlab-add-repository'
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gitlab-repository'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '409':
+ $ref: '#/responses/conflict'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-repositories
+ get:
+ summary: Get the GitLab repositories of the project
+ description: Endpoint to fetch the list of GitLab repositories for the project
+ operationId: getProjectGitLabRepositories
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ responses:
+ '200':
+ description: 'Success'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gitlab-list-repositories'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-repositories
+
+ /project/{projectSFID}/gitlab/repositories/{repositoryID}:
+ delete:
+ summary: Remove the GitLab repository from the project
+ description: Endpoint to remove a GitLab repository from a project
+ operationId: deleteProjectGitLabRepository
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/x-acl"
+ - $ref: "#/parameters/x-username"
+ - $ref: "#/parameters/x-email"
+ - name: projectSFID
+ in: path
+ type: string
+ required: true
+ - name: repositoryID
+ in: path
+ type: string
+ required: true
+ responses:
+ '204':
+ description: 'Resource Deleted'
+ headers:
+ x-request-id:
+ type: string
+ description: The unique request ID value - assigned/set by the API Gateway based on the session
+ '400':
+ $ref: '#/responses/invalid-request'
+ '401':
+ $ref: '#/responses/unauthorized'
+ '403':
+ $ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
+ tags:
+ - gitlab-repositories
+
/cla-group/{claGroupID}/icla/signatures:
get:
summary: List individual signatures for CLA Group
@@ -4224,6 +4340,9 @@ definitions:
event:
$ref: './common/event.yaml'
+ # ---------------------------------------------------------------------------
+ # GitHub Definitions
+ # ---------------------------------------------------------------------------
github-activity-input:
type: object
required:
@@ -4310,12 +4429,45 @@ definitions:
items:
$ref: '#/definitions/github-repository-branch-protection-status-checks'
+ github-organization:
+ $ref: './common/github-organization.yaml'
+
github-repository:
$ref: './common/github-repository.yaml'
- list-github-repositories:
- $ref: './common/list-github-repositories.yaml'
+ github-create-organization:
+ $ref: './common/github-create-organization.yaml'
+
+ github-update-organization:
+ $ref: './common/github-update-organization.yaml'
+
+ github-list-repositories:
+ $ref: './common/github-list-repositories.yaml'
+
+ # ---------------------------------------------------------------------------
+ # GitLab Definitions
+ # ---------------------------------------------------------------------------
+ gitlab-organization:
+ $ref: './common/gitlab-organization.yaml'
+
+ gitlab-repository:
+ $ref: './common/github-repository.yaml'
+
+ gitlab-create-organization:
+ $ref: './common/gitlab-create-organization.yaml'
+
+ gitlab-update-organization:
+ $ref: './common/github-update-organization.yaml'
+
+ gitlab-list-repositories:
+ $ref: './common/gitlab-list-repositories.yaml'
+ gitlab-add-repository:
+ $ref: './common/gitlab-add-repository.yaml'
+
+ # ---------------------------------------------------------------------------
+ # CLA Group Definitions
+ # ---------------------------------------------------------------------------
cla-groups:
$ref: './common/cla-groups.yaml'
@@ -4325,6 +4477,9 @@ definitions:
sf-project-summary:
$ref: './common/sf-project-summary.yaml'
+ # ---------------------------------------------------------------------------
+ # CLA Template Definitions
+ # ---------------------------------------------------------------------------
template:
$ref: './common/template.yaml'
@@ -4334,24 +4489,6 @@ definitions:
template-pdfs:
$ref: './common/template-pdfs.yaml'
- github-organization:
- $ref: './common/github-organization.yaml'
-
- create-github-organization:
- $ref: './common/create-github-organization.yaml'
-
- update-github-organization:
- $ref: './common/update-github-organization.yaml'
-
- gitlab-organization:
- $ref: './common/gitlab-organization.yaml'
-
- create-gitlab-organization:
- $ref: './common/create-github-organization.yaml'
-
- update-gitlab-organization:
- $ref: './common/update-github-organization.yaml'
-
user:
$ref: './common/user.yaml'
@@ -5426,56 +5563,6 @@ definitions:
items:
$ref: '#/definitions/project-github-repository'
- project-gitlab-organizations:
- type: object
- properties:
- list:
- type: array
- items:
- $ref: '#/definitions/project-gitlab-organization'
-
- project-gitlab-organization:
- type: object
- properties:
- auto_enabled:
- type: boolean
- description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
- x-omitempty: false
- autoEnableCLAGroupID:
- type: string
- description: The CLA Group ID which is attached to the auto-enabled flag
- autoEnabledCLAGroupName:
- type: string
- description: The CLA Group name which is attached to the auto-enabled flag
- branchProtectionEnabled:
- type: boolean
- description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
- x-omitempty: false
- installationURL:
- type: string
- x-nullable: true
- format: uri
- gitlab_organization_name:
- type: string
- description: The Gitlab Organization name
- example: "kubernetes"
- # Pattern aligns with UI and other platform services including Org Service
- # \w Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^([\w\-\.]+){2,255}$'
- minLength: 2
- maxLength: 255
- connection_status:
- type: string
- enum:
- - connected
- - partial_connection
- - connection_failure
- - no_connection
- repositories:
- type: array
- items:
- $ref: '#/definitions/project-github-repository'
-
project-github-repository:
type: object
properties:
@@ -5505,6 +5592,15 @@ definitions:
- connected
- connection_failure
+ gitlab-project-organizations:
+ $ref: './common/gitlab-project-organizations.yaml'
+
+ gitlab-project-organization:
+ $ref: './common/gitlab-project-organization.yaml'
+
+ gitlab-project-repository:
+ $ref: './common/gitlab-project-repository.yaml'
+
url-object:
type: object
properties:
diff --git a/cla-backend-go/swagger/common/create-github-organization.yaml b/cla-backend-go/swagger/common/github-create-organization.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/create-github-organization.yaml
rename to cla-backend-go/swagger/common/github-create-organization.yaml
diff --git a/cla-backend-go/swagger/common/list-github-repositories.yaml b/cla-backend-go/swagger/common/github-list-repositories.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/list-github-repositories.yaml
rename to cla-backend-go/swagger/common/github-list-repositories.yaml
diff --git a/cla-backend-go/swagger/common/github-repository.yaml b/cla-backend-go/swagger/common/github-repository.yaml
index 9aebb8eb5..786e330dc 100644
--- a/cla-backend-go/swagger/common/github-repository.yaml
+++ b/cla-backend-go/swagger/common/github-repository.yaml
@@ -3,45 +3,55 @@
type: object
properties:
- dateCreated:
- type: string
- description: Created date/time
- dateModified:
- type: string
- description: Last modified date/time
- repositoryExternalID:
- type: string
- description: The repository ID from the external service
- repositoryID:
- type: string
+ repository_id:
description: The internal repository ID
- repositoryName:
+ $ref: './common/properties/internal-id.yaml'
+ repository_external_id:
+ type: integer
+ description: The repository ID from the external service, such as GitHub or GitLab
+ minimum: 1
+ example: 7
+ repository_project_sfid:
+ description: Project SFID
+ $ref: './common/properties/external-id.yaml'
+ repository_cla_group_id:
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
+ repository_name:
type: string
description: The repository name
- repositoryOrganizationName:
+ example: 'easycla-test-repo-4'
+ repository_organization_name:
type: string
description: The organization name associated with this repository
- repositoryProjectID:
- type: string
- description: The CLA Group ID associated with this repository
- repositorySfdcID:
- type: string
- repositoryType:
- type: string
- description: The repository type - typically github, gerrit or possibly gitlab
- repositoryUrl:
+ example: 'The Linux Foundation/product/EasyCLA'
+ repository_url:
type: string
description: The external repository URL
+ example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
+ repository_type:
+ type: string
+ description: the repository type
+ example: 'gitlab'
enabled:
type: boolean
- description: Flag to indicate if this repository is enabled or not. Repositories may become disabled if they have been moved or deleted from GitHub.
+ description: Flag to indicate if this repository is enabled or not. Repositories may become disabled if they have been moved or deleted from GitHub or GitLab.
x-omitempty: false
+ date_created:
+ type: string
+ example: "2020-02-06T09:31:49.245630+0000"
+ minLength: 18
+ maxLength: 64
+ date_modified:
+ type: string
+ example: "2020-02-06T09:31:49.245646+0000"
+ minLength: 18
+ maxLength: 64
note:
type: string
description: An optional note field to store any additional information about this record. Helpful for auditing.
+ example: 'optional note about the repository - migrated on MM/DD/YYYY'
version:
type: string
description: The version identifier for this repository record
- projectSFID:
- type: string
- description: The project SFID associated with this repository
+ example: 'v1'
diff --git a/cla-backend-go/swagger/common/update-github-organization.yaml b/cla-backend-go/swagger/common/github-update-organization.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/update-github-organization.yaml
rename to cla-backend-go/swagger/common/github-update-organization.yaml
diff --git a/cla-backend-go/swagger/common/gitlab-add-repository.yaml b/cla-backend-go/swagger/common/gitlab-add-repository.yaml
new file mode 100644
index 000000000..7e908ead9
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-add-repository.yaml
@@ -0,0 +1,42 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+required:
+ - repository_external_id
+ - repository_project_sfid
+ - repository_cla_group_id
+ - repository_name
+ - repository_organization_name
+ - repository_url
+properties:
+ repository_external_id:
+ type: integer
+ description: The repository ID from the external service, such as GitHub or GitLab
+ minimum: 1
+ example: 7
+ repository_project_sfid:
+ description: Project SFID
+ $ref: './common/properties/external-id.yaml'
+ repository_cla_group_id:
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
+ repository_name:
+ type: string
+ description: The repository name
+ example: 'easycla-test-repo-4'
+ repository_organization_name:
+ type: string
+ description: The organization name associated with this repository
+ example: 'The Linux Foundation/product/EasyCLA'
+ repository_url:
+ type: string
+ description: The external repository URL
+ example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
+ enabled:
+ type: boolean
+ description: Flag to indicate if this repository is enabled or not. Repositories may become disabled if they have been moved or deleted from GitHub or GitLab.
+ x-omitempty: false
+ note:
+ description: optional note added to the record
+ type: string
diff --git a/cla-backend-go/swagger/common/gitlab-create-organization.yaml b/cla-backend-go/swagger/common/gitlab-create-organization.yaml
new file mode 100644
index 000000000..0a9ea81c4
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-create-organization.yaml
@@ -0,0 +1,27 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+required:
+ - organizationName
+properties:
+ organizationName:
+ type: string
+ description: The GitLab Group/Organization name
+ example: "kubernetes"
+ # Pattern aligns with UI and other platform services including Org Service
+ # \w Any word character (alphanumeric & underscore), dashes, periods
+ pattern: '^([\w\-\.]+){2,255}$'
+ minLength: 2
+ maxLength: 255
+ autoEnabled:
+ type: boolean
+ description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
+ default: false
+ autoEnabledClaGroupID:
+ type: string
+ description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
+ branchProtectionEnabled:
+ type: boolean
+ description: Flag to indicate if this GitLab Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ default: false
diff --git a/cla-backend-go/swagger/common/gitlab-list-repositories.yaml b/cla-backend-go/swagger/common/gitlab-list-repositories.yaml
new file mode 100644
index 000000000..1e9440adc
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-list-repositories.yaml
@@ -0,0 +1,9 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/gitlab-repository'
diff --git a/cla-backend-go/swagger/common/gitlab-organizations.yaml b/cla-backend-go/swagger/common/gitlab-organizations.yaml
index 42a292a78..52aafb588 100644
--- a/cla-backend-go/swagger/common/gitlab-organizations.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organizations.yaml
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: MIT
type: object
+description: GitLab Organizations
properties:
list:
type: array
diff --git a/cla-backend-go/swagger/common/gitlab-project-organization.yaml b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
new file mode 100644
index 000000000..e1701acef
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
@@ -0,0 +1,44 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+description: GitLab Project Organization
+properties:
+ auto_enabled:
+ type: boolean
+ description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
+ x-omitempty: false
+ autoEnableCLAGroupID:
+ type: string
+ description: The CLA Group ID which is attached to the auto-enabled flag
+ autoEnabledCLAGroupName:
+ type: string
+ description: The CLA Group name which is attached to the auto-enabled flag
+ branchProtectionEnabled:
+ type: boolean
+ description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ x-omitempty: false
+ installationURL:
+ type: string
+ x-nullable: true
+ format: uri
+ gitlab_organization_name:
+ type: string
+ description: The Gitlab Organization name
+ example: "kubernetes"
+ # Pattern aligns with UI and other platform services including Org Service
+ # \w Any word character (alphanumeric & underscore), dashes, periods
+ pattern: '^([\w\-\.]+){2,255}$'
+ minLength: 2
+ maxLength: 255
+ connection_status:
+ type: string
+ enum:
+ - connected
+ - partial_connection
+ - connection_failure
+ - no_connection
+ repositories:
+ type: array
+ items:
+ $ref: '#/definitions/gitlab-project-repository'
diff --git a/cla-backend-go/swagger/common/gitlab-project-organizations.yaml b/cla-backend-go/swagger/common/gitlab-project-organizations.yaml
new file mode 100644
index 000000000..152611ecd
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-project-organizations.yaml
@@ -0,0 +1,10 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+description: GitLab Project Organizations
+properties:
+ list:
+ type: array
+ items:
+ $ref: '#/definitions/gitlab-project-organization'
diff --git a/cla-backend-go/swagger/common/gitlab-project-repository.yaml b/cla-backend-go/swagger/common/gitlab-project-repository.yaml
new file mode 100644
index 000000000..7441f2eb2
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-project-repository.yaml
@@ -0,0 +1,41 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+description: GitLab Project Repository
+properties:
+ repository_id:
+ description: Repository Internal ID
+ $ref: './common/properties/internal-id.yaml'
+ x-omitempty: false
+ repository_gitlab_id:
+ type: integer
+ description: 'Repository GitLab ID value'
+ minimum: 1
+ example: 2292
+ repository_name:
+ type: string
+ description: 'GitLab Repository/Project name'
+ x-omitempty: false
+ cla_group_id:
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
+ x-omitempty: false
+ project_id:
+ description: Project SFID
+ $ref: './common/properties/external-id.yaml'
+ x-omitempty: false
+ parent_project_id:
+ description: Parent Project SFID
+ $ref: './common/properties/external-id.yaml'
+ x-omitempty: false
+ enabled:
+ type: boolean
+ description: 'Enabled flag'
+ x-omitempty: false
+ connection_status:
+ type: string
+ description: 'Connection status for the repository, one of the supported values connected or connection_failure'
+ enum:
+ - connected
+ - connection_failure
diff --git a/cla-backend-go/swagger/common/gitlab-repository.yaml b/cla-backend-go/swagger/common/gitlab-repository.yaml
new file mode 100644
index 000000000..6af477008
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-repository.yaml
@@ -0,0 +1,57 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ repositoryID:
+ description: The internal repository ID
+ $ref: './common/properties/internal-id.yaml'
+ repository_external_id:
+ type: integer
+ description: The repository ID from the external service, such as GitHub or GitLab
+ minimum: 1
+ example: 7
+ repository_cla_group_id:
+ type: string
+ description: The CLA Group ID associated with this repository
+ repository_project_sfid:
+ description: Project SFID
+ $ref: './common/properties/external-id.yaml'
+ repository_name:
+ type: string
+ description: The repository name
+ example: 'easycla-test-repo-4'
+ repository_organization_name:
+ type: string
+ description: The organization name associated with this repository
+ example: 'The Linux Foundation/product/EasyCLA'
+ repository_url:
+ type: string
+ description: The external repository URL
+ example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
+ repository_type:
+ type: string
+ description: the repository type
+ example: 'gitlab'
+ enabled:
+ type: boolean
+ description: Flag to indicate if this repository is enabled or not. Repositories may become disabled if they have been moved or deleted from GitHub or GitLab.
+ x-omitempty: false
+ date_created:
+ type: string
+ example: "2020-02-06T09:31:49.245630+0000"
+ minLength: 18
+ maxLength: 64
+ date_modified:
+ type: string
+ example: "2020-02-06T09:31:49.245646+0000"
+ minLength: 18
+ maxLength: 64
+ note:
+ type: string
+ description: An optional note field to store any additional information about this record. Helpful for auditing.
+ example: 'optional note about the repository - migrated on MM/DD/YYYY'
+ version:
+ type: string
+ description: The version identifier for this repository record
+ example: 'v1'
diff --git a/cla-backend-go/swagger/common/gitlab-update-organization.yaml b/cla-backend-go/swagger/common/gitlab-update-organization.yaml
new file mode 100644
index 000000000..e9875eca5
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-update-organization.yaml
@@ -0,0 +1,17 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+required:
+ - autoEnabled
+properties:
+ autoEnabled:
+ type: boolean
+ description: Flag to indicate if auto-enabled flag should be enabled. Group/Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
+ autoEnabledClaGroupID:
+ type: string
+ description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the GitLab Group/Organization. If autoEnabled is on this field needs to be set as well.
+ branchProtectionEnabled:
+ type: boolean
+ description: Flag to indicate if this Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ x-omitempty: true
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index a95b42785..1f29cf8bd 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -144,6 +144,27 @@ const GitHubEmailLabel = "GitHub Email Address"
//GitHubUserLabel represents the GH username Label used for email
const GitHubUserLabel = "GitHub Username"
+// GitLab is the GitLab spelled out with the proper case
+const GitLab = "GitLab"
+
+// GitLabLower is the GitLab spelled out in lower case
+const GitLabLower = "gitlab"
+
+//GitLabRepoNotFound is a string that indicates the GitLab repository is not found
+const GitLabRepoNotFound = "GitLab repository not found"
+
+//GitLabDuplicateRepoFound is a string that indicates that duplicate GitLab repositories were found
+const GitLabDuplicateRepoFound = "Duplicate GitLab repositories were found"
+
+//GitLabRepoExists is a string that indicates the GitLab repository already exists
+const GitLabRepoExists = "GitLab repository exists"
+
+//GitLabEmailLabel represents the GitLab Email label used for email
+const GitLabEmailLabel = "GitLab Email Address"
+
+//GitLabUserLabel represents the GitLab username Label used for email
+const GitLabUserLabel = "GitLab Username"
+
//EmailLabel represents LF/EasyCLA Email address
const EmailLabel = "Email Address"
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index 62ff6d6dc..ea5f35a30 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -268,6 +268,34 @@ func (e *CompanyNotFound) Unwrap() error {
return e.Err
}
+// InvalidRepositoryTypeError is an error model for an invalid repository type
+type InvalidRepositoryTypeError struct {
+ RepositoryType string
+ RepositoryName string
+ Err error
+}
+
+// Error is an error string function for the InvalidRepositoryTypeError model
+func (e *InvalidRepositoryTypeError) Error() string {
+ msg := "Invalid repository type"
+ if e.RepositoryType != "" {
+ msg = fmt.Sprintf("%s - type: %s ", msg, e.RepositoryType)
+ }
+ if e.RepositoryName != "" {
+ msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *InvalidRepositoryTypeError) Unwrap() error {
+ return e.Err
+}
+
// GitHubRepositoryNotFound is an error model for a GitHub repository not found
type GitHubRepositoryNotFound struct {
Message string
@@ -324,6 +352,98 @@ func (e *GitHubRepositoryExists) Unwrap() error {
return e.Err
}
+// GitLabRepositoryNotFound is an error model for a GitLab repository not found
+type GitLabRepositoryNotFound struct {
+ Message string
+ RepositoryName string
+ ProjectSFID string
+ CLAGroupID string
+ Err error
+}
+
+// Error is an error string function for the GitHubRepositoryNotFound model
+func (e *GitLabRepositoryNotFound) Error() string {
+ msg := GitLabRepoNotFound
+ if e.Message != "" {
+ msg = e.Message
+ }
+ if e.RepositoryName != "" {
+ msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
+ }
+ if e.ProjectSFID != "" {
+ msg = fmt.Sprintf("%s - project SFID: %s ", msg, e.ProjectSFID)
+ }
+ if e.CLAGroupID != "" {
+ msg = fmt.Sprintf("%s - CLA Group ID: %s ", msg, e.CLAGroupID)
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *GitLabRepositoryNotFound) Unwrap() error {
+ return e.Err
+}
+
+// GitLabDuplicateRepositoriesFound is an error model for a GitLab duplicate repositories found
+type GitLabDuplicateRepositoriesFound struct {
+ Message string
+ RepositoryName string
+ Err error
+}
+
+// Error is an error string function for the GitLabDuplicateRepositoriesFound model
+func (e *GitLabDuplicateRepositoriesFound) Error() string {
+ msg := GitLabDuplicateRepoFound
+ if e.Message != "" {
+ msg = e.Message
+ }
+ if e.RepositoryName != "" {
+ msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *GitLabDuplicateRepositoriesFound) Unwrap() error {
+ return e.Err
+}
+
+// GitLabRepositoryExists is an error model for when a GitHub repository already exists
+type GitLabRepositoryExists struct {
+ Message string
+ RepositoryName string
+ Err error
+}
+
+// Error is an error string function for the GitLabRepositoryExists model
+func (e *GitLabRepositoryExists) Error() string {
+ msg := GitLabRepoNotFound
+ if e.Message != "" {
+ msg = e.Message
+ }
+ if e.RepositoryName != "" {
+ msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
+ }
+ if e.Err != nil {
+ msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
+ }
+
+ return strings.TrimSpace(msg)
+}
+
+// Unwrap method returns its contained error
+func (e *GitLabRepositoryExists) Unwrap() error {
+ return e.Err
+}
+
// CLAManagerError is an error model for when a CLA Manager error occurs
type CLAManagerError struct {
Message string
diff --git a/cla-backend-go/v2/dynamo_events/autoenable.go b/cla-backend-go/v2/dynamo_events/autoenable.go
index 1db0a8b03..0a53e1c6b 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable.go
@@ -40,14 +40,14 @@ type AutoEnableService interface {
// NewAutoEnableService creates a new AutoEnableService
func NewAutoEnableService(repositoryService repositories.Service,
- githubRepo repositories.Repository,
+ githubRepo repositories.RepositoryInterface,
githubOrgRepo github_organizations.RepositoryInterface,
claRepository projects_cla_groups.Repository,
claService project.Service,
) AutoEnableService {
return &autoEnableServiceProvider{
repositoryService: repositoryService,
- githubRepo: githubRepo,
+ gitV1Repository: githubRepo,
githubOrgRepo: githubOrgRepo,
claRepository: claRepository,
claService: claService,
@@ -58,7 +58,7 @@ func NewAutoEnableService(repositoryService repositories.Service,
// having it separated in its own struct makes testing easier.
type autoEnableServiceProvider struct {
repositoryService repositories.Service
- githubRepo repositories.Repository
+ gitV1Repository repositories.RepositoryInterface
githubOrgRepo github_organizations.RepositoryInterface
claRepository projects_cla_groups.Repository
claService project.Service
@@ -119,7 +119,7 @@ func (a *autoEnableServiceProvider) CreateAutoEnabledRepository(repo *github.Rep
externalProjectID := claGroupModel.ProjectExternalID
- repoModel, err := a.githubRepo.AddGithubRepository(ctx, externalProjectID, projectSFID, &models.GithubRepositoryInput{
+ repoModel, err := a.gitV1Repository.GitHubAddRepository(ctx, externalProjectID, projectSFID, &models.GithubRepositoryInput{
RepositoryProjectID: swag.String(claGroupID),
RepositoryName: swag.String(repositoryFullName),
RepositoryType: swag.String("github"),
@@ -161,11 +161,11 @@ func (a *autoEnableServiceProvider) AutoEnabledForGithubOrg(f logrus.Fields, git
}
for _, repo := range repos.List {
- if repo.RepositoryProjectID == claGroupID {
+ if repo.RepositoryClaGroupID == claGroupID {
continue
}
- repo.RepositoryProjectID = claGroupID
+ repo.RepositoryClaGroupID = claGroupID
if err := a.repositoryService.UpdateClaGroupID(context.Background(), repo.RepositoryID, claGroupID); err != nil {
log.WithFields(f).Warnf("updating claGroupID for repository : %s failed : %v", repo.RepositoryID, err)
return err
@@ -278,7 +278,7 @@ See: GitHub CombinedRepository -> Settings -> Branches -> Branch Protection Rule
// DetermineClaGroupID checks if AutoEnabledClaGroupID is set then returns it (high precedence) otherwise tries to determine
// the autoEnabled claGroupID by guessing from existing repos
-func DetermineClaGroupID(f logrus.Fields, gitHubOrg *models.GithubOrganization, repos *models.ListGithubRepositories) (string, error) {
+func DetermineClaGroupID(f logrus.Fields, gitHubOrg *models.GithubOrganization, repos *models.GithubListRepositories) (string, error) {
if gitHubOrg.AutoEnabledClaGroupID != "" {
return gitHubOrg.AutoEnabledClaGroupID, nil
}
@@ -289,12 +289,12 @@ func DetermineClaGroupID(f logrus.Fields, gitHubOrg *models.GithubOrganization,
// check if any of the repos is member to more than one cla group, in general shouldn't happen
var claGroupID string
for _, repo := range repos.List {
- if repo.RepositoryProjectID == "" || repo.ProjectSFID == "" {
+ if repo.RepositoryClaGroupID == "" || repo.RepositoryProjectSfid == "" {
continue
}
- claGroupSet[repo.RepositoryProjectID] = true
- sfidSet[repo.ProjectSFID] = true
- claGroupID = repo.RepositoryProjectID
+ claGroupID = repo.RepositoryClaGroupID
+ claGroupSet[repo.RepositoryClaGroupID] = true
+ sfidSet[repo.RepositoryProjectSfid] = true
}
if len(claGroupSet) == 0 && len(sfidSet) == 0 {
diff --git a/cla-backend-go/v2/dynamo_events/autoenable_test.go b/cla-backend-go/v2/dynamo_events/autoenable_test.go
index 061408561..fda3ce198 100644
--- a/cla-backend-go/v2/dynamo_events/autoenable_test.go
+++ b/cla-backend-go/v2/dynamo_events/autoenable_test.go
@@ -58,7 +58,7 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{}, nil)
+ Return(&models.GithubListRepositories{}, nil)
},
},
{
@@ -71,15 +71,15 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{
+ Return(&models.GithubListRepositories{
List: []*models.GithubRepository{
{
- RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
- ProjectSFID: externalProjectID,
+ RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
+ RepositoryProjectSfid: externalProjectID,
},
{
- RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
- ProjectSFID: externalProjectID,
+ RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
+ RepositoryProjectSfid: externalProjectID,
},
},
}, nil)
@@ -96,17 +96,17 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{
+ Return(&models.GithubListRepositories{
List: []*models.GithubRepository{
{
- RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
- RepositoryProjectID: claGroupID,
- ProjectSFID: externalProjectID,
+ RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
+ RepositoryClaGroupID: claGroupID,
+ RepositoryProjectSfid: externalProjectID,
},
{
- RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
- RepositoryProjectID: "anotherclagroup",
- ProjectSFID: externalProjectID,
+ RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
+ RepositoryClaGroupID: "anotherclagroup",
+ RepositoryProjectSfid: externalProjectID,
},
},
}, nil)
@@ -124,15 +124,15 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{
+ Return(&models.GithubListRepositories{
List: []*models.GithubRepository{
{
- RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
- ProjectSFID: externalProjectID,
+ RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
+ RepositoryProjectSfid: externalProjectID,
},
{
- RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
- ProjectSFID: externalProjectID,
+ RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
+ RepositoryProjectSfid: externalProjectID,
},
},
}, nil)
@@ -156,17 +156,17 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{
+ Return(&models.GithubListRepositories{
List: []*models.GithubRepository{
{
- RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
- ProjectSFID: externalProjectID,
- RepositoryProjectID: claGroupID,
+ RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
+ RepositoryProjectSfid: externalProjectID,
+ RepositoryClaGroupID: claGroupID,
},
{
- RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
- ProjectSFID: externalProjectID,
- RepositoryProjectID: claGroupID,
+ RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
+ RepositoryProjectSfid: externalProjectID,
+ RepositoryClaGroupID: claGroupID,
},
},
}, nil)
@@ -182,16 +182,16 @@ func TestAutoEnableServiceProvider_AutoEnabledForGithubOrg(t *testing.T) {
m.
EXPECT().
ListProjectRepositories(gomock.Any(), externalProjectID, &enabled).
- Return(&models.ListGithubRepositories{
+ Return(&models.GithubListRepositories{
List: []*models.GithubRepository{
{
- RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
- ProjectSFID: externalProjectID,
+ RepositoryID: "d7c1050b-2f32-44ea-bad2-3c8ff980ccd4",
+ RepositoryProjectSfid: externalProjectID,
},
{
- RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
- ProjectSFID: externalProjectID,
- RepositoryProjectID: claGroupID,
+ RepositoryID: "b42216b4-8f6d-41c0-8cde-7b2acbf0656a",
+ RepositoryProjectSfid: externalProjectID,
+ RepositoryClaGroupID: claGroupID,
},
},
}, nil)
diff --git a/cla-backend-go/v2/dynamo_events/github_repository.go b/cla-backend-go/v2/dynamo_events/github_repository.go
index 3044b8504..dc2e16988 100644
--- a/cla-backend-go/v2/dynamo_events/github_repository.go
+++ b/cla-backend-go/v2/dynamo_events/github_repository.go
@@ -41,7 +41,7 @@ func (s *service) GithubRepoModifyAddEvent(event events.DynamoDBEventRecord) err
log.WithFields(f).Warnf("problem unmarshalling old repository model event, error: %+v", err)
return err
}
- claGroupID = oldRepoModel.RepositoryProjectID
+ claGroupID = oldRepoModel.RepositoryCLAGroupID
projectSFID = oldRepoModel.ProjectSFID
parentProjectSFID = oldRepoModel.RepositorySfdcID
} else {
@@ -51,7 +51,7 @@ func (s *service) GithubRepoModifyAddEvent(event events.DynamoDBEventRecord) err
log.WithFields(f).Warnf("problem unmarshalling the new repository model event, error: %+v", err)
return err
}
- claGroupID = newRepoModel.RepositoryProjectID
+ claGroupID = newRepoModel.RepositoryCLAGroupID
projectSFID = newRepoModel.ProjectSFID
parentProjectSFID = newRepoModel.RepositorySfdcID
}
diff --git a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
index ce536e51b..dbff5ff47 100644
--- a/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
+++ b/cla-backend-go/v2/dynamo_events/projects_cla_groups.go
@@ -209,8 +209,8 @@ func (s *service) ProjectUnenrolledDisableRepositoryHandler(event events.DynamoD
// For each GitHub repository...
for _, gitHubRepo := range gitHubRepos.List {
- log.WithFields(f).Debugf("disabling github repository: %s with id: %s for project with sfid: %s",
- gitHubRepo.RepositoryName, gitHubRepo.RepositoryID, gitHubRepo.ProjectSFID)
+ log.WithFields(f).Debugf("disabling github repository: %s with id: %s for project with sfid: %s for CLA Group: %s",
+ gitHubRepo.RepositoryName, gitHubRepo.RepositoryID, gitHubRepo.RepositoryProjectSfid, gitHubRepo.RepositoryClaGroupID)
disableErr := s.repositoryService.DisableRepository(ctx, gitHubRepo.RepositoryID)
if disableErr != nil {
log.WithFields(f).WithError(disableErr).Warnf("problem disabling github repository: %s with id: %s", gitHubRepo.RepositoryName, gitHubRepo.RepositoryID)
diff --git a/cla-backend-go/v2/github_activity/service.go b/cla-backend-go/v2/github_activity/service.go
index 5214cca3a..cdf95aa1d 100644
--- a/cla-backend-go/v2/github_activity/service.go
+++ b/cla-backend-go/v2/github_activity/service.go
@@ -36,7 +36,7 @@ type Service interface {
}
type eventHandlerService struct {
- githubRepo repositories.Repository
+ gitV1Repository repositories.RepositoryInterface
githubOrgRepo v1GithubOrg.RepositoryInterface
eventService events.Service
autoEnableService dynamo_events.AutoEnableService
@@ -45,23 +45,23 @@ type eventHandlerService struct {
}
// NewService creates a new instance of the Event Handler Service
-func NewService(githubRepo repositories.Repository,
+func NewService(gitV1Repository repositories.RepositoryInterface,
githubOrgRepo v1GithubOrg.RepositoryInterface,
eventService events.Service,
autoEnableService dynamo_events.AutoEnableService,
emailService emails.Service) Service {
- return newService(githubRepo, githubOrgRepo, eventService, autoEnableService, emailService, true)
+ return newService(gitV1Repository, githubOrgRepo, eventService, autoEnableService, emailService, true)
}
-func newService(githubRepo repositories.Repository,
+func newService(gitV1Repository repositories.RepositoryInterface,
githubOrgRepo v1GithubOrg.RepositoryInterface,
eventService events.Service,
autoEnableService dynamo_events.AutoEnableService,
emailService emails.Service,
sendEmail bool) Service {
return &eventHandlerService{
- githubRepo: githubRepo,
+ gitV1Repository: gitV1Repository,
githubOrgRepo: githubOrgRepo,
eventService: eventService,
autoEnableService: autoEnableService,
@@ -134,8 +134,8 @@ func (s *eventHandlerService) handleRepositoryAddedAction(ctx context.Context, s
return err
}
- if err := s.autoEnableService.NotifyCLAManagerForRepos(repoModel.RepositoryProjectID, []*models.GithubRepository{repoModel}); err != nil {
- log.WithFields(f).Warnf("notifyCLAManager for autoEnabled repo : %s for claGroup : %s failed : %v", repoModel.RepositoryName, repoModel.RepositoryProjectID, err)
+ if err := s.autoEnableService.NotifyCLAManagerForRepos(repoModel.RepositoryClaGroupID, []*models.GithubRepository{repoModel}); err != nil {
+ log.WithFields(f).Warnf("notifyCLAManager for autoEnabled repo : %s for claGroup : %s failed : %v", repoModel.RepositoryName, repoModel.RepositoryClaGroupID, err)
}
if sender == nil || sender.Login == nil || *sender.Login == "" {
@@ -146,9 +146,10 @@ func (s *eventHandlerService) handleRepositoryAddedAction(ctx context.Context, s
// sending the log event for the added repository
log.Debugf("handleRepositoryAddedAction sending RepositoryAdded Event for repo %s", *repo.FullName)
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryAdded,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
+ EventType: events.RepositoryAdded,
+ ProjectSFID: repoModel.RepositoryProjectSfid,
+ CLAGroupID: repoModel.RepositoryClaGroupID,
+ UserID: *sender.Login,
EventData: &events.RepositoryAddedEventData{
RepositoryName: *repo.FullName,
},
@@ -167,7 +168,7 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
return fmt.Errorf("missing repo id")
}
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
- repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ repoModel, err := s.gitV1Repository.GitHubGetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
@@ -178,16 +179,17 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
log.WithFields(f).Infof("disabling repo : %s", repoModel.RepositoryID)
- if err := s.githubRepo.DisableRepository(context.Background(), repoModel.RepositoryID); err != nil {
+ if err := s.gitV1Repository.GitHubDisableRepository(context.Background(), repoModel.RepositoryID); err != nil {
log.WithFields(f).Warnf("disabling repo : %s failed : %v", *repo.FullName, err)
return err
}
// sending event for the action
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryDisabled,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
+ EventType: events.RepositoryDisabled,
+ ProjectSFID: repoModel.RepositoryProjectSfid,
+ CLAGroupID: repoModel.RepositoryClaGroupID,
+ UserID: *sender.Login,
EventData: &events.RepositoryDisabledEventData{
RepositoryName: *repo.FullName,
},
@@ -195,7 +197,7 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
if s.sendEmail {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Removed")
- body, err := emails.RenderGithubRepositoryDisabledTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryDisabledTemplateParams{
+ body, err := emails.RenderGithubRepositoryDisabledTemplate(s.emailService, repoModel.RepositoryClaGroupID, emails.GithubRepositoryDisabledTemplateParams{
GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: "CLA Manager",
@@ -210,7 +212,7 @@ func (s *eventHandlerService) handleRepositoryRemovedAction(ctx context.Context,
return nil
}
- if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryProjectID, subject, body); err != nil {
+ if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryClaGroupID, subject, body); err != nil {
log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
}
@@ -230,7 +232,7 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
return fmt.Errorf("missing repo id")
}
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
- repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ repoModel, err := s.gitV1Repository.GitHubGetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
@@ -241,7 +243,7 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
log.WithFields(f).Infof("renaming Github Repository from : %s to : %s", repoModel.RepositoryName, *repo.Name)
- if _, err := s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
+ if _, err := s.gitV1Repository.GitHubUpdateRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
RepositoryName: repo.Name,
Note: "repository was renamed externally",
}); err != nil {
@@ -255,9 +257,10 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
// sending event for the action
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryRenamed,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
+ EventType: events.RepositoryRenamed,
+ ProjectSFID: repoModel.RepositoryProjectSfid,
+ CLAGroupID: repoModel.RepositoryClaGroupID,
+ UserID: *sender.Login,
EventData: &events.RepositoryRenamedEventData{
NewRepositoryName: *repo.Name,
OldRepositoryName: repoModel.RepositoryName,
@@ -266,7 +269,7 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
if s.sendEmail {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Renamed")
- body, err := emails.RenderGithubRepositoryRenamedTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryRenamedTemplateParams{
+ body, err := emails.RenderGithubRepositoryRenamedTemplate(s.emailService, repoModel.RepositoryClaGroupID, emails.GithubRepositoryRenamedTemplateParams{
GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: "CLA Manager",
@@ -282,7 +285,7 @@ func (s *eventHandlerService) handleRepositoryRenamedAction(ctx context.Context,
return nil
}
- if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryProjectID, subject, body); err != nil {
+ if err := s.emailService.NotifyClaManagersForClaGroupID(context.Background(), repoModel.RepositoryClaGroupID, subject, body); err != nil {
log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
}
@@ -314,7 +317,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
}
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
- repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ repoModel, err := s.gitV1Repository.GitHubGetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", repoName)
@@ -361,7 +364,7 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
return fmt.Errorf("aborting the repository : %s transfer, new githubOrg : %s doesn't have claGroupID set", repoModel.RepositoryName, newGithubOrg.OrganizationName)
}
- _, err = s.githubRepo.UpdateGithubRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
+ _, err = s.gitV1Repository.GitHubUpdateRepository(ctx, repoModel.RepositoryID, &models.GithubRepositoryInput{
Note: fmt.Sprintf("repository was transferred from org : %s to : %s", oldGithubOrg.OrganizationName, newGithubOrg.OrganizationName),
RepositoryOrganizationName: aws.String(newGithubOrg.OrganizationName),
RepositoryURL: repo.HTMLURL,
@@ -373,9 +376,10 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
// sending event for the action
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryTransferred,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
+ EventType: events.RepositoryTransferred,
+ ProjectSFID: repoModel.RepositoryProjectSfid,
+ CLAGroupID: repoModel.RepositoryClaGroupID,
+ UserID: *sender.Login,
EventData: &events.RepositoryTransferredEventData{
RepositoryName: repoModel.RepositoryName,
OldGithubOrgName: oldGithubOrg.OrganizationName,
@@ -394,15 +398,16 @@ func (s *eventHandlerService) handleRepositoryTransferredAction(ctx context.Cont
func (s *eventHandlerService) disableFailedTransferRepo(ctx context.Context, sender *github.User, f logrus.Fields, repoModel *models.GithubRepository, oldGithubOrg *models.GithubOrganization, newGithubOrg *models.GithubOrganization) error {
log.WithFields(f).Warnf("can't proceed with repo transfer operation because the new org doesn't have autoenabled=true, disabling the repo : %s", repoModel.RepositoryName)
- if err := s.githubRepo.DisableRepository(ctx, repoModel.RepositoryID); err != nil {
+ if err := s.gitV1Repository.GitHubDisableRepository(ctx, repoModel.RepositoryID); err != nil {
return fmt.Errorf("disabling the repo : %s failed : %v", repoModel.RepositoryID, err)
}
// send event for the disabled repository.
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryDisabled,
- ProjectID: repoModel.RepositoryProjectID,
- UserID: *sender.Login,
+ EventType: events.RepositoryDisabled,
+ ProjectSFID: repoModel.RepositoryProjectSfid,
+ CLAGroupID: repoModel.RepositoryClaGroupID,
+ UserID: *sender.Login,
EventData: &events.RepositoryDisabledEventData{
RepositoryName: repoModel.RepositoryName,
},
@@ -418,7 +423,7 @@ func (s *eventHandlerService) disableFailedTransferRepo(ctx context.Context, sen
func (s *eventHandlerService) notifyForGithubRepositoryTransferred(ctx context.Context, repoModel *models.GithubRepository, oldGithubOrg *models.GithubOrganization, newGithubOrg *models.GithubOrganization, success bool) error {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Transferred")
- body, err := emails.RenderGithubRepositoryTransferredTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryTransferredTemplateParams{
+ body, err := emails.RenderGithubRepositoryTransferredTemplate(s.emailService, repoModel.RepositoryClaGroupID, emails.GithubRepositoryTransferredTemplateParams{
GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: "CLA Manager",
@@ -433,7 +438,7 @@ func (s *eventHandlerService) notifyForGithubRepositoryTransferred(ctx context.C
return fmt.Errorf("rendering email template failed : %v", err)
}
- err = s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryProjectID, subject, body)
+ err = s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryClaGroupID, subject, body)
return err
}
@@ -447,7 +452,7 @@ func (s *eventHandlerService) handleRepositoryArchivedAction(ctx context.Context
return fmt.Errorf("missing repo id")
}
repositoryExternalID := strconv.FormatInt(*repo.ID, 10)
- repoModel, err := s.githubRepo.GetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
+ repoModel, err := s.gitV1Repository.GitHubGetRepositoryByGithubID(context.Background(), repositoryExternalID, true)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
log.WithFields(f).Warnf("event for non existing local repo : %s, nothing to do", *repo.FullName)
@@ -460,7 +465,7 @@ func (s *eventHandlerService) handleRepositoryArchivedAction(ctx context.Context
if s.sendEmail {
subject := fmt.Sprintf("EasyCLA: Github Repository Was Archived")
- body, err := emails.RenderGithubRepositoryArchivedTemplate(s.emailService, repoModel.RepositoryProjectID, emails.GithubRepositoryArchivedTemplateParams{
+ body, err := emails.RenderGithubRepositoryArchivedTemplate(s.emailService, repoModel.RepositoryClaGroupID, emails.GithubRepositoryArchivedTemplateParams{
GithubRepositoryActionTemplateParams: emails.GithubRepositoryActionTemplateParams{
CommonEmailParams: emails.CommonEmailParams{
RecipientName: "CLA Manager",
@@ -474,7 +479,7 @@ func (s *eventHandlerService) handleRepositoryArchivedAction(ctx context.Context
return nil
}
- if err := s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryProjectID, subject, body); err != nil {
+ if err := s.emailService.NotifyClaManagersForClaGroupID(ctx, repoModel.RepositoryClaGroupID, subject, body); err != nil {
log.WithFields(f).Warnf("notifying cla managers via email failed : %v", err)
}
diff --git a/cla-backend-go/v2/github_activity/service_test.go b/cla-backend-go/v2/github_activity/service_test.go
index aa40e8352..18daf241d 100644
--- a/cla-backend-go/v2/github_activity/service_test.go
+++ b/cla-backend-go/v2/github_activity/service_test.go
@@ -30,7 +30,7 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryRenamedActio
GetRepositoryByGithubID(gomock.Any(), "1", true).
Return(&models.GithubRepository{
Enabled: true,
- RepositoryExternalID: "1",
+ RepositoryExternalID: 1,
RepositoryID: repoID,
RepositoryName: repoName,
RepositoryOrganizationName: "org1",
@@ -109,7 +109,7 @@ func TestEventHandlerService_ProcessRepositoryEvent_HandleRepositoryTransferredA
GetRepositoryByGithubID(gomock.Any(), "1", true).
Return(&models.GithubRepository{
Enabled: true,
- RepositoryExternalID: "1",
+ RepositoryExternalID: 1,
RepositoryID: repoID,
RepositoryName: repoName,
RepositoryOrganizationName: oldOrgName,
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index e4ccf784b..f95f88046 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -8,7 +8,6 @@ import (
"fmt"
"net/url"
"sort"
- "strconv"
"strings"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -23,7 +22,7 @@ import (
v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
v1GithubOrg "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
- v1Repositories "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ gitV1Repository "github.com/communitybridge/easycla/cla-backend-go/repositories"
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/jinzhu/copier"
)
@@ -40,23 +39,23 @@ func v2GithubOrganizationModel(in *v1Models.GithubOrganization) (*models.GithubO
// Service contains functions of GithubOrganizations service
type Service interface {
GetGithubOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGithubOrganizations, error)
- AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error)
+ AddGithubOrganization(ctx context.Context, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error)
DeleteGithubOrganization(ctx context.Context, projectSFID string, githubOrgName string) error
UpdateGithubOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
}
type service struct {
repo v1GithubOrg.RepositoryInterface
- ghRepository v1Repositories.Repository
+ gitV1Repository gitV1Repository.RepositoryInterface
ghService v1GithubOrg.ServiceInterface
projectsCLAGroupService projects_cla_groups.Repository
}
// NewService creates a new githubOrganizations service
-func NewService(repo v1GithubOrg.RepositoryInterface, ghRepository v1Repositories.Repository, projectsCLAGroupService projects_cla_groups.Repository, ghService v1GithubOrg.ServiceInterface) Service {
+func NewService(repo v1GithubOrg.RepositoryInterface, gitV1Repository gitV1Repository.RepositoryInterface, projectsCLAGroupService projects_cla_groups.Repository, ghService v1GithubOrg.ServiceInterface) Service {
return service{
repo: repo,
- ghRepository: ghRepository,
+ gitV1Repository: gitV1Repository,
projectsCLAGroupService: projectsCLAGroupService,
ghService: ghService,
}
@@ -170,7 +169,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
log.WithFields(f).Debugf("loading github repositories from %d organizations for projectSFID: %s...", len(orgs.List), projectSFID)
var repoList []*v1Models.GithubRepository
for _, org := range orgs.List {
- orgRepos, orgReposErr := s.ghRepository.GetRepositoriesByOrganizationName(ctx, org.OrganizationName)
+ orgRepos, orgReposErr := s.gitV1Repository.GitHubGetRepositoriesByOrganizationName(ctx, org.OrganizationName)
if orgReposErr != nil || orgRepos == nil {
if _, ok := orgReposErr.(*utils.GitHubRepositoryNotFound); ok {
log.WithFields(f).Debug(orgReposErr)
@@ -191,21 +190,26 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
continue
}
key := fmt.Sprintf("%s#%v", repo.RepositoryOrganizationName, repo.RepositoryExternalID)
+
+ parentProjectModel, projectModelErr := v2ProjectService.GetClient().GetParentProjectModel(repo.RepositoryProjectSfid)
+ if projectModelErr != nil || parentProjectModel == nil {
+ log.WithFields(f).Warnf("unable to load parent for project: %s", repo.RepositoryProjectSfid)
+ return nil, projectModelErr
+ }
+
if _, ok := connectedRepo[key]; ok {
- repoGithubID, err := strconv.ParseInt(repo.RepositoryExternalID, 10, 64)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("repository github id is not integer")
- }
+
rorg.Repositories = append(rorg.Repositories, &models.ProjectGithubRepository{
ConnectionStatus: utils.Connected,
Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
- RepositoryGithubID: repoGithubID,
- ClaGroupID: repo.RepositoryProjectID,
- ProjectID: repo.ProjectSFID,
- ParentProjectID: repo.RepositorySfdcID,
+ RepositoryGithubID: repo.RepositoryExternalID,
+ ClaGroupID: repo.RepositoryClaGroupID,
+ ProjectID: repo.RepositoryProjectSfid,
+ ParentProjectID: parentProjectModel.ID,
})
+
// delete it from connectedRepo array since we have processed it
// connectedArray after this loop will contain repo for which github app have permission but
// they are enabled in cla
@@ -216,10 +220,11 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
Enabled: repo.Enabled,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
- ClaGroupID: repo.RepositoryProjectID,
- ProjectID: repo.ProjectSFID,
- ParentProjectID: repo.RepositorySfdcID,
+ ClaGroupID: repo.RepositoryClaGroupID,
+ ProjectID: repo.RepositoryProjectSfid,
+ ParentProjectID: parentProjectModel.ID,
})
+
if rorg.ConnectionStatus == utils.Connected {
rorg.ConnectionStatus = utils.PartialConnection
}
@@ -254,7 +259,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
return out, nil
}
-func (s service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.CreateGithubOrganization) (*models.GithubOrganization, error) {
+func (s service) AddGithubOrganization(ctx context.Context, projectSFID string, input *models.GithubCreateOrganization) (*models.GithubOrganization, error) {
f := logrus.Fields{
"functionName": "v2.github_organizations.service.AddGitHubOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -264,7 +269,7 @@ func (s service) AddGithubOrganization(ctx context.Context, projectSFID string,
"organizationName": utils.StringValue(input.OrganizationName),
}
- var in v1Models.CreateGithubOrganization
+ var in v1Models.GithubCreateOrganization
err := copier.Copy(&in, input)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem converting the github organization details")
@@ -320,7 +325,7 @@ func (s service) DeleteGithubOrganization(ctx context.Context, projectSFID strin
}
log.WithFields(f).Debug("disabling repositories for github organization...")
- err := s.ghRepository.DisableRepositoriesOfGithubOrganization(ctx, projectSFID, githubOrgName)
+ err := s.gitV1Repository.GitHubDisableRepositoriesOfOrganization(ctx, projectSFID, githubOrgName)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem disabling repositories for github organization")
return err
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 8b2164cb6..2e00e82f2 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -7,12 +7,13 @@ import (
"context"
"errors"
"fmt"
- "github.com/communitybridge/easycla/cla-backend-go/company"
- signatures1 "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
"regexp"
"strconv"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/company"
+ signatures1 "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
+
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
@@ -21,6 +22,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/signatures"
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+ gitV2Repositories "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -35,19 +37,21 @@ type Service interface {
type service struct {
usersRepository users.UserRepository
gitlabRepository gitlab_organizations.RepositoryInterface
- githubRepositories repositories.Repository
+ gitRepository repositories.RepositoryInterface
+ gitV2Repository gitV2Repositories.RepositoryInterface
signaturesRepository signatures.SignatureRepository
projectsCLAGroupsRepository projects_cla_groups.Repository
companyRepository company.IRepository
signatureRepository signatures.SignatureRepository
}
-func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, githubRepositories repositories.Repository, usersRepository users.UserRepository, signaturesRepository signatures.SignatureRepository, projectsCLAGroupsRepository projects_cla_groups.Repository,
+func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRepository repositories.RepositoryInterface, gitV2Repository gitV2Repositories.RepositoryInterface, usersRepository users.UserRepository, signaturesRepository signatures.SignatureRepository, projectsCLAGroupsRepository projects_cla_groups.Repository,
companyRepository company.IRepository, signatureRepository signatures.SignatureRepository) Service {
return &service{
- usersRepository: usersRepository,
gitlabRepository: gitlabRepository,
- githubRepositories: githubRepositories,
+ gitRepository: gitRepository,
+ gitV2Repository: gitV2Repository,
+ usersRepository: usersRepository,
signaturesRepository: signaturesRepository,
projectsCLAGroupsRepository: projectsCLAGroupsRepository,
companyRepository: companyRepository,
@@ -156,42 +160,32 @@ func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeE
repositoryPath := mergeEvent.Project.PathWithNamespace
parts := strings.Split(repositoryPath, "/")
organizationName := parts[0]
+
gitlabOrgs, err := s.gitlabRepository.GetGitlabOrganizationByName(ctx, organizationName)
- if err != nil || len(gitlabOrgs.List) == 0 {
+ if err != nil || gitlabOrgs == nil {
// try getting it with project name as well
gitlabOrgs, err = s.gitlabRepository.GetGitlabOrganizationByName(ctx, mergeEvent.Project.Namespace)
- if err != nil {
+ if err != nil || gitlabOrgs == nil {
return nil, fmt.Errorf("gitlab org : %s doesn't exist : %v", organizationName, err)
}
}
- if len(gitlabOrgs.List) == 0 {
- return nil, fmt.Errorf("gitlab org : %s doesn't exist", organizationName)
- }
-
- orgID := gitlabOrgs.List[0].OrganizationID
- gitlabOrg, err := s.gitlabRepository.GetGitlabOrganization(ctx, orgID)
+ gitlabOrg, err := s.gitlabRepository.GetGitlabOrganization(ctx, gitlabOrgs.OrganizationID)
if err != nil {
- return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", orgID, err)
+ return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", gitlabOrgs.OrganizationID, err)
}
return gitlabOrg, nil
}
func (s service) getGitlabRepoByExternalID(ctx context.Context, orgName, gitlabRepoID string) (*models.GithubRepository, error) {
- gitlabRepos, err := s.githubRepositories.GetRepositoriesByOrganizationName(ctx, orgName)
- if err != nil {
- return nil, fmt.Errorf("fetching gitlab repo for external id : %s, orgName : %s, failed : %v", gitlabRepoID, orgName, err)
+ gitlabRepo, err := s.gitV2Repository.GitLabGetRepositoryByName(ctx, orgName)
+ if err != nil || gitlabRepo == nil {
+ return nil, fmt.Errorf("unable to locate GitLab repo for external id : %s, orgName : %s, failed : %v", gitlabRepoID, orgName, err)
}
- if len(gitlabRepos) == 0 {
- return nil, fmt.Errorf("no repositories found for orgName : %s", orgName)
- }
-
- for _, gitlabRepo := range gitlabRepos {
- if gitlabRepo.RepositoryExternalID == gitlabRepoID && gitlabRepo.RepositoryType == "gitlab" {
- return gitlabRepo, nil
- }
+ if gitlabRepo.RepositoryExternalID == gitlabRepoID && gitlabRepo.RepositoryType == "gitlab" {
+ return gitlabRepo.ToGitHubModel(), nil
}
return nil, fmt.Errorf("no repositories found for orgName : %s and gitlab external id : %s", orgName, gitlabRepoID)
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
index 74eef308a..fd77b482b 100644
--- a/cla-backend-go/v2/gitlab-activity/service_test.go
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -4,11 +4,12 @@
package gitlab_activity
import (
+ "testing"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/xanzy/go-gitlab"
- "testing"
)
func TestIsUserApprovedForSignature(t *testing.T) {
@@ -22,83 +23,82 @@ func TestIsUserApprovedForSignature(t *testing.T) {
Username: "one",
}
- testCases := []struct{
- name string
+ testCases := []struct {
+ name string
signature *models.Signature
- expected bool
+ expected bool
}{
{
- name: "nothing matched",
- signature : &models.Signature{},
+ name: "nothing matched",
+ signature: &models.Signature{},
},
{
name: "email approval list non empty no match",
- signature : &models.Signature{
+ signature: &models.Signature{
EmailApprovalList: []string{"three@example.com"},
},
},
{
name: "email approval list match",
- signature : &models.Signature{
+ signature: &models.Signature{
EmailApprovalList: []string{"one@example.com"},
},
expected: true,
},
{
name: "domain approval list match no match",
- signature : &models.Signature{
+ signature: &models.Signature{
DomainApprovalList: []string{"*.foo.com"},
},
expected: false,
},
{
name: "domain approval list match domain star",
- signature : &models.Signature{
+ signature: &models.Signature{
DomainApprovalList: []string{"*.example.com"},
},
expected: true,
},
{
name: "domain approval list match domain star globbing",
- signature : &models.Signature{
+ signature: &models.Signature{
DomainApprovalList: []string{"*example.com"},
},
expected: true,
},
{
name: "domain approval list match domain star dot",
- signature : &models.Signature{
+ signature: &models.Signature{
DomainApprovalList: []string{".example.com"},
},
expected: true,
},
{
name: "gitlab username approval list no match",
- signature : &models.Signature{
+ signature: &models.Signature{
GitlabUsernameApprovalList: []string{"two"},
},
expected: false,
},
{
name: "gitlab username approval list match",
- signature : &models.Signature{
+ signature: &models.Signature{
GitlabUsernameApprovalList: []string{"one"},
},
expected: true,
},
}
- for _, tc := range testCases{
+ for _, tc := range testCases {
t.Run(tc.name, func(tt *testing.T) {
result := IsUserApprovedForSignature(logrus.Fields{}, tc.signature, userModel, gitlabUser)
- if tc.expected{
+ if tc.expected {
assert.True(tt, result)
- }else{
+ } else {
assert.False(tt, result)
}
})
}
-
-}
\ No newline at end of file
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 0c15a6f3e..16f28eed3 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -37,7 +37,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "gitlab_organizations.handlers.GitlabOrganizationsGetProjectGitlabOrganizationsHandler",
+ "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsGetProjectGitlabOrganizationsHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -77,7 +77,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "Gitlab_organization.handlers.GitlabOrganizationsAddProjectGitlabOrganizationHandler",
+ "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsAddProjectGitlabOrganizationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -183,7 +183,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "github_organization.handlers.GithubOrganizationsDeleteProjectGithubOrganizationHandler",
+ "functionName": "v2.gitlab_organizations.handlers.GithubOrganizationsDeleteProjectGithubOrganizationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"orgName": params.OrgName,
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 27e8e032b..83f709404 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -32,10 +32,10 @@ const (
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
- AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.CreateGitlabOrganization) (*models2.GitlabOrganization, error)
+ AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganizations, error)
+ GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
@@ -57,13 +57,14 @@ func NewRepository(awsSession *session.Session, stage string) RepositoryInterfac
}
}
-func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.CreateGitlabOrganization) (*models2.GitlabOrganization, error) {
+func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error) {
+ gitLabOrganizationName := utils.StringValue(input.OrganizationName)
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.AddGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFID": projectSFID,
- "organizationName": utils.StringValue(input.OrganizationName),
+ "organizationName": gitLabOrganizationName,
"autoEnabled": utils.BoolValue(input.AutoEnabled),
"branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
}
@@ -71,19 +72,26 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
// First, let's check to see if we have an existing gitlab organization with the same name
existingRecord, getErr := repo.GetGitlabOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
if getErr != nil {
- log.WithFields(f).WithError(getErr).Debug("unable to locate existing github organization by name")
- }
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab organization by name %s - ok to create a new record", gitLabOrganizationName)
+ }
+
+ if existingRecord != nil {
+ log.WithFields(f).Debugf("An existing GitLab organization with name %s exists in our database", gitLabOrganizationName)
+ // If everything matches...
+ if projectSFID == existingRecord.ProjectSFID {
+ log.WithFields(f).Debug("Existing github organization with same SFID - should be able to update it")
+ enabledFlag := true
+ updateErr := repo.UpdateGitlabOrganization(ctx, projectSFID, gitLabOrganizationName,
+ utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled), &enabledFlag)
+ if updateErr != nil {
+ return nil, updateErr
+ }
- if existingRecord != nil && len(existingRecord.List) > 0 {
- log.WithFields(f).Debugf("Existing github organization exists in our database, count: %d", len(existingRecord.List))
- if len(existingRecord.List) > 1 {
- log.WithFields(f).Warning("more than one github organization with the same name in the database")
- }
- if parentProjectSFID == existingRecord.List[0].OrganizationSfid {
- log.WithFields(f).Debug("Existing github organization with same parent SFID - should be able to update it")
- } else {
- log.WithFields(f).Debug("Existing github organization with different parent SFID - won't be able to update it - will return conflict")
+ // Return the updated record
+ return repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName)
}
+
+ log.WithFields(f).Debug("Existing github organization with different project SFID - won't be able to update it - will return conflict")
return nil, fmt.Errorf("record already exists")
}
@@ -146,7 +154,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
return ToModel(gitlabOrg), nil
}
-// GetGitlabOrganizations get github organizations based on the project SFID
+// GetGitlabOrganizations get GitLab organizations based on the project SFID
func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.GetGitHubOrganizations",
@@ -202,8 +210,8 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
return &models2.GitlabOrganizations{List: gitlabOrgList}, nil
}
-// GetGitlabOrganizationByName get github organization by name
-func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganizations, error) {
+// GetGitlabOrganizationByName get GitLab organization by name
+func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -238,10 +246,9 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOr
}
if len(results.Items) == 0 {
log.WithFields(f).Debug("Unable to find github organization by name - no results")
- return &models2.GitlabOrganizations{
- List: []*models2.GitlabOrganization{},
- }, nil
+ return nil, nil
}
+
var resultOutput []*GitlabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
@@ -249,8 +256,9 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOr
return nil, err
}
- ghOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
- return &models2.GitlabOrganizations{List: ghOrgList}, nil
+ log.WithFields(f).Debug("building response model...")
+ gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
+ return gitlabOrgList[0], nil
}
// GetGitlabOrganization by organization name
@@ -357,19 +365,16 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
}
_, currentTime := utils.CurrentTime()
- gitlabOrgs, lookupErr := repo.GetGitlabOrganizationByName(ctx, organizationName)
+ gitlabOrg, lookupErr := repo.GetGitlabOrganizationByName(ctx, organizationName)
if lookupErr != nil {
log.WithFields(f).Warnf("error looking up Gitlab organization by name, error: %+v", lookupErr)
return lookupErr
}
- if gitlabOrgs == nil || len(gitlabOrgs.List) == 0 {
- lookupErr := errors.New("unable to lookup Gitlab organization by name")
- log.WithFields(f).Warnf("error looking up Gitlab organization, error: %+v", lookupErr)
- return lookupErr
+ if gitlabOrg == nil {
+ log.WithFields(f).Warn("error looking up Gitlab organization - no results")
+ return errors.New("unable to lookup Gitlab organization by name")
}
- gitlabOrg := gitlabOrgs.List[0]
-
expressionAttributeNames := map[string]*string{
"#A": aws.String("auto_enabled"),
"#C": aws.String("auto_enabled_cla_group_id"),
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 0f23e7beb..a12bbe51e 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -13,7 +13,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/config"
"github.com/go-openapi/strfmt"
- v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ //v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -25,8 +25,8 @@ import (
// Service contains functions of GitlabOrganizations service
type Service interface {
- GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGitlabOrganizations, error)
- AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.CreateGitlabOrganization) (*models.GitlabOrganization, error)
+ GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
+ AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
@@ -39,7 +39,7 @@ type service struct {
claGroupRepository projects_cla_groups.Repository
}
-// NewService creates a new githubOrganizations service
+// NewService creates a new gitlab organization service
func NewService(repo RepositoryInterface, claGroupRepository projects_cla_groups.Repository) Service {
return service{
repo: repo,
@@ -112,7 +112,7 @@ func (s service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrganiz
return ToModel(dbModel), nil
}
-func (s service) AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.CreateGitlabOrganization) (*models.GitlabOrganization, error) {
+func (s service) AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.AddGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -122,7 +122,6 @@ func (s service) AddGitlabOrganization(ctx context.Context, projectSFID string,
"organizationName": utils.StringValue(input.OrganizationName),
}
- log.WithFields(f).Debug("looking up project in project service...")
psc := v2ProjectService.GetClient()
project, err := psc.GetProject(projectSFID)
if err != nil {
@@ -140,24 +139,25 @@ func (s service) AddGitlabOrganization(ctx context.Context, projectSFID string,
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
- log.WithFields(f).Debug("adding github organization...")
+ log.WithFields(f).Debug("adding gitlab organization...")
resp, err := s.repo.AddGitlabOrganization(ctx, parentProjectSFID, projectSFID, input)
if err != nil {
- log.WithFields(f).WithError(err).Warn("problem adding github organization for project")
+ log.WithFields(f).WithError(err).Warn("problem adding gitlab organization for project")
return nil, err
}
+ log.WithFields(f).Debugf("created GitLab organization with ID: %s", resp.OrganizationID)
- return resp, nil
+ return s.GetGitlabOrganizations(ctx, projectSFID)
}
-func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.ProjectGitlabOrganizations, error) {
+func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
- // Load the GitHub Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
+ // Load the GitLab Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
orgs, err := s.repo.GetGitlabOrganizations(ctx, projectSFID)
if err != nil {
@@ -183,23 +183,23 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
log.WithFields(f).Debug("located parentProjectID...")
// Our response model
- out := &models.ProjectGitlabOrganizations{
- List: make([]*models.ProjectGitlabOrganization, 0),
+ out := &models.GitlabProjectOrganizations{
+ List: make([]*models.GitlabProjectOrganization, 0),
}
- // Next, we need to load a bunch of additional data for the response including the github status (if it's still connected/live, not renamed/moved), the CLA Group details, etc.
+ // Next, we need to load a bunch of additional data for the response including the GitLab status (if it's still connected/live, not renamed/moved), the CLA Group details, etc.
- // A temp data model for holding the intermediate results
- type gitlabRepoInfo struct {
- orgName string
- repoInfo *v1Models.GithubRepositoryInfo
- }
+ //// A temp data model for holding the intermediate results
+ //type gitlabRepoInfo struct {
+ // orgName string
+ // repoInfo *v1Models.GitLabRepositoryInfo
+ //}
- orgmap := make(map[string]*models.ProjectGitlabOrganization)
+ orgmap := make(map[string]*models.GitlabProjectOrganization)
for _, org := range orgs.List {
autoEnabledCLAGroupName := ""
if org.AutoEnabledClaGroupID != "" {
- log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitHub auth enabled CLA Group response", org.AutoEnabledClaGroupID)
+ log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitLab auth enabled CLA Group response", org.AutoEnabledClaGroupID)
claGroupMode, claGroupLookupErr := s.claGroupRepository.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
if claGroupLookupErr != nil {
log.WithFields(f).WithError(claGroupLookupErr).Warnf("Unable to lookup CLA Group by ID: %s", org.AutoEnabledClaGroupID)
@@ -216,12 +216,12 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
}
installationURL := buildInstallationURL(org.OrganizationID, orgDetailed.AuthState)
- rorg := &models.ProjectGitlabOrganization{
+ rorg := &models.GitlabProjectOrganization{
AutoEnabled: org.AutoEnabled,
AutoEnableCLAGroupID: org.AutoEnabledClaGroupID,
AutoEnabledCLAGroupName: autoEnabledCLAGroupName,
GitlabOrganizationName: org.OrganizationName,
- Repositories: make([]*models.ProjectGithubRepository, 0),
+ Repositories: make([]*models.GitlabProjectRepository, 0),
InstallationURL: installationURL,
}
@@ -279,7 +279,7 @@ func (s service) DeleteGitlabOrganization(ctx context.Context, projectSFID strin
log.WithFields(f).Debugf("retrieved parent of project sfid : %s -> %s", projectSFID, parentProjectSFID)
// Todo: Enable this when the repositories are implemented
- //err := s.ghRepository.DisableRepositoriesOfGithubOrganization(ctx, parentProjectSFID, gitlabOrgName)
+ //err := s.ghRepository.GitHubDisableRepositoriesOfOrganization(ctx, parentProjectSFID, gitlabOrgName)
//if err != nil {
// log.WithFields(f).Warnf("problem disabling repositories for github organizations, error: %+v", projErr)
// return err
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
new file mode 100644
index 000000000..7e2445dd5
--- /dev/null
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -0,0 +1,138 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package repositories
+
+import (
+ "context"
+ "strconv"
+
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ repoModels "github.com/communitybridge/easycla/cla-backend-go/repositories"
+)
+
+// GitLabAddRepository service function
+func (s *Service) GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*v2Models.GitlabRepository, error) {
+ dbModel, err := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, input)
+ if err != nil {
+ return nil, err
+ }
+
+ return dbModelToGitLabRepository(dbModel)
+}
+
+// GitLabGetRepository service function
+func (s *Service) GitLabGetRepository(ctx context.Context, repositoryID string) (*v2Models.GitlabRepository, error) {
+ dbModel, err := s.gitV2Repository.GitLabGetRepository(ctx, repositoryID)
+ if err != nil {
+ return nil, err
+ }
+
+ return dbModelToGitLabRepository(dbModel)
+}
+
+// GitLabGetRepositoryByName service function
+func (s *Service) GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*v2Models.GitlabRepository, error) {
+ dbModel, err := s.gitV2Repository.GitLabGetRepositoryByName(ctx, repositoryName)
+ if err != nil {
+ return nil, err
+ }
+
+ return dbModelToGitLabRepository(dbModel)
+}
+
+// GitLabGetRepositoriesByProjectSFID service function
+func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabListRepositories, error) {
+ dbModel, err := s.gitV2Repository.GitHubGetRepositoriesByProjectSFID(ctx, projectSFID)
+ if err != nil {
+ return nil, err
+ }
+
+ responses, err := dbModelsToGitLabRepositories(dbModel)
+ if err != nil {
+ return nil, err
+ }
+
+ return &v2Models.GitlabListRepositories{
+ List: responses,
+ }, nil
+}
+
+// GitLabGetRepositoriesByCLAGroup service function
+func (s *Service) GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabListRepositories, error) {
+ var dbModels []*repoModels.RepositoryDBModel
+ var err error
+ if enabled {
+ dbModels, err = s.gitV2Repository.GitHubGetRepositoriesByCLAGroupEnabled(ctx, claGroupID)
+ } else {
+ dbModels, err = s.gitV2Repository.GitHubGetRepositoriesByCLAGroupDisabled(ctx, claGroupID)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ responses, err := dbModelsToGitLabRepositories(dbModels)
+ if err != nil {
+ return nil, err
+ }
+
+ return &v2Models.GitlabListRepositories{
+ List: responses,
+ }, nil
+}
+
+// GitLabEnableRepository service function
+func (s *Service) GitLabEnableRepository(ctx context.Context, repositoryID string) error {
+ return s.gitV2Repository.GitLabEnableRepositoryByID(ctx, repositoryID)
+}
+
+// GitLabDisableRepository service function
+func (s *Service) GitLabDisableRepository(ctx context.Context, repositoryID string) error {
+ return s.gitV2Repository.GitLabDisableRepositoryByID(ctx, repositoryID)
+}
+
+// GitLabDisableCLAGroupRepositories service function
+func (s *Service) GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
+ return s.gitV2Repository.GitLabDisableCLAGroupRepositories(ctx, claGroupID)
+}
+
+// dbModelToGitLabRepository converts the database model to a v2 response model
+func dbModelToGitLabRepository(dbModel *repoModels.RepositoryDBModel) (*v2Models.GitlabRepository, error) {
+
+ gitLabExternalID, err := strconv.ParseInt(dbModel.RepositoryExternalID, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+
+ response := v2Models.GitlabRepository{
+ RepositoryID: dbModel.RepositoryID, // Internal database ID for this repository record
+ RepositoryProjectSfid: dbModel.ProjectSFID, // Project SFID
+ RepositoryClaGroupID: dbModel.RepositoryCLAGroupID, // CLA Group ID
+ RepositoryExternalID: gitLabExternalID, // GitLab unique gitV1Repository ID
+ RepositoryName: dbModel.RepositoryName, // Short repository name
+ RepositoryOrganizationName: dbModel.RepositoryOrganizationName, // Group/Organization name
+ RepositoryURL: dbModel.RepositoryURL, // full url
+ RepositoryType: dbModel.RepositoryType, // gitlab
+ Enabled: dbModel.Enabled, // Enabled flag
+ DateCreated: dbModel.DateCreated, // date created
+ DateModified: dbModel.DateModified, // date updated
+ Note: dbModel.Note, // Optional note
+ Version: dbModel.Version, // record version
+ }
+
+ return &response, nil
+}
+
+// dbModelsToGitLabRepositories converts the slice of database models to a slice of v2 response model
+func dbModelsToGitLabRepositories(dbModels []*repoModels.RepositoryDBModel) ([]*v2Models.GitlabRepository, error) {
+ var responses []*v2Models.GitlabRepository
+ for _, dbModel := range dbModels {
+ response, err := dbModelToGitLabRepository(dbModel)
+ if err != nil {
+ return nil, err
+ }
+ // Add to the list
+ responses = append(responses, response)
+ }
+ return responses, nil
+}
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index bf8ba337d..4364ec18c 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -9,6 +9,8 @@ import (
"fmt"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_repositories"
+
"github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
"github.com/sirupsen/logrus"
@@ -29,14 +31,14 @@ import (
)
// Configure establishes the middleware handlers for the repository service
-func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventService events.Service) { // nolint
api.GithubRepositoriesGetProjectGithubRepositoriesHandler = github_repositories.GetProjectGithubRepositoriesHandlerFunc(
func(params github_repositories.GetProjectGithubRepositoriesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesGetProjectGithubRepositoriesHandler",
+ "functionName": "v2.repositories.handlers.GitHubRepositoriesGetProjectGithubRepositoriesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -51,7 +53,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.ListProjectRepositories(ctx, params.ProjectSFID)
+ result, err := service.GitHubListProjectRepositories(ctx, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("repository not found for projectSFID: %s", params.ProjectSFID)
@@ -66,7 +68,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- response := &models.ListGithubRepositories{}
+ response := &models.GithubListRepositories{}
err = copier.Copy(response, result)
if err != nil {
msg := fmt.Sprintf("problem converting response for projectSFID: %s", params.ProjectSFID)
@@ -84,7 +86,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesAddProjectGithubRepositoryHandler",
+ "functionName": "v2.repositories.handlers.GitHubRepositoriesAddProjectGithubRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -114,7 +116,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
log.WithFields(f).Debugf("Adding GitHub repositories for project: %s", params.ProjectSFID)
- results, err := service.AddGithubRepositories(ctx, params.ProjectSFID, params.GithubRepositoryInput)
+ results, err := service.GitHubAddRepositories(ctx, params.ProjectSFID, params.GithubRepositoryInput)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryExists); ok {
msg := fmt.Sprintf("unable to add repository - repository already exists for projectSFID: %s", params.ProjectSFID)
@@ -154,7 +156,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
}
- v2Response := &models.ListGithubRepositories{}
+ v2Response := &models.GithubListRepositories{}
v2Response.List = v2ResponseList
return github_repositories.NewAddProjectGithubRepositoryOK().WithPayload(v2Response)
@@ -166,7 +168,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesDeleteProjectGithubRepositoryHandler",
+ "functionName": "v2.repositories.handlers.GitHubRepositoriesDeleteProjectGithubRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -182,7 +184,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseForbidden(reqID, msg))
}
- ghRepo, err := service.GetRepository(ctx, params.RepositoryID)
+ ghRepo, err := service.GitHubGetRepository(ctx, params.RepositoryID)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
msg := fmt.Sprintf("repository not found for projectSFID: %s", params.ProjectSFID)
@@ -197,7 +199,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- err = service.DisableRepository(ctx, params.RepositoryID)
+ err = service.GitHubDisableRepository(ctx, params.RepositoryID)
if err != nil {
msg := fmt.Sprintf("problem disabling repository for projectSFID: %s, error: %+v", params.ProjectSFID, err)
log.WithFields(f).WithError(err).Warn(msg)
@@ -208,7 +210,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
ProjectSFID: params.ProjectSFID,
- ProjectID: ghRepo.RepositoryProjectID,
+ CLAGroupID: ghRepo.RepositoryClaGroupID,
LfUsername: authUser.UserName,
EventData: &events.RepositoryDisabledEventData{
RepositoryName: ghRepo.RepositoryName,
@@ -224,7 +226,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesGetProjectGithubRepositoryBranchProtectionHandler",
+ "functionName": "v2.repositories.handlers.GitHubRepositoriesGetProjectGithubRepositoryBranchProtectionHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -247,7 +249,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
branchName = *params.BranchName
}
- protectedBranch, err := service.GetProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, branchName)
+ protectedBranch, err := service.GitHubGetProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, branchName)
if err != nil {
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
msg := fmt.Sprintf("unable to locatate branch protection projectSFID: %s, repository: %s", params.ProjectSFID, params.RepositoryID)
@@ -285,7 +287,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "GitHubRepositoriesUpdateProjectGitHubRepositoryBranchProtectionHandler",
+ "functionName": "v2.repositories.handlers.GitHubRepositoriesUpdateProjectGitHubRepositoryBranchProtectionHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
@@ -301,9 +303,9 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseForbidden(reqID, msg))
}
- protectedBranch, err := service.UpdateProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, params.GithubRepositoryBranchProtectionInput)
+ protectedBranch, err := service.GitHubUpdateProtectedBranch(ctx, params.ProjectSFID, params.RepositoryID, params.GithubRepositoryBranchProtectionInput)
if err != nil {
- log.Warnf("update protected branch failed for repo %s : %v", params.RepositoryID, err)
+ log.Warnf("update protected branch failed for gitV1Repository %s : %v", params.RepositoryID, err)
if _, ok := err.(*utils.GitHubRepositoryNotFound); ok {
msg := fmt.Sprintf("unable to update branch protection for projectSFID: %s, repository: %s", params.ProjectSFID, params.RepositoryID)
log.WithFields(f).WithError(err).Warn(msg)
@@ -338,7 +340,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
}
- repoModel, repoErr := service.GetRepository(ctx, params.RepositoryID)
+ repoModel, repoErr := service.GitHubGetRepository(ctx, params.RepositoryID)
if repoErr != nil {
msg := fmt.Sprintf("problem fetching the repository for projectSFID: %s, with repository: %s, error: %+v", params.ProjectSFID, params.RepositoryID, err)
log.WithFields(f).WithError(repoErr).Warning(msg)
@@ -360,4 +362,163 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
return github_repositories.NewGetProjectGithubRepositoryBranchProtectionOK().WithPayload(protectedBranch)
})
+
+ api.GitlabRepositoriesGetProjectGitLabRepositoriesHandler = gitlab_repositories.GetProjectGitLabRepositoriesHandlerFunc(
+ func(params gitlab_repositories.GetProjectGitLabRepositoriesParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesGetProjectGitLabRepositoriesHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Get GitLab Repositories with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ result, err := service.GitLabGetRepositoriesByProjectSFID(ctx, params.ProjectSFID)
+ if err != nil {
+ if strings.ContainsAny(err.Error(), "getProjectNotFound") {
+ msg := fmt.Sprintf("repository not found for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ msg := fmt.Sprintf("problem looking up repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ response := &models.GitlabListRepositories{}
+ err = copier.Copy(response, result)
+ if err != nil {
+ msg := fmt.Sprintf("problem converting response for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesInternalServerError().WithXRequestID(reqID).WithPayload(utils.ErrorResponseInternalServerErrorWithError(reqID, msg, err))
+ }
+
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesOK().WithPayload(response)
+ })
+
+ api.GitlabRepositoriesAddProjectGitLabRepositoryHandler = gitlab_repositories.AddProjectGitLabRepositoryHandlerFunc(
+ func(params gitlab_repositories.AddProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesAddProjectGitLabRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "repositoryGitLabID": utils.Int64Value(params.GitlabAddRepository.RepositoryExternalID),
+ "repositoryName": utils.StringValue(params.GitlabAddRepository.RepositoryName),
+ "repositoryURL": utils.StringValue(params.GitlabAddRepository.RepositoryURL),
+ "repositoryOrganizationName": utils.StringValue(params.GitlabAddRepository.RepositoryOrganizationName),
+ "repositoryCLAGroupID": utils.StringValue(params.GitlabAddRepository.RepositoryClaGroupID),
+ "repositoryProjectSFID": utils.StringValue(params.GitlabAddRepository.RepositoryProjectSfid),
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Add GitLab Repositories with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_repositories.NewAddProjectGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ // If no repository GitLab ID values provided...
+ // RepositoryGitlabID - provided by the older retool UI which provides only one value
+ // RepositoryGitlabIds - provided by new PCC which passes multiple values
+ if params.GitlabAddRepository.RepositoryExternalID == nil {
+ msg := "missing repository GitLab ID value"
+ return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("Adding GitLab repository for project: %s", params.ProjectSFID)
+ result, err := service.GitLabAddRepository(ctx, params.ProjectSFID, params.GitlabAddRepository)
+ if err != nil {
+ if _, ok := err.(*utils.GitLabRepositoryExists); ok {
+ msg := fmt.Sprintf("unable to add repository - repository with name: %s already exists for projectSFID: %s", utils.StringValue(params.GitlabAddRepository.RepositoryName), params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewAddProjectGitLabRepositoryConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
+ }
+ msg := fmt.Sprintf("problem adding GitLab repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ // Log the event
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryAdded,
+ ProjectSFID: params.ProjectSFID,
+ CLAGroupID: utils.StringValue(params.GitlabAddRepository.RepositoryClaGroupID),
+ LfUsername: authUser.UserName,
+ EventData: &events.RepositoryAddedEventData{
+ RepositoryName: utils.StringValue(params.GitlabAddRepository.RepositoryName),
+ },
+ })
+
+ return gitlab_repositories.NewAddProjectGitLabRepositoryOK().WithPayload(result)
+ })
+
+ api.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler = gitlab_repositories.DeleteProjectGitLabRepositoryHandlerFunc(
+ func(params gitlab_repositories.DeleteProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
+ reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
+ ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ f := logrus.Fields{
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "repositoryID": params.RepositoryID,
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Delete Gitlab Repositories with Project scope of %s",
+ authUser.UserName, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_repositories.NewDeleteProjectGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ }
+
+ ghRepo, err := service.GitLabGetRepository(ctx, params.RepositoryID)
+ if err != nil {
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ msg := fmt.Sprintf("repository not found for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewDeleteProjectGitLabRepositoryNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ msg := fmt.Sprintf("problem looking up repository for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewDeleteProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ err = service.GitLabDisableRepository(ctx, params.RepositoryID)
+ if err != nil {
+ msg := fmt.Sprintf("problem disabling repository for projectSFID: %s, error: %+v", params.ProjectSFID, err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return gitlab_repositories.NewDeleteProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryDisabled,
+ ProjectSFID: params.ProjectSFID,
+ CLAGroupID: ghRepo.RepositoryClaGroupID,
+ LfUsername: authUser.UserName,
+ EventData: &events.RepositoryDisabledEventData{
+ RepositoryName: ghRepo.RepositoryName,
+ },
+ })
+
+ return gitlab_repositories.NewDeleteProjectGitLabRepositoryNoContent().WithXRequestID(reqID)
+ })
}
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
new file mode 100644
index 000000000..df24ca1b6
--- /dev/null
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -0,0 +1,465 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package repositories
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/dynamodb"
+ "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
+ "github.com/aws/aws-sdk-go/service/dynamodb/expression"
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ repoModels "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/gofrs/uuid"
+ "github.com/sirupsen/logrus"
+)
+
+// RepositoryInterface interface defines the functions for the GitLab repository data model
+type RepositoryInterface interface {
+ GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error)
+ GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error)
+ GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
+ GitHubGetRepositoriesByCLAGroupEnabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
+ GitHubGetRepositoriesByCLAGroupDisabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
+ GitHubGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) ([]*repoModels.RepositoryDBModel, error)
+ GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*repoModels.RepositoryDBModel, error)
+ GitLabEnableRepositoryByID(ctx context.Context, repositoryID string) error
+ GitLabDisableRepositoryByID(ctx context.Context, repositoryID string) error
+ GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
+}
+
+// Repository object/struct
+type Repository struct {
+ stage string
+ dynamoDBClient *dynamodb.DynamoDB
+ repositoryTableName string
+ gitLabOrgTableName string
+}
+
+// NewRepository creates a new instance of the GitLab repository service
+func NewRepository(awsSession *session.Session, stage string) *Repository {
+ return &Repository{
+ stage: stage,
+ dynamoDBClient: dynamodb.New(awsSession),
+ repositoryTableName: fmt.Sprintf("cla-%s-repositories", stage),
+ gitLabOrgTableName: fmt.Sprintf("cla-%s-gitlab-orgs", stage),
+ }
+}
+
+// GitLabGetRepository returns the database model for the internal repository ID
+func (r *Repository) GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error) {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repositories.GitLabGetRepository",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "repositoryID": repositoryID,
+ }
+
+ result, err := r.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
+ TableName: aws.String(r.repositoryTableName),
+ Key: map[string]*dynamodb.AttributeValue{
+ "repository_id": {
+ S: aws.String(repositoryID),
+ },
+ },
+ })
+
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem querying using repository ID")
+ return nil, err
+ }
+ if len(result.Item) == 0 {
+ msg := fmt.Sprintf("repository with ID: %s does not exist", repositoryID)
+ log.WithFields(f).Warn(msg)
+ return nil, &utils.GitHubRepositoryNotFound{
+ Message: msg,
+ }
+ }
+
+ // Decode the results into a model
+ var out repoModels.RepositoryDBModel
+ err = dynamodbattribute.UnmarshalMap(result.Item, &out)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem unmarshalling database repository response")
+ return nil, err
+ }
+
+ return &out, nil
+}
+
+// GitLabGetRepositoryByName returns the database model for the specified repository
+func (r *Repository) GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryNameColumn).Equal(expression.Value(repositoryName))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
+ record, err := r.getRepositoryWithConditionFilter(ctx, condition, filter, repoModels.RepositoryNameIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ RepositoryName: repositoryName,
+ }
+ }
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabDuplicateRepositoriesFound); ok {
+ return nil, &utils.GitLabDuplicateRepositoriesFound{
+ RepositoryName: repositoryName,
+ }
+ }
+ // Some other error
+ return nil, err
+ }
+
+ return record, nil
+}
+
+// GitHubGetRepositoriesByCLAGroup returns the database models for the specified CLA Group ID
+func (r *Repository) GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryCLAGroupIDColumn).Equal(expression.Value(claGroupID))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryProjectIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ CLAGroupID: claGroupID,
+ }
+ }
+
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
+// GitHubGetRepositoriesByCLAGroupEnabled returns the database models for the specified CLA Group ID that are enabled
+func (r *Repository) GitHubGetRepositoriesByCLAGroupEnabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryCLAGroupIDColumn).Equal(expression.Value(claGroupID))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower)).
+ And(expression.Name(repoModels.RepositoryEnabledColumn).Equal(expression.Value(true)))
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryProjectIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ CLAGroupID: claGroupID,
+ }
+ }
+
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
+// GitHubGetRepositoriesByCLAGroupDisabled returns the database models for the specified CLA Group ID that are disabled
+func (r *Repository) GitHubGetRepositoriesByCLAGroupDisabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryCLAGroupIDColumn).Equal(expression.Value(claGroupID))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower)).
+ And(expression.Name(repoModels.RepositoryEnabledColumn).Equal(expression.Value(false)))
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryProjectIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ CLAGroupID: claGroupID,
+ }
+ }
+
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
+// GitHubGetRepositoriesByProjectSFID returns a list of repositories associated with the specified project
+func (r *Repository) GitHubGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryProjectIDColumn).Equal(expression.Value(projectSFID))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
+
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryProjectSFIDIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ ProjectSFID: projectSFID,
+ }
+ }
+
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
+// GitLabAddRepository creates a new entry in the repositories table using the specified input parameters
+func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*repoModels.RepositoryDBModel, error) {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repositories.GitHubAddRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "repositoryExternalID": utils.Int64Value(input.RepositoryExternalID),
+ "repositoryURL": utils.StringValue(input.RepositoryURL),
+ "repositoryName": utils.StringValue(input.RepositoryName),
+ "repositoryType": utils.GitLabLower,
+ "repositoryCLAGroupID": utils.StringValue(input.RepositoryClaGroupID),
+ "repositoryProjectSFID": utils.StringValue(input.RepositoryProjectSfid),
+ "repositoryOrganizationName": utils.StringValue(input.RepositoryOrganizationName),
+ }
+
+ // Check first to see if the repository already exists
+ _, err := r.GitLabGetRepositoryByName(ctx, utils.StringValue(input.RepositoryName))
+ if err != nil {
+ // Expecting Not found - no issue if not found - all other error we throw
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); !ok {
+ return nil, err
+ }
+ } else {
+ return nil, &utils.GitLabRepositoryExists{
+ Message: fmt.Sprintf("GitLab repository with name: %s has alerady been registered", utils.StringValue(input.RepositoryName)),
+ RepositoryName: "",
+ Err: nil,
+ }
+ }
+
+ _, currentTime := utils.CurrentTime()
+ repoID, err := uuid.NewV4()
+ if err != nil {
+ return nil, err
+ }
+
+ repository := &repoModels.RepositoryDBModel{
+ RepositoryID: repoID.String(), // internal ID that we assign
+ RepositorySfdcID: projectSFID,
+ ProjectSFID: projectSFID,
+ DateCreated: currentTime,
+ DateModified: currentTime,
+ RepositoryExternalID: strconv.FormatInt(utils.Int64Value(input.RepositoryExternalID), 10),
+ RepositoryName: utils.StringValue(input.RepositoryName),
+ RepositoryURL: utils.StringValue(input.RepositoryURL),
+ RepositoryOrganizationName: utils.StringValue(input.RepositoryOrganizationName), // gitlab group/organization
+ RepositoryCLAGroupID: utils.StringValue(input.RepositoryClaGroupID),
+ RepositoryType: utils.GitLabLower, // should always be gitlab
+ Enabled: true, // default is enabled
+ Note: fmt.Sprintf("created on %s", currentTime),
+ Version: "v1",
+ }
+ av, err := dynamodbattribute.MarshalMap(repository)
+ if err != nil {
+ log.WithFields(f).Warnf("problem marshalling the input, error: %+v", err)
+ return nil, err
+ }
+
+ _, err = r.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
+ Item: av,
+ TableName: aws.String(r.repositoryTableName),
+ })
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to add github repository")
+ return nil, err
+ }
+
+ return repository, nil
+}
+
+// GitLabEnableRepositoryByID enables the specified repository
+func (r *Repository) GitLabEnableRepositoryByID(ctx context.Context, repositoryID string) error {
+ return r.setRepositoryEnabledValue(ctx, repositoryID, true)
+}
+
+// GitLabDisableRepositoryByID disables the specified repository
+func (r *Repository) GitLabDisableRepositoryByID(ctx context.Context, repositoryID string) error {
+ return r.setRepositoryEnabledValue(ctx, repositoryID, false)
+}
+
+// GitLabEnableCLAGroupRepositories enables the specified CLA Group repositories
+func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
+ repositories, err := r.GitHubGetRepositoriesByCLAGroup(ctx, claGroupID)
+ if err != nil {
+ return err
+ }
+
+ for _, repo := range repositories {
+ enableErr := r.GitLabEnableRepositoryByID(ctx, repo.RepositoryID)
+ if enableErr != nil {
+ return enableErr
+ }
+ }
+
+ return nil
+}
+
+// GitLabDisableCLAGroupRepositories disables the GitLab repositories by the specified CLA Group
+func (r *Repository) GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
+ repositories, err := r.GitHubGetRepositoriesByCLAGroup(ctx, claGroupID)
+ if err != nil {
+ return err
+ }
+
+ for _, repo := range repositories {
+ enableErr := r.GitLabDisableRepositoryByID(ctx, repo.RepositoryID)
+ if enableErr != nil {
+ return enableErr
+ }
+ }
+
+ return nil
+}
+
+// getRepositoryWithConditionFilter fetches the repository entry based on the specified condition and filter criteria using the provided index
+func (r *Repository) getRepositoryWithConditionFilter(ctx context.Context, condition expression.KeyConditionBuilder, filter expression.ConditionBuilder, indexName string) (*repoModels.RepositoryDBModel, error) {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repository.getRepositoryWithConditionFilter",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "indexName": indexName,
+ }
+
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).WithFilter(filter).Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem creating builder")
+ return nil, err
+ }
+
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(r.repositoryTableName),
+ IndexName: aws.String(indexName),
+ }
+
+ results, err := r.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to get repositories using query: %+v", queryInput)
+ return nil, err
+ }
+
+ if len(results.Items) == 0 {
+ log.WithFields(f).Warnf("no repositories found matching filter critera: %+v", queryInput)
+ // Generic - no details as we don't know what filter content was provided
+ return nil, &utils.GitLabRepositoryNotFound{}
+ }
+
+ var repositories []*repoModels.RepositoryDBModel
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &repositories)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem unmarshalling response")
+ return nil, err
+ }
+
+ if len(repositories) > 1 {
+ log.WithFields(f).Warn("multiple repositories records with the same repository name and type found")
+ // Generic - no details as we don't know what filter content was provided
+ return nil, &utils.GitLabDuplicateRepositoriesFound{}
+ }
+
+ return repositories[0], nil
+}
+
+// getRepositoriesWithConditionFilter fetches the repository entry based on the specified condition and filter criteria
+// using the provided index
+func (r *Repository) getRepositoriesWithConditionFilter(ctx context.Context, condition expression.KeyConditionBuilder, filter expression.ConditionBuilder, indexName string) ([]*repoModels.RepositoryDBModel, error) {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repository.getRepositoriesWithConditionFilter",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "indexName": indexName,
+ }
+
+ expr, err := expression.NewBuilder().WithKeyCondition(condition).WithFilter(filter).Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem creating builder")
+ return nil, err
+ }
+
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(r.repositoryTableName),
+ IndexName: aws.String(indexName),
+ }
+
+ results, err := r.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("unable to get repositories using query: %+v", queryInput)
+ return nil, err
+ }
+
+ if len(results.Items) == 0 {
+ log.WithFields(f).Warnf("no repositories found matching filter critera: %+v", queryInput)
+ // Generic - no details as we don't know what filter content was provided
+ return nil, &utils.GitLabRepositoryNotFound{}
+ }
+
+ var repositories []*repoModels.RepositoryDBModel
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &repositories)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem unmarshalling response")
+ return nil, err
+ }
+
+ return repositories, nil
+}
+
+// setRepositoryEnabledValue sets the specified repository to the specified enabled value
+func (r *Repository) setRepositoryEnabledValue(ctx context.Context, repositoryID string, enabledValue bool) error {
+ // Load the existing model - need to fetch the old values, if available
+ existingModel, getErr := r.GitLabGetRepository(ctx, repositoryID)
+ if getErr != nil {
+ return getErr
+ }
+ if existingModel == nil {
+ return fmt.Errorf("unable to locate existing repository entry by ID: %s", repositoryID)
+ }
+
+ // If we have an old note - grab it/save it
+ var existingNote = ""
+ if existingModel.Note != "" {
+ if !strings.HasSuffix(strings.TrimSpace(existingModel.Note), ".") {
+ existingNote = strings.TrimSpace(existingModel.Note) + ". "
+ } else {
+ existingNote = strings.TrimSpace(existingModel.Note) + " "
+ }
+ }
+
+ _, now := utils.CurrentTime()
+ _, err := r.dynamoDBClient.UpdateItem(&dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ "repository_id": {S: aws.String(repositoryID)},
+ },
+ ExpressionAttributeNames: map[string]*string{
+ "#enabled": aws.String(repoModels.RepositoryEnabledColumn),
+ "#note": aws.String(repoModels.RepositoryNoteColumn),
+ "#dateModified": aws.String(repoModels.RepositoryDateModifiedColumn),
+ },
+ ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
+ ":enabledValue": {
+ BOOL: aws.Bool(enabledValue),
+ },
+ ":noteValue": {
+ S: aws.String(fmt.Sprintf("%s Updated enabled flag to %t on %s.", existingNote, enabledValue, now)),
+ },
+ ":dateModifiedValue": {
+ S: aws.String(now),
+ },
+ },
+ UpdateExpression: aws.String("SET #enabled = :enabledValue, #note = :noteValue, #dateModified = :dateModifiedValue"),
+ TableName: aws.String(r.repositoryTableName),
+ })
+
+ return err
+}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index c83126b5a..3b80b9d1f 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -29,28 +29,44 @@ import (
v2ProjectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
)
-// Service contains functions of GitHub V3Repositories service
-type Service interface {
- AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error)
- EnableRepository(ctx context.Context, repositoryID string) error
- DisableRepository(ctx context.Context, repositoryID string) error
- ListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.ListGithubRepositories, error)
- GetRepository(ctx context.Context, repositoryID string) (*v1Models.GithubRepository, error)
- GetRepositoryByName(ctx context.Context, repositoryName string) (*v1Models.GithubRepository, error)
- DisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
- GetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error)
- UpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error)
+// ServiceInterface contains functions of the repositories service
+type ServiceInterface interface {
+ // GitHub
+
+ GitHubAddRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error)
+ GitHubEnableRepository(ctx context.Context, repositoryID string) error
+ GitHubDisableRepository(ctx context.Context, repositoryID string) error
+ GitHubListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.GithubListRepositories, error)
+ GitHubGetRepository(ctx context.Context, repositoryID string) (*v1Models.GithubRepository, error)
+ GitHubGetRepositoryByName(ctx context.Context, repositoryName string) (*v1Models.GithubRepository, error)
+ GitHubDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
+ GitHubGetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error)
+ GitHubUpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error)
+
+ // GitLab
+
+ GitLabGetRepository(ctx context.Context, repositoryID string) (*v2Models.GitlabRepository, error)
+ GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*v2Models.GitlabRepository, error)
+ //GitLabGetRepositoriesByGitLabID(ctx context.Context, gitLabID int64) (*v2Models.GitlabRepository, error)
+ GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabListRepositories, error)
+ GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabListRepositories, error)
+ GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*v2Models.GitlabRepository, error)
+ GitLabEnableRepository(ctx context.Context, repositoryID string) error
+ GitLabDisableRepository(ctx context.Context, repositoryID string) error
+ GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
}
-// GithubOrgRepo provide method to get github organization by name
+// GithubOrgRepo provide method to get GitHub organization by name
type GithubOrgRepo interface {
GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganizations, error)
GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganization, error)
GetGitHubOrganizations(ctx context.Context, projectSFID string) (*v1Models.GithubOrganizations, error)
}
-type service struct {
- repo v1Repositories.Repository
+// Service is the service model/structure
+type Service struct {
+ gitV1Repository v1Repositories.RepositoryInterface
+ gitV2Repository RepositoryInterface
projectsClaGroupsRepo projects_cla_groups.Repository
ghOrgRepo GithubOrgRepo
}
@@ -62,17 +78,19 @@ var (
)
// NewService creates a new githubOrganizations service
-func NewService(repo v1Repositories.Repository, pcgRepo projects_cla_groups.Repository, ghOrgRepo GithubOrgRepo) Service {
- return &service{
- repo: repo,
+func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository RepositoryInterface, pcgRepo projects_cla_groups.Repository, ghOrgRepo GithubOrgRepo) ServiceInterface {
+ return &Service{
+ gitV1Repository: gitV1Repository,
+ gitV2Repository: gitV2Repository,
projectsClaGroupsRepo: pcgRepo,
ghOrgRepo: ghOrgRepo,
}
}
-func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error) {
+// GitHubAddRepositories adds the specified GitHub repository to the specified project
+func (s *Service) GitHubAddRepositories(ctx context.Context, projectSFID string, input *models.GithubRepositoryInput) ([]*v1Models.GithubRepository, error) {
f := logrus.Fields{
- "functionName": "v2.repositories.service.AddGithubRepositories",
+ "functionName": "v2.repositories.service.GitHubAddRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"claGroupID": utils.StringValue(input.ClaGroupID),
@@ -155,7 +173,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
// Check if this repository exists in our database
log.WithFields(f).Debugf("checking if GitHub repository by name: %s exists...", utils.StringValue(ghRepo.FullName))
- existingRepositoryModel, lookupErr := s.GetRepositoryByName(ctx, utils.StringValue(ghRepo.FullName))
+ existingRepositoryModel, lookupErr := s.GitHubGetRepositoryByName(ctx, utils.StringValue(ghRepo.FullName))
if lookupErr != nil {
// If we have the repository not found error - this is ok - we are expecting this
if notFoundErr, ok := lookupErr.(*utils.GitHubRepositoryNotFound); ok {
@@ -186,7 +204,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
}
// Update Repo details in case of any changes
- updatedRepository, updateErr := s.repo.UpdateGithubRepository(ctx, existingRepositoryModel.RepositoryID, v1Input)
+ updatedRepository, updateErr := s.gitV1Repository.GitHubUpdateRepository(ctx, existingRepositoryModel.RepositoryID, v1Input)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warnf("unable to update GitHub repository with name: %s, id: %s, using input: %+v", utils.StringValue(ghRepo.FullName), existingRepositoryModel.RepositoryID, v1Input)
return nil, updateErr
@@ -210,7 +228,7 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
RepositoryURL: ghRepo.HTMLURL,
}
- addedModel, addErr := s.repo.AddGithubRepository(ctx, parentProjectSFID, projectSFID, in)
+ addedModel, addErr := s.gitV1Repository.GitHubAddRepository(ctx, parentProjectSFID, projectSFID, in)
if addErr != nil {
log.WithFields(f).WithError(addErr).Warnf("unable to add github repository: %s for project: %s", *ghRepo.FullName, projectSFID)
return nil, addErr
@@ -224,17 +242,20 @@ func (s *service) AddGithubRepositories(ctx context.Context, projectSFID string,
return response, nil
}
-func (s *service) EnableRepository(ctx context.Context, repositoryID string) error {
- return s.repo.EnableRepository(ctx, repositoryID)
+// GitHubEnableRepository service function
+func (s *Service) GitHubEnableRepository(ctx context.Context, repositoryID string) error {
+ return s.gitV1Repository.GitHubEnableRepository(ctx, repositoryID)
}
-func (s *service) DisableRepository(ctx context.Context, repositoryID string) error {
- return s.repo.DisableRepository(ctx, repositoryID)
+// GitHubDisableRepository service function
+func (s *Service) GitHubDisableRepository(ctx context.Context, repositoryID string) error {
+ return s.gitV1Repository.GitHubDisableRepository(ctx, repositoryID)
}
-func (s *service) ListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.ListGithubRepositories, error) {
+// GitHubListProjectRepositories service function
+func (s *Service) GitHubListProjectRepositories(ctx context.Context, projectSFID string) (*v1Models.GithubListRepositories, error) {
f := logrus.Fields{
- "functionName": "v2.repositories.service.ListProjectRepositories",
+ "functionName": "v2.repositories.service.GitHubListProjectRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -256,7 +277,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
}
log.WithFields(f).Debug("loaded project from the project service")
enabled := true
- return s.repo.ListProjectRepositories(ctx, projectSFID, &enabled)
+ return s.gitV1Repository.GitHubListProjectRepositories(ctx, projectSFID, &enabled)
//// Lookup orgs via projectSFID
//log.WithFields(f).Debug("querying EasyCLA for organizations by project id...")
@@ -276,7 +297,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
//}
//
//// Our response - empty to start with
- //response := &v1Models.ListGithubRepositories{
+ //response := &v1Models.GithubListRepositories{
// List: []*v1Models.GithubRepository{},
//}
//
@@ -313,7 +334,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
//}
//
//// Now, query our DB....
- //listOurGitHubRepos, err := s.repo.ListProjectRepositories(ctx, "", projectSFID, true)
+ //listOurGitHubRepos, err := s.gitV1Repository.GitHubListProjectRepositories(ctx, "", projectSFID, true)
//if err != nil {
// log.WithFields(f).WithError(err).Warn("unable to lookup repository records by id in our repositories table ")
// return response, err
@@ -323,7 +344,7 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
// return response, err
//}
//
- //// For each repo that we have...
+ //// For each gitV1Repository that we have...
//for _, ourGitHubRepo := range listOurGitHubRepos.List {
// // Inefficient, but ok if the number of repos is relatively small
// for _, r := range response.List {
@@ -343,17 +364,20 @@ func (s *service) ListProjectRepositories(ctx context.Context, projectSFID strin
//return response, nil
}
-func (s *service) GetRepository(ctx context.Context, repositoryID string) (*v1Models.GithubRepository, error) {
- return s.repo.GetRepository(ctx, repositoryID)
+// GitHubGetRepository service function
+func (s *Service) GitHubGetRepository(ctx context.Context, repositoryID string) (*v1Models.GithubRepository, error) {
+ return s.gitV1Repository.GitHubGetRepository(ctx, repositoryID)
}
-func (s *service) GetRepositoryByName(ctx context.Context, repositoryName string) (*v1Models.GithubRepository, error) {
- return s.repo.GetRepositoryByName(ctx, repositoryName)
+// GitHubGetRepositoryByName service function
+func (s *Service) GitHubGetRepositoryByName(ctx context.Context, repositoryName string) (*v1Models.GithubRepository, error) {
+ return s.gitV1Repository.GitHubGetRepositoryByName(ctx, repositoryName)
}
-func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error) {
+// GitHubGetProtectedBranch service function
+func (s *Service) GitHubGetProtectedBranch(ctx context.Context, projectSFID, repositoryID, branchName string) (*v2Models.GithubRepositoryBranchProtection, error) {
f := logrus.Fields{
- "functionName": "v2.repositories.service.GetProtectedBranch",
+ "functionName": "v2.repositories.service.GitHubGetProtectedBranch",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
@@ -389,7 +413,7 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
if errors.Is(err, branch_protection.ErrBranchNotProtected) {
return result, nil
}
- log.WithFields(f).WithError(err).Warnf("getting the github protected branch for owner : %s, repo : %s and branch : %s failed : %v", owner, githubRepoName, branchName, err)
+ log.WithFields(f).WithError(err).Warnf("getting the github protected branch for owner : %s, gitV1Repository : %s and branch : %s failed : %v", owner, githubRepoName, branchName, err)
return nil, err
}
@@ -405,9 +429,10 @@ func (s *service) GetProtectedBranch(ctx context.Context, projectSFID, repositor
return result, nil
}
-func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error) {
+// GitHubUpdateProtectedBranch service function
+func (s *Service) GitHubUpdateProtectedBranch(ctx context.Context, projectSFID, repositoryID string, input *v2Models.GithubRepositoryBranchProtectionInput) (*v2Models.GithubRepositoryBranchProtection, error) {
f := logrus.Fields{
- "functionName": "v2.repositories.service.UpdateProtectedBranch",
+ "functionName": "v2.repositories.service.GitHubUpdateProtectedBranch",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"repositoryID": repositoryID,
@@ -477,10 +502,11 @@ func (s *service) UpdateProtectedBranch(ctx context.Context, projectSFID, reposi
return nil, err
}
- return s.GetProtectedBranch(ctx, projectSFID, repositoryID, branchName)
+ return s.GitHubGetProtectedBranch(ctx, projectSFID, repositoryID, branchName)
}
-func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID string) (*v1Models.GithubRepository, error) {
+// getGithubRepo service function
+func (s *Service) getGithubRepo(ctx context.Context, projectSFID, repositoryID string) (*v1Models.GithubRepository, error) {
f := logrus.Fields{
"functionName": "v2.repositories.service.getGitHubRepo",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -493,14 +519,14 @@ func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID s
if err != nil {
return nil, err
}
- githubRepository, err := s.GetRepository(ctx, repositoryID)
+ githubRepository, err := s.GitHubGetRepository(ctx, repositoryID)
if err != nil {
log.WithFields(f).Warnf("fetching repository failed : %s : %v", repositoryID, err)
return nil, err
}
- // check if project and repo are actually associated
- if githubRepository.ProjectSFID != projectSFID {
+ // check if project and gitV1Repository are actually associated
+ if githubRepository.RepositoryProjectSfid != projectSFID {
msg := fmt.Sprintf("github repository %s doesn't belong to project : %s", repositoryID, projectSFID)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
@@ -509,7 +535,8 @@ func (s *service) getGithubRepo(ctx context.Context, projectSFID, repositoryID s
return githubRepository, nil
}
-func (s *service) getBranchProtectionRepositoryForOrgName(ctx context.Context, githubOrgName string) (*branch_protection.BranchProtectionRepository, error) {
+// getBranchProtectionRepositoryForOrgName service function
+func (s *Service) getBranchProtectionRepositoryForOrgName(ctx context.Context, githubOrgName string) (*branch_protection.BranchProtectionRepository, error) {
f := logrus.Fields{
"functionName": "v2.repositories.service.getGitHubClientForOrgName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -529,22 +556,23 @@ func (s *service) getBranchProtectionRepositoryForOrgName(ctx context.Context, g
return branchProtectionRepo, nil
}
-func (s *service) getGithubOwner(ctx context.Context, branchProtectionRepository *branch_protection.BranchProtectionRepository, githubOrgName, githubRepoName string) (string, error) {
+// getGithubOwner service function
+func (s *Service) getGithubOwner(ctx context.Context, branchProtectionRepository *branch_protection.BranchProtectionRepository, githubOrgName, githubRepoName string) (string, error) {
owner, err := branchProtectionRepository.GetOwnerName(ctx, githubOrgName, githubRepoName)
if err != nil {
- log.Warnf("getting the owner name for org : %s and repo : %s failed : %v", githubOrgName, githubRepoName, err)
+ log.Warnf("getting the owner name for org : %s and gitV1Repository : %s failed : %v", githubOrgName, githubRepoName, err)
return "", err
}
if owner == "" {
- log.Warnf("GitHub returned empty owner name for org : %s and repo : %s", githubOrgName, githubRepoName)
+ log.Warnf("GitHub returned empty owner name for org : %s and gitV1Repository : %s", githubOrgName, githubRepoName)
return "", fmt.Errorf("empty owner name")
}
return owner, nil
}
-// getRequiredProtectedBranchCheckStatus
-func (s *service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *branch_protection.BranchProtectionRule, requiredChecks []string) []*v2Models.GithubRepositoryBranchProtectionStatusChecks {
+// getRequiredProtectedBranchCheckStatus service function to get the required protected branch check status
+func (s *Service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *branch_protection.BranchProtectionRule, requiredChecks []string) []*v2Models.GithubRepositoryBranchProtectionStatusChecks {
f := logrus.Fields{
"functionName": "v2.repositories.service.getRequiredProtectedBranchCheckStatus",
}
@@ -578,15 +606,16 @@ func (s *service) getRequiredProtectedBranchCheckStatus(branchProtectionRule *br
return result
}
-func (s *service) DisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
+// GitHubDisableCLAGroupRepositories service function to disable CLA group repositories
+func (s *Service) GitHubDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
f := logrus.Fields{
- "functionName": "v2.repositories.service.DisableCLAGroupRepositories",
+ "functionName": "v2.repositories.service.GitHubDisableCLAGroupRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
}
var deleteErr error
- ghOrgs, err := s.repo.GetCLAGroupRepositoriesGroupByOrgs(ctx, claGroupID, true)
+ ghOrgs, err := s.gitV1Repository.GitHubGetCLAGroupRepositoriesGroupByOrgs(ctx, claGroupID, true)
if err != nil {
return err
}
@@ -594,7 +623,7 @@ func (s *service) DisableCLAGroupRepositories(ctx context.Context, claGroupID st
log.WithFields(f).Debugf("Deleting repositories for cla-group :%s", claGroupID)
for _, ghOrg := range ghOrgs {
for _, item := range ghOrg.List {
- deleteErr = s.repo.DisableRepository(ctx, item.RepositoryID)
+ deleteErr = s.gitV1Repository.GitHubDisableRepository(ctx, item.RepositoryID)
if deleteErr != nil {
log.WithFields(f).Warnf("Unable to remove repository: %s for project :%s error :%v", item.RepositoryID, claGroupID, deleteErr)
}
From a6090a3335b547a327f4d385dbb1d1c228b24b73 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:05:54 -0700
Subject: [PATCH 0398/1276] Bump path-parse from 1.0.6 to 1.0.7 in
/cla-frontend-corporate-console/src (#3130)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-corporate-console/src/yarn.lock b/cla-frontend-corporate-console/src/yarn.lock
index 111cea165..c4d00d2d8 100644
--- a/cla-frontend-corporate-console/src/yarn.lock
+++ b/cla-frontend-corporate-console/src/yarn.lock
@@ -2906,8 +2906,9 @@ path-key@^2.0.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
From 3010897be7b3f9ca4f97701080a42c58825b4e70 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:06:06 -0700
Subject: [PATCH 0399/1276] Bump path-parse from 1.0.6 to 1.0.7 in
/cla-frontend-project-console/src (#3131)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-project-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-project-console/src/yarn.lock b/cla-frontend-project-console/src/yarn.lock
index 6fe68d0dd..191d14eee 100644
--- a/cla-frontend-project-console/src/yarn.lock
+++ b/cla-frontend-project-console/src/yarn.lock
@@ -3027,8 +3027,9 @@ path-key@^2.0.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
From 2772d37b07bf3af4a4cc926c3941c456122d5577 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:06:18 -0700
Subject: [PATCH 0400/1276] Bump jszip from 3.5.0 to 3.7.1 in
/cla-frontend-corporate-console (#3134)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-corporate-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-corporate-console/yarn.lock b/cla-frontend-corporate-console/yarn.lock
index 4094dd7cd..fe72ed2b5 100644
--- a/cla-frontend-corporate-console/yarn.lock
+++ b/cla-frontend-corporate-console/yarn.lock
@@ -3748,9 +3748,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
- integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
+ integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
From ed99606c6d21506c86316b284c42facd281276ad Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:06:29 -0700
Subject: [PATCH 0401/1276] Bump path-parse from 1.0.6 to 1.0.7 in
/cla-frontend-contributor-console/src (#3132)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/src/yarn.lock | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-frontend-contributor-console/src/yarn.lock b/cla-frontend-contributor-console/src/yarn.lock
index e1fc3cdc2..f93af2470 100644
--- a/cla-frontend-contributor-console/src/yarn.lock
+++ b/cla-frontend-contributor-console/src/yarn.lock
@@ -2903,8 +2903,9 @@ path-key@^2.0.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
From 077f327caf63fa0e08affb5d5ee6d7699bcc17b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:06:43 -0700
Subject: [PATCH 0402/1276] Bump jszip from 3.5.0 to 3.7.1 in /cla-backend-go
(#3139)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend-go/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock
index 55e94ac56..f44d4608b 100644
--- a/cla-backend-go/yarn.lock
+++ b/cla-backend-go/yarn.lock
@@ -3021,9 +3021,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
- integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
+ integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
From 46c7b28667e7bfdfaf775cd288f2992c9c25c647 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:06:55 -0700
Subject: [PATCH 0403/1276] Bump jszip from 3.5.0 to 3.7.1 in /cla-backend
(#3137)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-backend/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock
index 2112e51ab..ea5cc2a06 100644
--- a/cla-backend/yarn.lock
+++ b/cla-backend/yarn.lock
@@ -3709,9 +3709,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.1.0, jszip@^3.2.2, jszip@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
- integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
+ integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
From ad91337760b754e933b8998956314d5f68ff86b7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:07:10 -0700
Subject: [PATCH 0404/1276] Bump path-parse from 1.0.6 to 1.0.7 (#3135)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index ecc42d4b0..9801b4dcf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3591,9 +3591,9 @@ path-loader@^1.0.10:
superagent "^3.8.3"
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
- integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^4.0.0:
version "4.0.0"
From aa24a02cf3abe043647d8ced037086b20b93c23e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:07:24 -0700
Subject: [PATCH 0405/1276] Bump jszip from 3.5.0 to 3.7.1 in
/cla-frontend-contributor-console (#3138)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
cla-frontend-contributor-console/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-frontend-contributor-console/yarn.lock b/cla-frontend-contributor-console/yarn.lock
index d5cee53c5..3a75259b0 100644
--- a/cla-frontend-contributor-console/yarn.lock
+++ b/cla-frontend-contributor-console/yarn.lock
@@ -3761,9 +3761,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
- integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
+ integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
From 186ab82b25aa9a47795a918138c8f50ccf8c63e5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Aug 2021 17:07:36 -0700
Subject: [PATCH 0406/1276] Bump jszip from 3.5.0 to 3.7.1 (#3136)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 9801b4dcf..1e449494c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2894,9 +2894,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
- integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9"
+ integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
From 41c3e6c4605704fe20855f0e9fa44432db592bc1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 11 Aug 2021 17:22:59 -0700
Subject: [PATCH 0407/1276] Added GitLab Org Permissions Feedback (#3141)
---
cla-backend-go/swagger/cla.v2.yaml | 4 ++
.../v2/gitlab_organizations/handlers.go | 39 +++++++++++++++----
2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 93dd7f870..638828d42 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1643,6 +1643,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
'500':
@@ -1794,6 +1796,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
'500':
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 16f28eed3..e19172201 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"strings"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
@@ -44,9 +45,17 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_organizations.NewGetProjectGitlabOrganizationsNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Get Project GitHub Organizations with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Get Project GitHub Organizations for Project '%s' with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewGetProjectGitlabOrganizationsForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -84,9 +93,17 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"projectSFID": params.ProjectSFID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Add Project Gitlab Organizations with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Add Project GitHub Organizations for Project '%s' with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -191,14 +208,22 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
"authEmail": authUser.Email,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_organizations.NewDeleteProjectGitlabOrganizationNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("authUser %s does not have access to Delete Project GitHub Organizations with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Delete Project GitHub Organizations for Project '%s' with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewDeleteProjectGitlabOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- err := service.DeleteGitlabOrganization(ctx, params.ProjectSFID, params.OrgName)
+ err = service.DeleteGitlabOrganization(ctx, params.ProjectSFID, params.OrgName)
if err != nil {
if strings.Contains(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("project not found with given SFID: %s", params.ProjectSFID)
From cd8d4943dbf88fd7920e155f970086c4df16aefb Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 11 Aug 2021 17:35:32 -0700
Subject: [PATCH 0408/1276] Added GitLab Org Permissions Feedback (#3142)
---
cla-backend-go/v2/gitlab_organizations/handlers.go | 2 ++
cla-backend-go/v2/gitlab_organizations/service.go | 3 +--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index e19172201..d93aebc0a 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -10,6 +10,8 @@ import (
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"strings"
+ project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
"github.com/gofrs/uuid"
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index a12bbe51e..30d89d83f 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -299,9 +299,8 @@ func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI
//params.Add("redirect_uri", "http://localhost:8080/v4/gitlab/oauth/callback")
params.Add("response_type", "code")
params.Add("state", state)
- params.Add("scope", "read_user read_api read_repository write_repository api")
+ params.Add("scope", "api read_user read_api read_repository write_repository email")
installationURL := strfmt.URI(base + "?" + params.Encode())
return &installationURL
-
}
From 75143529ab6bc3886d0b6e26ab73a450d95fef73 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 11 Aug 2021 23:34:34 -0700
Subject: [PATCH 0409/1276] Updated Debug for GitLab (#3143)
---
cla-backend-go/gitlab/client.go | 12 ++++++------
cla-backend-go/gitlab/client_test.go | 17 ++++++++++-------
cla-backend-go/gitlab/init.go | 14 ++++++++++----
.../v2/gitlab_organizations/handlers.go | 1 -
4 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/cla-backend-go/gitlab/client.go b/cla-backend-go/gitlab/client.go
index d04757245..757267078 100644
--- a/cla-backend-go/gitlab/client.go
+++ b/cla-backend-go/gitlab/client.go
@@ -40,15 +40,15 @@ func NewGitlabOauthClient(authInfo string) (*gitlab.Client, error) {
// EncryptAuthInfo encrypts the oauth response into a string
func EncryptAuthInfo(oauthResp *OauthSuccessResponse) (string, error) {
- key := getGitlabAppPrivateKey()
+ key := getGitLabAppPrivateKey()
keyDecoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
- return "", fmt.Errorf("decode key : %v", err)
+ return "", fmt.Errorf("problem decoding GitLab private glClientKey, error: %v", err)
}
b, err := json.Marshal(oauthResp)
if err != nil {
- return "", fmt.Errorf("oauth resp json marshall : %v", err)
+ return "", fmt.Errorf("problem marshalling oauth resp json, error: %v", err)
}
authInfo := string(b)
//log.Infof("auth info before encrypting : %s", authInfo)
@@ -61,7 +61,7 @@ func EncryptAuthInfo(oauthResp *OauthSuccessResponse) (string, error) {
return hex.EncodeToString(encrypted), nil
}
-// DecryptAuthInfo decrytps the authinfo into OauthSuccessResponse data structure
+// DecryptAuthInfo decrytps the auth info into OauthSuccessResponse data structure
func DecryptAuthInfo(authInfoEncoded string) (*OauthSuccessResponse, error) {
ciphertext, err := hex.DecodeString(authInfoEncoded)
if err != nil {
@@ -70,10 +70,10 @@ func DecryptAuthInfo(authInfoEncoded string) (*OauthSuccessResponse, error) {
//log.Infof("auth info decoded : %s", ciphertext)
- key := getGitlabAppPrivateKey()
+ key := getGitLabAppPrivateKey()
keyDecoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
- return nil, fmt.Errorf("decode key : %v", err)
+ return nil, fmt.Errorf("decode glClientKey : %v", err)
}
//log.Debugf("before decrypt : keyDecoded : %s, cipherText : %s", keyDecoded, ciphertext)
diff --git a/cla-backend-go/gitlab/client_test.go b/cla-backend-go/gitlab/client_test.go
index 46c994c2c..30855ba71 100644
--- a/cla-backend-go/gitlab/client_test.go
+++ b/cla-backend-go/gitlab/client_test.go
@@ -10,16 +10,18 @@ import (
"github.com/stretchr/testify/assert"
)
-var key = "0WqnDWHnZKo2cmQ8m93EtY9ZBpfzQW4UnnEuRmgtJKM="
+var glClientID = "124453345"
+var glClientKey = "0WqnDWHnZKo2cmQ8m93EtY9ZBpfzQW4UnnEuRmgtJKM="
var oauthRespStr = `{"access_token":"a30671b8749ba5d48925712344377f11a5aba43ec630f099e464b9843796e6a6","token_type":"Bearer","expires_in":0,"refresh_token":"0838a31d0d796973eacefdf513523e6e47aa06fac9d26622964da1e473509458","created_at":1626435922}`
func TestNewGitlabOauthClient(t *testing.T) {
- Init("124453345", key)
+ Init(glClientID, glClientKey)
t.Cleanup(func() {
- gitlabAppPrivateKey = ""
+ gitLabAppPrivateKey = ""
})
- t.Logf("app private key is : %s", getGitlabAppPrivateKey())
+ t.Logf("app private ID is : %s", getGitLabAppID())
+ t.Logf("app private key is : %s", getGitLabAppPrivateKey())
var oauthResp OauthSuccessResponse
err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
@@ -34,12 +36,13 @@ func TestNewGitlabOauthClient(t *testing.T) {
}
func TestEncryptDecryptAuthInfo(t *testing.T) {
- Init("124453345", key)
+ Init(glClientID, glClientKey)
t.Cleanup(func() {
- gitlabAppPrivateKey = ""
+ gitLabAppPrivateKey = ""
})
- t.Logf("app private key is : %s", getGitlabAppPrivateKey())
+ t.Logf("app private ID is : %s", getGitLabAppID())
+ t.Logf("app private key is : %s", getGitLabAppPrivateKey())
var oauthResp OauthSuccessResponse
err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
diff --git a/cla-backend-go/gitlab/init.go b/cla-backend-go/gitlab/init.go
index b390e6124..c15db8ce9 100644
--- a/cla-backend-go/gitlab/init.go
+++ b/cla-backend-go/gitlab/init.go
@@ -3,13 +3,19 @@
package gitlab
-var gitlabAppPrivateKey string
+var gitLabAppPrivateKey string
+var gitLabAppID string
// Init initializes the required gitlab variables
func Init(glAppID string, glAppPrivateKey string) {
- gitlabAppPrivateKey = glAppPrivateKey
+ gitLabAppID = glAppID
+ gitLabAppPrivateKey = glAppPrivateKey
}
-func getGitlabAppPrivateKey() string {
- return gitlabAppPrivateKey
+func getGitLabAppID() string {
+ return gitLabAppID
+}
+
+func getGitLabAppPrivateKey() string {
+ return gitLabAppPrivateKey
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index d93aebc0a..a3bf4268f 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -7,7 +7,6 @@ import (
"context"
"errors"
"fmt"
- project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"strings"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
From 80c51ee82544d05d2e13171d3b99e6aaf5ea0210 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 10:35:01 -0700
Subject: [PATCH 0410/1276] Updated Debug for GitLab (#3144)
---
cla-backend-go/gitlab/auth.go | 22 +++++++++-
.../v2/gitlab_organizations/handlers.go | 12 ++---
.../v2/gitlab_organizations/repository.go | 44 +++++++++----------
.../v2/gitlab_organizations/service.go | 2 +-
4 files changed, 49 insertions(+), 31 deletions(-)
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab/auth.go
index d9da3a218..b49ff162d 100644
--- a/cla-backend-go/gitlab/auth.go
+++ b/cla-backend-go/gitlab/auth.go
@@ -4,12 +4,23 @@
package gitlab
import (
+ "errors"
+ "fmt"
+
"github.com/communitybridge/easycla/cla-backend-go/config"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/go-resty/resty/v2"
+ "github.com/sirupsen/logrus"
)
// FetchOauthCredentials is responsible for fetching the credentials from gitlab for alredy started Oauth process (access_token, refresh_token)
func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab.auth.FetchOauthCredentials",
+ "code": code,
+ "redirectURI": config.GetConfig().Gitlab.RedirectURI,
+ }
+
client := resty.New()
params := map[string]string{
"client_id": config.GetConfig().Gitlab.AppID,
@@ -20,14 +31,21 @@ func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
//"redirect_uri": "http://localhost:8080/v4/gitlab/oauth/callback",
}
+ url := "https://gitlab.com/oauth/token"
resp, err := client.R().
SetQueryParams(params).
SetResult(&OauthSuccessResponse{}).
- Post("https://gitlab.com/oauth/token")
-
+ Post(url)
if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem invoking GitLab auth token exchange to: %s", url)
return nil, err
}
+ if resp.StatusCode() < 200 || resp.StatusCode() > 299 {
+ msg := fmt.Sprintf("problem invoking GitLab auth token exchange to: %s with status code: %d", url, resp.StatusCode())
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
return resp.Result().(*OauthSuccessResponse), nil
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index a3bf4268f..d6100aadc 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -55,7 +55,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Get Project GitHub Organizations for Project '%s' with scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Get Project GitLab Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewGetProjectGitlabOrganizationsForbidden().WithPayload(
@@ -103,7 +103,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Add Project GitHub Organizations for Project '%s' with scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Add Project GitLab Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
@@ -138,7 +138,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
result, err := service.AddGitlabOrganization(ctx, params.ProjectSFID, params.Body)
if err != nil {
- msg := fmt.Sprintf("unable to add github organization, error: %+v", err)
+ msg := fmt.Sprintf("unable to add GitLab organization, error: %+v", err)
log.WithFields(f).WithError(err).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
@@ -201,7 +201,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.handlers.GithubOrganizationsDeleteProjectGithubOrganizationHandler",
+ "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": params.ProjectSFID,
"orgName": params.OrgName,
@@ -218,7 +218,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Delete Project GitHub Organizations for Project '%s' with scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Delete Project GitLab Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_organizations.NewDeleteProjectGitlabOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
@@ -238,7 +238,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
- EventType: events.GitHubOrganizationDeleted,
+ EventType: events.GitlabOrganizationDeleted,
ProjectSFID: params.ProjectSFID,
EventData: &events.GitlabOrganizationDeletedEventData{
GitlabOrganizationName: params.OrgName,
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 83f709404..2a6bd05ca 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -35,7 +35,7 @@ type RepositoryInterface interface {
AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*GitlabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models2.GitlabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID, authInfo string) error
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
@@ -79,7 +79,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
log.WithFields(f).Debugf("An existing GitLab organization with name %s exists in our database", gitLabOrganizationName)
// If everything matches...
if projectSFID == existingRecord.ProjectSFID {
- log.WithFields(f).Debug("Existing github organization with same SFID - should be able to update it")
+ log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
enabledFlag := true
updateErr := repo.UpdateGitlabOrganization(ctx, projectSFID, gitLabOrganizationName,
utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled), &enabledFlag)
@@ -91,7 +91,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
return repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName)
}
- log.WithFields(f).Debug("Existing github organization with different project SFID - won't be able to update it - will return conflict")
+ log.WithFields(f).Debug("Existing GitLab organization with different project SFID - won't be able to update it - will return conflict")
return nil, fmt.Errorf("record already exists")
}
@@ -126,7 +126,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
Version: "v1",
}
- log.WithFields(f).Debug("Encoding github organization record for adding to the database...")
+ log.WithFields(f).Debug("Encoding GitLab organization record for adding to the database...")
av, err := dynamodbattribute.MarshalMap(gitlabOrg)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to marshall request for query")
@@ -157,7 +157,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
// GetGitlabOrganizations get GitLab organizations based on the project SFID
func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.repository.GetGitHubOrganizations",
+ "functionName": "v2.gitlab_organizations.repository.GetGitlabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -188,7 +188,7 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
results, err := repo.dynamoDBClient.Query(queryInput)
if err != nil {
- log.WithFields(f).Warnf("error retrieving github_organizations using project_sfid = %s. error = %s", projectSFID, err.Error())
+ log.WithFields(f).Warnf("error retrieving gitlab_organizations using project_sfid = %s. error = %s", projectSFID, err.Error())
return nil, err
}
@@ -211,16 +211,16 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
}
// GetGitlabOrganizationByName get GitLab organization by name
-func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOrganizationName string) (*models2.GitlabOrganization, error) {
+func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models2.GitlabOrganization, error) {
f := logrus.Fields{
- "functionName": "v1.github_organizations.repository.GetGitHubOrganizationByName",
+ "functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "githubOrganizationName": githubOrganizationName,
+ "gitLabOrganizationName": gitLabOrganizationName,
}
- githubOrganizationName = strings.ToLower(githubOrganizationName)
+ gitLabOrganizationName = strings.ToLower(gitLabOrganizationName)
- condition := expression.Key("organization_name_lower").Equal(expression.Value(strings.ToLower(githubOrganizationName)))
+ condition := expression.Key("organization_name_lower").Equal(expression.Value(strings.ToLower(gitLabOrganizationName)))
builder := expression.NewBuilder().WithKeyCondition(condition)
// Use the nice builder to create the expression
expr, err := builder.Build()
@@ -238,14 +238,14 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, githubOr
IndexName: aws.String(GitlabOrgLowerNameIndex),
}
- log.WithFields(f).Debugf("querying for github organization by name using organization_name_lower=%s...", strings.ToLower(githubOrganizationName))
+ log.WithFields(f).Debugf("querying for GitLab organization by name using organization_name_lower=%s...", strings.ToLower(gitLabOrganizationName))
results, err := repo.dynamoDBClient.Query(queryInput)
if err != nil {
- log.WithFields(f).WithError(err).Warnf("error retrieving github_organizations using githubOrganizationName = %s", githubOrganizationName)
+ log.WithFields(f).WithError(err).Warnf("error retrieving gitlab_organizations using gitLabOrganizationName = %s", gitLabOrganizationName)
return nil, err
}
if len(results.Items) == 0 {
- log.WithFields(f).Debug("Unable to find github organization by name - no results")
+ log.WithFields(f).Debug("Unable to find GitLab organization by name - no results")
return nil, nil
}
@@ -269,7 +269,7 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
"gitlabOrganizationID": gitlabOrganizationID,
}
- log.WithFields(f).Debug("Querying for github organization by name...")
+ log.WithFields(f).Debug("Querying for GitLab organization by name...")
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
Key: map[string]*dynamodb.AttributeValue{
"organization_id": {
@@ -282,7 +282,7 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
return nil, err
}
if len(result.Item) == 0 {
- log.WithFields(f).Debug("Unable to find github organization by name - no results")
+ log.WithFields(f).Debug("Unable to find GitLab organization by name - no results")
return nil, nil
}
@@ -429,10 +429,10 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
f := logrus.Fields{
- "functionName": "v1.github_organizations.repository.DeleteGitHubOrganization",
+ "functionName": "v1.gitlab_organizations.repository.DeleteGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
- "githubOrgName": gitlabOrgName,
+ "gitlabOrgName": gitlabOrgName,
}
var gitlabOrganizationID string
@@ -443,14 +443,14 @@ func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID
return errors.New(errMsg)
}
- for _, githubOrg := range orgs.List {
- if strings.EqualFold(githubOrg.OrganizationName, gitlabOrgName) {
- gitlabOrganizationID = githubOrg.OrganizationID
+ for _, gitLabOrg := range orgs.List {
+ if strings.EqualFold(gitLabOrg.OrganizationName, gitlabOrgName) {
+ gitlabOrganizationID = gitLabOrg.OrganizationID
break
}
}
- log.WithFields(f).Debug("Deleting GitHub organization...")
+ log.WithFields(f).Debug("Deleting GitLab organization...")
// Update enabled flag as false
_, currentTime := utils.CurrentTime()
note := fmt.Sprintf("Enabled set to false due to org deletion at %s ", currentTime)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 30d89d83f..ef0d8b714 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -263,7 +263,7 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
func (s service) DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error {
f := logrus.Fields{
- "functionName": "DeleteGitHubOrganization",
+ "functionName": "DeleteGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"gitlabOrgName": gitlabOrgName,
From 368bb503e0809979dd7c8c02720cf3df40a45635 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 12:46:13 -0700
Subject: [PATCH 0411/1276] Added GitLab Auth Debug (#3146)
---
cla-backend-go/gitlab/auth.go | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab/auth.go
index b49ff162d..db1c97090 100644
--- a/cla-backend-go/gitlab/auth.go
+++ b/cla-backend-go/gitlab/auth.go
@@ -16,11 +16,14 @@ import (
// FetchOauthCredentials is responsible for fetching the credentials from gitlab for alredy started Oauth process (access_token, refresh_token)
func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
f := logrus.Fields{
- "functionName": "gitlab.auth.FetchOauthCredentials",
- "code": code,
- "redirectURI": config.GetConfig().Gitlab.RedirectURI,
+ "functionName": "gitlab.auth.FetchOauthCredentials",
+ "code": code,
+ "redirectURI": config.GetConfig().Gitlab.RedirectURI,
+ "gitLabClientID": fmt.Sprintf("%s...", config.GetConfig().Gitlab.AppID[0:6]),
+ "gitLabClientSecret": fmt.Sprintf("%s...", config.GetConfig().Gitlab.ClientSecret[0:6]),
}
+ // For info on this authorization flow, see: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
client := resty.New()
params := map[string]string{
"client_id": config.GetConfig().Gitlab.AppID,
@@ -42,7 +45,7 @@ func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
}
if resp.StatusCode() < 200 || resp.StatusCode() > 299 {
- msg := fmt.Sprintf("problem invoking GitLab auth token exchange to: %s with status code: %d", url, resp.StatusCode())
+ msg := fmt.Sprintf("problem invoking GitLab auth token exchange to: %s with status code: %d, response: %s", url, resp.StatusCode(), string(resp.Body()))
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
From 7eced9a4f480ddee1c04f84a1a2a62b006a8028a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 13:02:29 -0700
Subject: [PATCH 0412/1276] Resolved GitHub Org Parent Project Issues (#3147)
---
cla-backend-go/v2/github_organizations/service.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index f95f88046..c4f75b635 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -191,10 +191,10 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
}
key := fmt.Sprintf("%s#%v", repo.RepositoryOrganizationName, repo.RepositoryExternalID)
+ parentProjectSFID = repo.RepositoryProjectSfid
parentProjectModel, projectModelErr := v2ProjectService.GetClient().GetParentProjectModel(repo.RepositoryProjectSfid)
- if projectModelErr != nil || parentProjectModel == nil {
- log.WithFields(f).Warnf("unable to load parent for project: %s", repo.RepositoryProjectSfid)
- return nil, projectModelErr
+ if projectModelErr == nil && parentProjectModel != nil {
+ parentProjectSFID = parentProjectModel.ID
}
if _, ok := connectedRepo[key]; ok {
@@ -207,7 +207,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
RepositoryGithubID: repo.RepositoryExternalID,
ClaGroupID: repo.RepositoryClaGroupID,
ProjectID: repo.RepositoryProjectSfid,
- ParentProjectID: parentProjectModel.ID,
+ ParentProjectID: parentProjectSFID,
})
// delete it from connectedRepo array since we have processed it
From 9fad7a4d0f8c25b5e0547da3d63d4d3dd265c036 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 14:17:59 -0700
Subject: [PATCH 0413/1276] Resolved Additional Parent Project ID Issue (#3148)
---
cla-backend-go/v2/github_organizations/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/github_organizations/service.go b/cla-backend-go/v2/github_organizations/service.go
index c4f75b635..9c57eaaed 100644
--- a/cla-backend-go/v2/github_organizations/service.go
+++ b/cla-backend-go/v2/github_organizations/service.go
@@ -222,7 +222,7 @@ func (s service) GetGithubOrganizations(ctx context.Context, projectSFID string)
RepositoryName: repo.RepositoryName,
ClaGroupID: repo.RepositoryClaGroupID,
ProjectID: repo.RepositoryProjectSfid,
- ParentProjectID: parentProjectModel.ID,
+ ParentProjectID: parentProjectSFID,
})
if rorg.ConnectionStatus == utils.Connected {
From 71f794acc0eba73fabaa4a0f0cc5fbb8a70cadab Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 14:42:38 -0700
Subject: [PATCH 0414/1276] Added More GitLab Auth Debug (#3149)
---
cla-backend-go/gitlab/auth.go | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab/auth.go
index db1c97090..6f374437c 100644
--- a/cla-backend-go/gitlab/auth.go
+++ b/cla-backend-go/gitlab/auth.go
@@ -15,22 +15,32 @@ import (
// FetchOauthCredentials is responsible for fetching the credentials from gitlab for alredy started Oauth process (access_token, refresh_token)
func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
+ gitLabConfig := config.GetConfig().Gitlab
f := logrus.Fields{
- "functionName": "gitlab.auth.FetchOauthCredentials",
- "code": code,
- "redirectURI": config.GetConfig().Gitlab.RedirectURI,
- "gitLabClientID": fmt.Sprintf("%s...", config.GetConfig().Gitlab.AppID[0:6]),
- "gitLabClientSecret": fmt.Sprintf("%s...", config.GetConfig().Gitlab.ClientSecret[0:6]),
+ "functionName": "gitlab.auth.FetchOauthCredentials",
+ "code": code,
+ "redirectURI": config.GetConfig().Gitlab.RedirectURI,
+ }
+
+ if len(gitLabConfig.AppID) > 4 {
+ f["gitLabClientID"] = fmt.Sprintf("%s...%s", gitLabConfig.AppID[0:4], gitLabConfig.AppID[len(gitLabConfig.AppID)-4:])
+ } else {
+ return nil, errors.New("gitlab application client ID value is not set - value is empty or malformed")
+ }
+ if len(gitLabConfig.ClientSecret) > 4 {
+ f["gitLabClientSecret"] = fmt.Sprintf("%s...%s", gitLabConfig.ClientSecret[0:4], gitLabConfig.ClientSecret[len(gitLabConfig.ClientSecret)-4:])
+ } else {
+ return nil, errors.New("gitlab application client secret value is not set - value is empty or malformed")
}
// For info on this authorization flow, see: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
client := resty.New()
params := map[string]string{
- "client_id": config.GetConfig().Gitlab.AppID,
- "client_secret": config.GetConfig().Gitlab.ClientSecret,
+ "client_id": gitLabConfig.AppID,
+ "client_secret": gitLabConfig.ClientSecret,
"code": code,
"grant_type": "authorization_code",
- "redirect_uri": config.GetConfig().Gitlab.RedirectURI,
+ "redirect_uri": gitLabConfig.RedirectURI,
//"redirect_uri": "http://localhost:8080/v4/gitlab/oauth/callback",
}
From a6b720f6b5ae932ac7ab2cd405be649f1dfd1d18 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 17:02:24 -0700
Subject: [PATCH 0415/1276] Testing GitLab Auth (#3150)
---
.../cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/gitlab/client.go | 16 +++----
cla-backend-go/gitlab/client_test.go | 26 +++++------
cla-backend-go/gitlab/init.go | 44 +++++++++++++++----
cla-backend-go/v2/gitlab-activity/service.go | 6 ++-
.../v2/gitlab_organizations/service.go | 6 ++-
7 files changed, 63 insertions(+), 39 deletions(-)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index 72edafe5f..f396fd985 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -94,7 +94,7 @@ func init() {
token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
+ _ = gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
project_service.InitClient(configFile.APIGatewayURL)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 35e59ba7a..3f1370192 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -237,7 +237,7 @@ func server(localMode bool) http.Handler {
// initialize github
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
+ _ = gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
diff --git a/cla-backend-go/gitlab/client.go b/cla-backend-go/gitlab/client.go
index 757267078..06a0e7e5f 100644
--- a/cla-backend-go/gitlab/client.go
+++ b/cla-backend-go/gitlab/client.go
@@ -28,8 +28,8 @@ type OauthSuccessResponse struct {
}
// NewGitlabOauthClient creates a new gitlab client from the given oauth info, authInfo is encrypted
-func NewGitlabOauthClient(authInfo string) (*gitlab.Client, error) {
- oauthResp, err := DecryptAuthInfo(authInfo)
+func NewGitlabOauthClient(authInfo string, gitLabApp *App) (*gitlab.Client, error) {
+ oauthResp, err := DecryptAuthInfo(authInfo, gitLabApp)
if err != nil {
return nil, err
}
@@ -39,9 +39,8 @@ func NewGitlabOauthClient(authInfo string) (*gitlab.Client, error) {
}
// EncryptAuthInfo encrypts the oauth response into a string
-func EncryptAuthInfo(oauthResp *OauthSuccessResponse) (string, error) {
- key := getGitLabAppPrivateKey()
- keyDecoded, err := base64.StdEncoding.DecodeString(key)
+func EncryptAuthInfo(oauthResp *OauthSuccessResponse, gitLabApp *App) (string, error) {
+ keyDecoded, err := base64.StdEncoding.DecodeString(gitLabApp.GetAppPrivateKey())
if err != nil {
return "", fmt.Errorf("problem decoding GitLab private glClientKey, error: %v", err)
}
@@ -61,8 +60,8 @@ func EncryptAuthInfo(oauthResp *OauthSuccessResponse) (string, error) {
return hex.EncodeToString(encrypted), nil
}
-// DecryptAuthInfo decrytps the auth info into OauthSuccessResponse data structure
-func DecryptAuthInfo(authInfoEncoded string) (*OauthSuccessResponse, error) {
+// DecryptAuthInfo decrypts the auth info into OauthSuccessResponse data structure
+func DecryptAuthInfo(authInfoEncoded string, gitLabApp *App) (*OauthSuccessResponse, error) {
ciphertext, err := hex.DecodeString(authInfoEncoded)
if err != nil {
return nil, fmt.Errorf("decode auth info %s : %v", authInfoEncoded, err)
@@ -70,8 +69,7 @@ func DecryptAuthInfo(authInfoEncoded string) (*OauthSuccessResponse, error) {
//log.Infof("auth info decoded : %s", ciphertext)
- key := getGitLabAppPrivateKey()
- keyDecoded, err := base64.StdEncoding.DecodeString(key)
+ keyDecoded, err := base64.StdEncoding.DecodeString(gitLabApp.GetAppPrivateKey())
if err != nil {
return nil, fmt.Errorf("decode glClientKey : %v", err)
}
diff --git a/cla-backend-go/gitlab/client_test.go b/cla-backend-go/gitlab/client_test.go
index 30855ba71..45149a423 100644
--- a/cla-backend-go/gitlab/client_test.go
+++ b/cla-backend-go/gitlab/client_test.go
@@ -15,45 +15,39 @@ var glClientKey = "0WqnDWHnZKo2cmQ8m93EtY9ZBpfzQW4UnnEuRmgtJKM="
var oauthRespStr = `{"access_token":"a30671b8749ba5d48925712344377f11a5aba43ec630f099e464b9843796e6a6","token_type":"Bearer","expires_in":0,"refresh_token":"0838a31d0d796973eacefdf513523e6e47aa06fac9d26622964da1e473509458","created_at":1626435922}`
func TestNewGitlabOauthClient(t *testing.T) {
- Init(glClientID, glClientKey)
- t.Cleanup(func() {
- gitLabAppPrivateKey = ""
- })
+ gitLabApp := Init(glClientID, glClientKey)
- t.Logf("app private ID is : %s", getGitLabAppID())
- t.Logf("app private key is : %s", getGitLabAppPrivateKey())
+ t.Logf("app private ID is : %s", gitLabApp.GetAppID())
+ t.Logf("app private key is : %s", gitLabApp.GetAppPrivateKey())
var oauthResp OauthSuccessResponse
err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
assert.NoError(t, err)
- encrypted, err := EncryptAuthInfo(&oauthResp)
+ encrypted, err := EncryptAuthInfo(&oauthResp, gitLabApp)
assert.NoError(t, err)
- client, err := NewGitlabOauthClient(encrypted)
+ client, err := NewGitlabOauthClient(encrypted, gitLabApp)
assert.NoError(t, err)
assert.NotNil(t, client)
}
func TestEncryptDecryptAuthInfo(t *testing.T) {
- Init(glClientID, glClientKey)
- t.Cleanup(func() {
- gitLabAppPrivateKey = ""
- })
+ gitLabApp := Init(glClientID, glClientKey)
- t.Logf("app private ID is : %s", getGitLabAppID())
- t.Logf("app private key is : %s", getGitLabAppPrivateKey())
+ t.Logf("app private ID is : %s", gitLabApp.GetAppID())
+ t.Logf("app private key is : %s", gitLabApp.GetAppPrivateKey())
var oauthResp OauthSuccessResponse
err := json.Unmarshal([]byte(oauthRespStr), &oauthResp)
assert.NoError(t, err)
t.Logf("unmarshall ok : %+v", oauthResp)
- encrypted, err := EncryptAuthInfo(&oauthResp)
+ encrypted, err := EncryptAuthInfo(&oauthResp, gitLabApp)
assert.NoError(t, err)
t.Logf("encrypted auth info : %s", encrypted)
- oauthRespDecrypted, err := DecryptAuthInfo(encrypted)
+ oauthRespDecrypted, err := DecryptAuthInfo(encrypted, gitLabApp)
assert.NoError(t, err)
assert.Equal(t, &oauthResp, oauthRespDecrypted)
diff --git a/cla-backend-go/gitlab/init.go b/cla-backend-go/gitlab/init.go
index c15db8ce9..8ed849ce3 100644
--- a/cla-backend-go/gitlab/init.go
+++ b/cla-backend-go/gitlab/init.go
@@ -3,19 +3,45 @@
package gitlab
-var gitLabAppPrivateKey string
-var gitLabAppID string
+import (
+ "sync"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+)
+
+// App is a wrapper for the GitLab configuration items
+type App struct {
+ gitLabAppPrivateKey string
+ gitLabAppID string
+}
+
+var gitLabAppSingleton *App
+
+var once sync.Once
// Init initializes the required gitlab variables
-func Init(glAppID string, glAppPrivateKey string) {
- gitLabAppID = glAppID
- gitLabAppPrivateKey = glAppPrivateKey
+func Init(glAppID string, glAppPrivateKey string) *App {
+ if gitLabAppSingleton == nil {
+ once.Do(
+ func() {
+ log.Debug("Creating object single instance...")
+ gitLabAppSingleton = &App{
+ gitLabAppID: glAppID,
+ gitLabAppPrivateKey: glAppPrivateKey,
+ }
+ })
+ } else {
+ log.Debug("GitLabApp object single instance already - returning singleton instance")
+ }
+ return gitLabAppSingleton
}
-func getGitLabAppID() string {
- return gitLabAppID
+// GetAppID returns the GitLab application ID
+func (app *App) GetAppID() string {
+ return app.gitLabAppID
}
-func getGitLabAppPrivateKey() string {
- return gitLabAppPrivateKey
+// GetAppPrivateKey returns the GitLab application private key
+func (app *App) GetAppPrivateKey() string {
+ return app.gitLabAppPrivateKey
}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 2e00e82f2..30dce2993 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -11,6 +11,8 @@ import (
"strconv"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+
"github.com/communitybridge/easycla/cla-backend-go/company"
signatures1 "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/signatures"
@@ -43,6 +45,7 @@ type service struct {
projectsCLAGroupsRepository projects_cla_groups.Repository
companyRepository company.IRepository
signatureRepository signatures.SignatureRepository
+ gitLabApp *gitlab2.App
}
func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRepository repositories.RepositoryInterface, gitV2Repository gitV2Repositories.RepositoryInterface, usersRepository users.UserRepository, signaturesRepository signatures.SignatureRepository, projectsCLAGroupsRepository projects_cla_groups.Repository,
@@ -56,6 +59,7 @@ func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRe
projectsCLAGroupsRepository: projectsCLAGroupsRepository,
companyRepository: companyRepository,
signatureRepository: signatureRepository,
+ gitLabApp: gitlab2.Init(config.GetConfig().Gitlab.AppID, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -85,7 +89,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
log.WithFields(f).Debugf("internal gitlab org : %s:%s is associated with external path : %s", gitlabOrg.OrganizationID, gitlabOrg.OrganizationName, repositoryPath)
- gitlabClient, err := gitlab2.NewGitlabOauthClient(gitlabOrg.AuthInfo)
+ gitlabClient, err := gitlab2.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
if err != nil {
return fmt.Errorf("initializing gitlab client : %v", err)
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index ef0d8b714..f813f9bd9 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -37,6 +37,7 @@ type Service interface {
type service struct {
repo RepositoryInterface
claGroupRepository projects_cla_groups.Repository
+ gitLabApp *gitlab.App
}
// NewService creates a new gitlab organization service
@@ -44,6 +45,7 @@ func NewService(repo RepositoryInterface, claGroupRepository projects_cla_groups
return service{
repo: repo,
claGroupRepository: claGroupRepository,
+ gitLabApp: gitlab.Init(config.GetConfig().Gitlab.AppID, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -71,7 +73,7 @@ func (s service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganiz
}
log.WithFields(f).Debugf("updating gitlab org auth")
- authInfoEncrypted, err := gitlab.EncryptAuthInfo(oauthResp)
+ authInfoEncrypted, err := gitlab.EncryptAuthInfo(oauthResp, s.gitLabApp)
if err != nil {
return fmt.Errorf("encrypt failed : %v", err)
}
@@ -228,7 +230,7 @@ func (s service) GetGitlabOrganizations(ctx context.Context, projectSFID string)
if orgDetailed.AuthInfo == "" {
rorg.ConnectionStatus = utils.NoConnection
} else {
- glClient, err := gitlab.NewGitlabOauthClient(orgDetailed.AuthInfo)
+ glClient, err := gitlab.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
if err != nil {
log.WithFields(f).Errorf("initializing gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
rorg.ConnectionStatus = utils.ConnectionFailure
From 8798a46983db63a5ee8b5838f6545b65e163cda3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 12 Aug 2021 23:28:55 -0700
Subject: [PATCH 0416/1276] Added More Debug for Callback (#3151)
Signed-off-by: David Deal
---
cla-backend-go/cmd/gitlab/auth/main.go | 12 ++++++------
cla-backend-go/config/ssm.go | 2 ++
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/cla-backend-go/cmd/gitlab/auth/main.go b/cla-backend-go/cmd/gitlab/auth/main.go
index 798a07ef6..33c72e56d 100644
--- a/cla-backend-go/cmd/gitlab/auth/main.go
+++ b/cla-backend-go/cmd/gitlab/auth/main.go
@@ -18,9 +18,9 @@ import (
)
const (
- REDIRECT_URI = "http://localhost:8080/gitlab/oauth/callback"
- APPLICATION_ID = "18718b478096e6a257eda51414d0d446ad28866c15187aa765f602fe906d0b17"
- APPLICATION_SECRET = "8dd14ace0eb0e4674b849b6fed4ce51bbcc456fc62d9149aff15353c1dda6327"
+ clientRedirectURI = "http://localhost:8080/gitlab/oauth/callback"
+ clientAppID = "18718b478096e6a257eda51414d0d446ad28866c15187aa765f602fe906d0b17"
+ clientAppSecret = "8dd14ace0eb0e4674b849b6fed4ce51bbcc456fc62d9149aff15353c1dda6327"
)
const (
@@ -168,11 +168,11 @@ func main() {
client := resty.New()
params := map[string]string{
- "client_id": APPLICATION_ID,
- "client_secret": APPLICATION_SECRET,
+ "client_id": clientAppID,
+ "client_secret": clientAppSecret,
"code": code,
"grant_type": "authorization_code",
- "redirect_uri": REDIRECT_URI,
+ "redirect_uri": clientRedirectURI,
}
resp, err := client.R().
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index d9c4ffeac..643216973 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -159,8 +159,10 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.Gitlab.ClientSecret = resp.value
case fmt.Sprintf("cla-gitlab-app-id-%s", stage):
config.Gitlab.AppID = resp.value
+ log.WithFields(f).Debugf("CLA GitLab App ID: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-private-key-%s", stage):
config.Gitlab.AppPrivateKey = resp.value
+ log.WithFields(f).Debugf("CLA GitLab App Private Key: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage):
config.Gitlab.RedirectURI = resp.value
case fmt.Sprintf("cla-gitlab-app-web-hook-uri-%s", stage):
From 8c15b0e54f01367517e06df6eb8a1c20e3153b99 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 13 Aug 2021 08:48:08 -0700
Subject: [PATCH 0417/1276] Testing GitLab Secret Fix (#3152)
---
cla-backend-go/config/ssm.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 643216973..1cf481f26 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -155,13 +155,14 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.GitHub.TestRepositoryID = resp.value
// gitlab ssm
- case fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage):
- config.Gitlab.ClientSecret = resp.value
+ //case fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage):
+ // config.Gitlab.ClientSecret = resp.value
case fmt.Sprintf("cla-gitlab-app-id-%s", stage):
config.Gitlab.AppID = resp.value
log.WithFields(f).Debugf("CLA GitLab App ID: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-private-key-%s", stage):
config.Gitlab.AppPrivateKey = resp.value
+ config.Gitlab.ClientSecret = resp.value
log.WithFields(f).Debugf("CLA GitLab App Private Key: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage):
config.Gitlab.RedirectURI = resp.value
From b5344bfcbf9d23c06ade7527aebd9585b08668e2 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 13 Aug 2021 11:02:51 -0700
Subject: [PATCH 0418/1276] Added GitLab App Client Secret (#3153)
---
cla-backend-go/cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/config/config.go | 12 ++++++------
cla-backend-go/config/ssm.go | 14 +++++++++-----
cla-backend-go/gitlab/auth.go | 12 ++++++------
cla-backend-go/gitlab/client_test.go | 5 +++--
cla-backend-go/gitlab/init.go | 11 +++++++++--
cla-backend-go/v2/gitlab-activity/service.go | 2 +-
cla-backend-go/v2/gitlab_organizations/service.go | 5 ++---
9 files changed, 38 insertions(+), 27 deletions(-)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index f396fd985..ceb81722d 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -94,7 +94,7 @@ func init() {
token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- _ = gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
+ _ = gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
project_service.InitClient(configFile.APIGatewayURL)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 3f1370192..67fdf51ae 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -237,7 +237,7 @@ func server(localMode bool) http.Handler {
// initialize github
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- _ = gitlab.Init(configFile.Gitlab.AppID, configFile.Gitlab.AppPrivateKey)
+ _ = gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 07c6d58dd..47407cf23 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -137,13 +137,13 @@ type GitHub struct {
TestRepositoryID string `json:"test_repository_id"`
}
-// Gitlab model
+// Gitlab config data model
type Gitlab struct {
- ClientSecret string `json:"clientSecret"`
- AppID string `json:"app_id"`
- AppPrivateKey string `json:"app_private_key"`
- RedirectURI string `json:"redirect_uri"`
- WebHookURI string `json:"web_hook_uri"`
+ AppClientID string `json:"app_client_id"`
+ AppClientSecret string `json:"app_client_secret"`
+ AppPrivateKey string `json:"app_client_private_key"`
+ RedirectURI string `json:"app_redirect_uri"`
+ WebHookURI string `json:"app_web_hook_uri"`
}
// MetricsReport keeps the config needed to send the metrics data report
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 1cf481f26..b0f80e695 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -69,8 +69,9 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-gh-test-organization-installation-id-%s", stage),
fmt.Sprintf("cla-gh-test-repository-%s", stage),
fmt.Sprintf("cla-gh-test-repository-id-%s", stage),
- fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage),
+ //fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage),
fmt.Sprintf("cla-gitlab-app-id-%s", stage),
+ fmt.Sprintf("cla-gitlab-app-secret-%s", stage),
fmt.Sprintf("cla-gitlab-app-private-key-%s", stage),
fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage),
fmt.Sprintf("cla-gitlab-app-web-hook-uri-%s", stage),
@@ -155,14 +156,17 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.GitHub.TestRepositoryID = resp.value
// gitlab ssm
- //case fmt.Sprintf("cla-gitlab-oauth-secret-go-backend-%s", stage):
- // config.Gitlab.ClientSecret = resp.value
case fmt.Sprintf("cla-gitlab-app-id-%s", stage):
- config.Gitlab.AppID = resp.value
+ config.Gitlab.AppClientID = resp.value
+ // DEBUG
log.WithFields(f).Debugf("CLA GitLab App ID: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
+ case fmt.Sprintf("cla-gitlab-app-secret-%s", stage):
+ config.Gitlab.AppClientSecret = resp.value
+ // DEBUG
+ log.WithFields(f).Debugf("CLA GitLab App Secret: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-private-key-%s", stage):
config.Gitlab.AppPrivateKey = resp.value
- config.Gitlab.ClientSecret = resp.value
+ // DEBUG
log.WithFields(f).Debugf("CLA GitLab App Private Key: %s...%s", resp.value[0:4], resp.value[len(resp.value)-4:])
case fmt.Sprintf("cla-gitlab-app-redirect-uri-%s", stage):
config.Gitlab.RedirectURI = resp.value
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab/auth.go
index 6f374437c..d18fce41e 100644
--- a/cla-backend-go/gitlab/auth.go
+++ b/cla-backend-go/gitlab/auth.go
@@ -22,13 +22,13 @@ func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
"redirectURI": config.GetConfig().Gitlab.RedirectURI,
}
- if len(gitLabConfig.AppID) > 4 {
- f["gitLabClientID"] = fmt.Sprintf("%s...%s", gitLabConfig.AppID[0:4], gitLabConfig.AppID[len(gitLabConfig.AppID)-4:])
+ if len(gitLabConfig.AppClientID) > 4 {
+ f["gitLabClientID"] = fmt.Sprintf("%s...%s", gitLabConfig.AppClientID[0:4], gitLabConfig.AppClientID[len(gitLabConfig.AppClientID)-4:])
} else {
return nil, errors.New("gitlab application client ID value is not set - value is empty or malformed")
}
- if len(gitLabConfig.ClientSecret) > 4 {
- f["gitLabClientSecret"] = fmt.Sprintf("%s...%s", gitLabConfig.ClientSecret[0:4], gitLabConfig.ClientSecret[len(gitLabConfig.ClientSecret)-4:])
+ if len(gitLabConfig.AppClientSecret) > 4 {
+ f["gitLabClientSecret"] = fmt.Sprintf("%s...%s", gitLabConfig.AppClientSecret[0:4], gitLabConfig.AppClientSecret[len(gitLabConfig.AppClientSecret)-4:])
} else {
return nil, errors.New("gitlab application client secret value is not set - value is empty or malformed")
}
@@ -36,8 +36,8 @@ func FetchOauthCredentials(code string) (*OauthSuccessResponse, error) {
// For info on this authorization flow, see: https://docs.gitlab.com/ee/api/oauth2.html#authorization-code-flow
client := resty.New()
params := map[string]string{
- "client_id": gitLabConfig.AppID,
- "client_secret": gitLabConfig.ClientSecret,
+ "client_id": gitLabConfig.AppClientID,
+ "client_secret": gitLabConfig.AppClientSecret,
"code": code,
"grant_type": "authorization_code",
"redirect_uri": gitLabConfig.RedirectURI,
diff --git a/cla-backend-go/gitlab/client_test.go b/cla-backend-go/gitlab/client_test.go
index 45149a423..7c6d0967c 100644
--- a/cla-backend-go/gitlab/client_test.go
+++ b/cla-backend-go/gitlab/client_test.go
@@ -11,11 +11,12 @@ import (
)
var glClientID = "124453345"
+var glClientSecret = "124453345"
var glClientKey = "0WqnDWHnZKo2cmQ8m93EtY9ZBpfzQW4UnnEuRmgtJKM="
var oauthRespStr = `{"access_token":"a30671b8749ba5d48925712344377f11a5aba43ec630f099e464b9843796e6a6","token_type":"Bearer","expires_in":0,"refresh_token":"0838a31d0d796973eacefdf513523e6e47aa06fac9d26622964da1e473509458","created_at":1626435922}`
func TestNewGitlabOauthClient(t *testing.T) {
- gitLabApp := Init(glClientID, glClientKey)
+ gitLabApp := Init(glClientID, glClientSecret, glClientKey)
t.Logf("app private ID is : %s", gitLabApp.GetAppID())
t.Logf("app private key is : %s", gitLabApp.GetAppPrivateKey())
@@ -33,7 +34,7 @@ func TestNewGitlabOauthClient(t *testing.T) {
}
func TestEncryptDecryptAuthInfo(t *testing.T) {
- gitLabApp := Init(glClientID, glClientKey)
+ gitLabApp := Init(glClientID, glClientSecret, glClientKey)
t.Logf("app private ID is : %s", gitLabApp.GetAppID())
t.Logf("app private key is : %s", gitLabApp.GetAppPrivateKey())
diff --git a/cla-backend-go/gitlab/init.go b/cla-backend-go/gitlab/init.go
index 8ed849ce3..e3beb6127 100644
--- a/cla-backend-go/gitlab/init.go
+++ b/cla-backend-go/gitlab/init.go
@@ -11,8 +11,9 @@ import (
// App is a wrapper for the GitLab configuration items
type App struct {
- gitLabAppPrivateKey string
gitLabAppID string
+ gitLabAppSecret string
+ gitLabAppPrivateKey string
}
var gitLabAppSingleton *App
@@ -20,13 +21,14 @@ var gitLabAppSingleton *App
var once sync.Once
// Init initializes the required gitlab variables
-func Init(glAppID string, glAppPrivateKey string) *App {
+func Init(glAppID, glAppSecret, glAppPrivateKey string) *App {
if gitLabAppSingleton == nil {
once.Do(
func() {
log.Debug("Creating object single instance...")
gitLabAppSingleton = &App{
gitLabAppID: glAppID,
+ gitLabAppSecret: glAppSecret,
gitLabAppPrivateKey: glAppPrivateKey,
}
})
@@ -41,6 +43,11 @@ func (app *App) GetAppID() string {
return app.gitLabAppID
}
+// GetAppSecret returns the GitLab application secret
+func (app *App) GetAppSecret() string {
+ return app.gitLabAppSecret
+}
+
// GetAppPrivateKey returns the GitLab application private key
func (app *App) GetAppPrivateKey() string {
return app.gitLabAppPrivateKey
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 30dce2993..f2a416338 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -59,7 +59,7 @@ func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRe
projectsCLAGroupsRepository: projectsCLAGroupsRepository,
companyRepository: companyRepository,
signatureRepository: signatureRepository,
- gitLabApp: gitlab2.Init(config.GetConfig().Gitlab.AppID, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitlab2.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index f813f9bd9..fb8808d0c 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -45,7 +45,7 @@ func NewService(repo RepositoryInterface, claGroupRepository projects_cla_groups
return service{
repo: repo,
claGroupRepository: claGroupRepository,
- gitLabApp: gitlab.Init(config.GetConfig().Gitlab.AppID, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitlab.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -79,7 +79,6 @@ func (s service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganiz
}
return s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, authInfoEncrypted)
-
}
func (s service) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
@@ -296,7 +295,7 @@ func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI
state := fmt.Sprintf("%s:%s", gitlabOrgID, authStateNonce)
params := url.Values{}
- params.Add("client_id", c.Gitlab.AppID)
+ params.Add("client_id", c.Gitlab.AppClientID)
params.Add("redirect_uri", c.Gitlab.RedirectURI)
//params.Add("redirect_uri", "http://localhost:8080/v4/gitlab/oauth/callback")
params.Add("response_type", "code")
From 3877167a346d1ea0bd2adc46bb238d5b5f5079d9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 13 Aug 2021 16:35:11 -0700
Subject: [PATCH 0419/1276] Adding GitLab Install Success Page (#3154)
---
cla-backend-go/cmd/server.go | 4 +-
cla-backend-go/gitlab/client.go | 5 +
.../models.go | 7 +-
cla-backend-go/v2/gitlab-activity/service.go | 4 +-
.../v2/gitlab_organizations/handlers.go | 135 ++++++++++--
.../v2/gitlab_organizations/repository.go | 32 +--
.../v2/gitlab_organizations/service.go | 196 +++++++++++-------
.../v2/repositories/gitlab_services.go | 74 +++++++
cla-backend-go/v2/repositories/service.go | 9 +-
9 files changed, 357 insertions(+), 109 deletions(-)
rename cla-backend-go/v2/{gitlab_organizations => common}/models.go (86%)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 67fdf51ae..a3a34bef8 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -345,10 +345,10 @@ func server(localMode bool) http.Handler {
v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
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)
+ gitlab_organizations.Configure(v2API, gitlabOrganizationsService, v2RepositoriesService, eventsService)
+ gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
v2Gerrits.Configure(v2API, gerritService, v1ProjectService, eventsService, v1ProjectClaGroupRepo)
v2Company.Configure(v2API, v2CompanyService, v1ProjectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleV1URL)
diff --git a/cla-backend-go/gitlab/client.go b/cla-backend-go/gitlab/client.go
index 06a0e7e5f..145a9e73c 100644
--- a/cla-backend-go/gitlab/client.go
+++ b/cla-backend-go/gitlab/client.go
@@ -38,6 +38,11 @@ func NewGitlabOauthClient(authInfo string, gitLabApp *App) (*gitlab.Client, erro
return gitlab.NewOAuthClient(oauthResp.AccessToken)
}
+// NewGitlabOauthClientFromAccessToken creates a new gitlab client from the given access token
+func NewGitlabOauthClientFromAccessToken(accessToken string) (*gitlab.Client, error) {
+ return gitlab.NewOAuthClient(accessToken)
+}
+
// EncryptAuthInfo encrypts the oauth response into a string
func EncryptAuthInfo(oauthResp *OauthSuccessResponse, gitLabApp *App) (string, error) {
keyDecoded, err := base64.StdEncoding.DecodeString(gitLabApp.GetAppPrivateKey())
diff --git a/cla-backend-go/v2/gitlab_organizations/models.go b/cla-backend-go/v2/common/models.go
similarity index 86%
rename from cla-backend-go/v2/gitlab_organizations/models.go
rename to cla-backend-go/v2/common/models.go
index 2094a002b..d3fc221bd 100644
--- a/cla-backend-go/v2/gitlab_organizations/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -1,4 +1,4 @@
-package gitlab_organizations
+package common
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT
@@ -10,6 +10,7 @@ import (
// GitlabOrganization is data model for gitlab organizations
type GitlabOrganization struct {
OrganizationID string `json:"organization_id"`
+ ExternalGroupID int `json:"external_gitlab_group_id"`
DateCreated string `json:"date_created,omitempty"`
DateModified string `json:"date_modified,omitempty"`
OrganizationName string `json:"organization_name,omitempty"`
@@ -38,10 +39,12 @@ func ToModel(in *GitlabOrganization) *models2.GitlabOrganization {
AutoEnabled: in.AutoEnabled,
AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
ProjectSFID: in.ProjectSFID,
+ // Not exposing ExternalGroupID
}
}
-func toModels(input []*GitlabOrganization) []*models2.GitlabOrganization {
+// ToModels converts a list of GitLab organizations to a list of external GitLab organization response models
+func ToModels(input []*GitlabOrganization) []*models2.GitlabOrganization {
out := make([]*models2.GitlabOrganization, 0)
for _, in := range input {
out = append(out, ToModel(in))
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index f2a416338..625b6d82c 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -11,6 +11,8 @@ import (
"strconv"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/common"
+
"github.com/communitybridge/easycla/cla-backend-go/config"
"github.com/communitybridge/easycla/cla-backend-go/company"
@@ -160,7 +162,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
return err
}
-func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*gitlab_organizations.GitlabOrganization, error) {
+func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*common.GitlabOrganization, error) {
repositoryPath := mergeEvent.Project.PathWithNamespace
parts := strings.Split(repositoryPath, "/")
organizationName := parts[0]
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index d6100aadc..3a375d69a 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -7,8 +7,11 @@ import (
"context"
"errors"
"fmt"
+ "net/http"
"strings"
+ "github.com/go-openapi/runtime"
+
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
@@ -26,11 +29,12 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/utils"
+ v2GitRepo "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"
"github.com/go-openapi/runtime/middleware"
)
// Configure setups handlers on api with service
-func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+func Configure(api *operations.EasyclaAPI, service ServiceInterface, gitV2Service v2GitRepo.ServiceInterface, eventService events.Service) {
api.GitlabOrganizationsGetProjectGitlabOrganizationsHandler = gitlab_organizations.GetProjectGitlabOrganizationsHandlerFunc(
func(params gitlab_organizations.GetProjectGitlabOrganizationsParams, authUser *auth.User) middleware.Responder {
@@ -283,37 +287,134 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
stateVar := codeParts[1]
ctx := context.Background()
- _, err := service.GetGitlabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
+ gitLabOrg, err := service.GetGitlabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
if err != nil {
- msg := fmt.Sprintf("fetching gitlab model failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
return gitlab_activity.NewGitlabOauthCallbackBadRequest().WithPayload(
- utils.ErrorResponseBadRequest(reqID, msg))
+ utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("fetching gitlab model failed : %s : %v", gitlabOrganizationID, err)))
}
// now fetch the oauth credentials and store to db
oauthResp, err := gitlab.FetchOauthCredentials(params.Code)
if err != nil {
- msg := fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
return gitlab_activity.NewGitlabOauthCallbackInternalServerError().WithPayload(
- utils.ErrorResponseBadRequest(reqID, msg))
+ utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)))
}
- log.Infof("oauth resp is like : %+v", oauthResp)
+ log.WithFields(f).Debugf("oauth resp is like : %+v", oauthResp)
err = service.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
if err != nil {
- msg := fmt.Sprintf("updating gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
return gitlab_activity.NewGitlabOauthCallbackInternalServerError().WithPayload(
- utils.ErrorResponseBadRequest(reqID, msg))
+ utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("updating gitlab credentials failed : %s : %v", gitlabOrganizationID, err)))
}
- return gitlab_activity.NewGitlabOauthCallbackOK().WithPayload(&models.SuccessResponse{
- Code: "200",
- Message: "oauth credentials stored successfully",
- XRequestID: reqID,
- })
+ // Reload the GitLab organization - will have additional details now...
+ updatedGitLabOrgDBModel, err := service.GetGitlabOrganizationByID(ctx, gitLabOrg.OrganizationID)
+ if err != nil {
+ return gitlab_activity.NewGitlabOauthCallbackInternalServerError().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, fmt.Sprintf("problem loading updated gitlab organization by ID: %s : %v", gitlabOrganizationID, err)))
+ }
+
+ _, err = gitV2Service.GitLabAddRepositoriesByApp(ctx, updatedGitLabOrgDBModel)
+ if err != nil {
+ return NewRedirectServerError(reqID, updatedGitLabOrgDBModel.OrganizationName, err)
+ }
+
+ return NewRedirectOK(reqID, updatedGitLabOrgDBModel.ProjectSFID, updatedGitLabOrgDBModel.OrganizationName)
})
}
+
+// GetRedirectOK Success
+type GetRedirectOK struct {
+ ReqID string
+ ProjectSFID string
+ GitLabGroupName string
+}
+
+// NewRedirectOK creates a new redirect handler
+func NewRedirectOK(reqID, projectSFID, gitLabGroupName string) *GetRedirectOK {
+ return &GetRedirectOK{reqID, projectSFID, gitLabGroupName}
+}
+
+// WriteResponse to the client
+func (o *GetRedirectOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+ configPage := "https://gitlab.com/-/profile/applications"
+
+ html := fmt.Sprintf(`
+
+
+ LFX EasyCLA Service GitLab App Installation Status
+
+
+
+
+
+
+
+
+
+
+
+
+
LFx EasyCLA Service GitLab App - Installation Successful
+
Thank you for installing the LFX EasyCLA GitLab Application/Bot. Your GitLab Group and repositories are now onboarded.
`, failed, authorInfo, gitlabSupportURL, easyCLASupportURL)
+ result += msg
+ } else if errors.Is(missingUser.err, missingCompanyAffiliation) {
+ msg := fmt.Sprintf(`
%s is authorized, but they must confirm their affiliation with their company.
+ Start the authorization process
+ by clicking here, click "Corporate",
+ select the appropriate company from the list, then confirm
+ your affiliation on the page that appears.
+ For further assistance with EasyCLA,
+ please submit a support request ticket.
+
`, signURL, failed, authorInfo, signURL, easyCLASupportURL)
+ result += msg
+
+ }
+ }
+ result += "
"
+ }
+
+ return result
+}
+
+func GetFullSignURL(gitlabOrganizationID string, gitlabRepositoryID string, mrID string) string {
+ return fmt.Sprintf("%s/v2/repository-provider/%s/sign/%s/%s/%s/#/",
+ config.GetConfig().APIGatewayURL,
+ utils.GitLabLower,
+ gitlabOrganizationID,
+ gitlabRepositoryID,
+ mrID,
+ )
+}
+
+func getAuthorInfo(gitlabUser *gitlab.User) string {
+ return fmt.Sprintf("%d:%s", gitlabUser.ID, gitlabUser.Username)
+}
+
func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*common.GitlabOrganization, error) {
repositoryPath := mergeEvent.Project.PathWithNamespace
parts := strings.Split(repositoryPath, "/")
@@ -184,17 +270,13 @@ func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeE
return gitlabOrg, nil
}
-func (s service) getGitlabRepoByExternalID(ctx context.Context, orgName, gitlabRepoID string) (*models.GithubRepository, error) {
- gitlabRepo, err := s.gitV2Repository.GitLabGetRepositoryByName(ctx, orgName)
+func (s service) getGitlabRepoByName(ctx context.Context, repoNameWithPath string) (*models.GithubRepository, error) {
+ gitlabRepo, err := s.gitV2Repository.GitLabGetRepositoryByName(ctx, repoNameWithPath)
if err != nil || gitlabRepo == nil {
- return nil, fmt.Errorf("unable to locate GitLab repo for external id : %s, orgName : %s, failed : %v", gitlabRepoID, orgName, err)
- }
-
- if gitlabRepo.RepositoryExternalID == gitlabRepoID && gitlabRepo.RepositoryType == "gitlab" {
- return gitlabRepo.ToGitHubModel(), nil
+ return nil, fmt.Errorf("unable to locate GitLab repo for repoNameWithPath : %s, failed : %v", repoNameWithPath, err)
}
- return nil, fmt.Errorf("no repositories found for orgName : %s and gitlab external id : %s", orgName, gitlabRepoID)
+ return gitlabRepo.ToGitHubModel(), nil
}
func (s service) hasUserSigned(ctx context.Context, claGroupID string, gitlabUser *gitlab.User) (bool, error) {
@@ -214,7 +296,7 @@ func (s service) hasUserSigned(ctx context.Context, claGroupID string, gitlabUse
if userModel == nil {
msg := fmt.Sprintf("gitlab user : %d:%s not found in easycla records", gitlabUser.ID, gitlabUser.Username)
log.WithFields(f).Error(msg)
- return false, fmt.Errorf(msg)
+ return false, missingID
}
log.WithFields(f).Debugf("found following easyCLA user for gitlab record, userID: %s, lfusername : %s", userModel.UserID, userModel.LfUsername)
@@ -260,6 +342,11 @@ func (s service) hasUserSigned(ctx context.Context, claGroupID string, gitlabUse
return false, fmt.Errorf(msg)
}
+ if !IsUserApprovedForSignature(f, corporateSignature, userModel, gitlabUser) {
+ log.WithFields(f).Debugf("user is not approved in signature : %s", corporateSignature.SignatureID)
+ return false, missingCompanyApproval
+ }
+
employeeSignatures, err := s.signaturesRepository.GetProjectCompanyEmployeeSignatures(ctx, signatures1.GetProjectCompanyEmployeeSignaturesParams{
CompanyID: companyID,
ProjectID: claGroupID,
@@ -274,16 +361,11 @@ func (s service) hasUserSigned(ctx context.Context, claGroupID string, gitlabUse
if len(employeeSignatures.Signatures) == 0 {
msg := fmt.Sprintf("no employee signature records found for company : %s user : %s association", companyID, userModel.UserID)
log.WithFields(f).Errorf(msg)
- return false, fmt.Errorf(msg)
- }
-
- if IsUserApprovedForSignature(f, corporateSignature, userModel, gitlabUser) {
- log.WithFields(f).Debugf("user is approved in signature : %s", corporateSignature.SignatureID)
- return true, nil
+ return false, missingCompanyAffiliation
}
- log.WithFields(f).Warnf("user not in one of the approval lists")
- return false, fmt.Errorf("not signed")
+ log.WithFields(f).Warnf("is in signature approval list : %s and has employee signature", corporateSignature.SignatureID)
+ return true, nil
}
func (s service) findUserModelForGitlabUser(f logrus.Fields, gitlabUser *gitlab.User) (*models.User, bool, error) {
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
index fd77b482b..7b651d5a1 100644
--- a/cla-backend-go/v2/gitlab-activity/service_test.go
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -4,6 +4,8 @@
package gitlab_activity
import (
+ "fmt"
+ "strings"
"testing"
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
@@ -102,3 +104,85 @@ func TestIsUserApprovedForSignature(t *testing.T) {
}
}
+
+func TestPrepareMrCommentContent(t *testing.T) {
+
+ signedContains := ":white_check_mark: %s"
+ missingUserContains := ":x: The commit associated with %s is missing the User's ID"
+ missingAffiliationContains := "%s is authorized, but they must confirm their affiliation"
+ missingApprovalContains := "%s's commit is not authorized under a signed CLA"
+
+ testCases := []struct {
+ name string
+ signed []*gitlab.User
+ missing []*gatedGitlabUser
+ expectedMsgs []string
+ }{
+ {
+ name: "all signed",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ {ID: 2, Username: "oracle"},
+ },
+ expectedMsgs: []string{signedContains, signedContains},
+ },
+ {
+ name: "missing id",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingID, User: &gitlab.User{ID: 3, Username: "missing"}},
+ },
+ expectedMsgs: []string{signedContains, missingUserContains},
+ },
+ {
+ name: "missing affiliation",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingCompanyAffiliation, User: &gitlab.User{ID: 4, Username: "affiliationUser"}},
+ },
+ expectedMsgs: []string{signedContains, missingAffiliationContains},
+ },
+ {
+ name: "missing approval",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingCompanyApproval, User: &gitlab.User{ID: 5, Username: "approvalUser"}},
+ },
+ expectedMsgs: []string{signedContains, missingApprovalContains},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ result := PrepareMrCommentContent(tc.missing, tc.signed, "https://sign.com")
+ tt.Logf("the result is : %s", result)
+ parts := strings.Split(result, "
")
+ assert.Len(tt, parts, len(tc.expectedMsgs)+1)
+
+ var allUsers []*gitlab.User
+
+ if len(tc.signed) > 0 {
+ for _, s := range tc.signed {
+ allUsers = append(allUsers, s)
+ }
+ }
+
+ if len(tc.missing) > 0 {
+ for _, m := range tc.missing {
+ allUsers = append(allUsers, m.User)
+ }
+ }
+
+ for i, p := range parts[1:] {
+ expected := fmt.Sprintf(tc.expectedMsgs[i], getAuthorInfo(allUsers[i]))
+ assert.Contains(tt, p, expected)
+ }
+ })
+ }
+}
From c4948979b0e40c1868fcfbef31c398c7393d60dd Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 17 Aug 2021 17:17:56 -0700
Subject: [PATCH 0428/1276] Updated GitLab Org Data Model (#3165)
---
cla-backend-go/swagger/cla.v2.yaml | 4 +-
.../swagger/common/gitlab-organization.yaml | 33 +++--
.../common/gitlab-project-organization.yaml | 10 +-
.../common/gitlab-repository-info.yaml | 17 +++
cla-backend-go/tests/gitlab_client_test.go | 134 +++++++++++++++---
cla-backend-go/v2/common/models.go | 38 ++++-
cla-backend-go/v2/gitlab-activity/service.go | 2 +-
.../v2/gitlab_organizations/constants.go | 37 +++++
.../v2/gitlab_organizations/repository.go | 78 +++++-----
.../v2/gitlab_organizations/service.go | 13 +-
.../v2/repositories/gitlab_services.go | 2 +-
cla-backend-go/v2/repositories/service.go | 2 +-
12 files changed, 287 insertions(+), 83 deletions(-)
create mode 100644 cla-backend-go/swagger/common/gitlab-repository-info.yaml
create mode 100644 cla-backend-go/v2/gitlab_organizations/constants.go
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 638828d42..d8f01d922 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -4556,8 +4556,8 @@ definitions:
github-repository-info:
$ref: './common/github-repository-info.yaml'
- #company:
- # $ref: './common/company.yaml'
+ gitlab-repository-info:
+ $ref: './common/gitlab-repository-info.yaml'
total-count-metrics:
type: object
diff --git a/cla-backend-go/swagger/common/gitlab-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization.yaml
index e344805bf..5e407708a 100644
--- a/cla-backend-go/swagger/common/gitlab-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization.yaml
@@ -3,29 +3,37 @@
type: object
properties:
- organizationID:
+ organization_id:
type: string
description: internal id of the gitlab organization
- dateCreated:
+ date_created:
type: string
example: "2020-02-06T09:31:49.245630+0000"
minLength: 18
maxLength: 64
- dateModified:
+ date_modified:
type: string
example: "2020-02-06T09:31:49.245646+0000"
minLength: 18
maxLength: 64
- organizationName:
+ organization_name:
type: string
example: "communitybridge"
- organizationSfid:
+ organization_url:
+ type: string
+ description: The Gitlab Group/Organization url
+ example: "github.com/Linux Foundation/product/EasyCLA"
+ organization_full_path:
+ type: string
+ description: The Gitlab Group/Organization full path
+ example: "linuxfoundation/product/easycla"
+ organization_sfid:
type: string
example: "a0941000002wBz4AAA"
version:
type: string
example: "v1"
- projectSFID:
+ project_sfid:
type: string
example: "a0941000002wBz4AAA"
enabled:
@@ -36,14 +44,14 @@ properties:
type: boolean
description: Flag that indicates whether this Gitlab Organization is authorized with Gitlab, if false it might mean that Gitlab Oauth process is not compeleted yet or the token was revoked and user needs to go through the auth process again
x-omitempty: false
- autoEnabled:
+ auto_enabled:
type: boolean
description: Flag to indicate if this Gitlab Organization is configured to allow new repositories to be auto-enabled/auto-enrolled in EasyCLA.
x-omitempty: false
- autoEnabledClaGroupID:
+ auto_enabled_cla_group_id:
type: string
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
- gitlabInfo:
+ gitlab_info:
type: object
properties:
error:
@@ -59,17 +67,16 @@ properties:
bio:
type: string
x-nullable: true
- htmlUrl:
+ html_url:
type: string
x-nullable: true
example: "https://github.com/communitybridge"
format: uri
- installationURL:
+ installation_url:
type: string
x-nullable: true
description: "if the Gitlab Organization is not connected yet can use this url to go through the process of authorizing the easyCLA bot"
format: uri
-
repositories:
type: object
properties:
@@ -79,4 +86,4 @@ properties:
list:
type: array
items:
- $ref: '#/definitions/github-repository-info'
+ $ref: '#/definitions/gitlab-repository-info'
diff --git a/cla-backend-go/swagger/common/gitlab-project-organization.yaml b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
index e1701acef..f1062c3c1 100644
--- a/cla-backend-go/swagger/common/gitlab-project-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
@@ -22,7 +22,7 @@ properties:
type: string
x-nullable: true
format: uri
- gitlab_organization_name:
+ organization_name:
type: string
description: The Gitlab Organization name
example: "kubernetes"
@@ -31,6 +31,14 @@ properties:
pattern: '^([\w\-\.]+){2,255}$'
minLength: 2
maxLength: 255
+ organization_url:
+ type: string
+ description: The Gitlab Group/Organization url
+ example: "github.com/Linux Foundation/product/EasyCLA"
+ organization_full_path:
+ type: string
+ description: The Gitlab Group/Organization full path
+ example: "linuxfoundation/product/easycla"
connection_status:
type: string
enum:
diff --git a/cla-backend-go/swagger/common/gitlab-repository-info.yaml b/cla-backend-go/swagger/common/gitlab-repository-info.yaml
new file mode 100644
index 000000000..b88c4d414
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-repository-info.yaml
@@ -0,0 +1,17 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+properties:
+ repository_gitlab_id:
+ type: integer
+ example: 250509778
+ repository_name:
+ type: string
+ example: "communitybridge/easycla"
+ repository_type:
+ type: string
+ example: "github"
+ repository_url:
+ type: string
+ example: "https://github.com/communitybridge/easycla"
diff --git a/cla-backend-go/tests/gitlab_client_test.go b/cla-backend-go/tests/gitlab_client_test.go
index f313e52fc..851c0b61a 100644
--- a/cla-backend-go/tests/gitlab_client_test.go
+++ b/cla-backend-go/tests/gitlab_client_test.go
@@ -6,8 +6,14 @@ package tests
import (
"encoding/json"
"fmt"
+ "io"
+ "net/url"
+ "os"
"testing"
+ ini "github.com/communitybridge/easycla/cla-backend-go/init"
+ "github.com/spf13/viper"
+
"github.com/communitybridge/easycla/cla-backend-go/utils"
gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
@@ -17,30 +23,102 @@ import (
const enabled = false // nolint
const group = "The Linux Foundation/product/EasyCLA"
-const accessToken = ""
+const accessInfo = ""
+
+const easyCLAGroupName = "linuxfoundation/product/easycla"
+
+func TestGitLabGetGroup(t *testing.T) { // no lint
+
+ if enabled { // nolint
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
-func TestGitLabSearchGroup(t *testing.T) { // no lint
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
+
+ // Need to look up the GitLab Group/Organization to obtain the ID
+ groupModel, resp, getError := gitLabClient.Groups.GetGroup(url.QueryEscape(easyCLAGroupName))
+ assert.Nil(t, getError, "GitLab GetGroup Error is Nil")
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ assert.Fail(t, fmt.Sprintf("unable to locate GitLab group by value: %s, status code: %d", easyCLAGroupName, resp.StatusCode))
+ }
+ assert.NotNil(t, groupModel, "Group Model is not nil")
+ t.Logf("group name: %s, ID: %d, path: %s", groupModel.Name, groupModel.ID, groupModel.Path)
+ }
+}
+
+func TestGitLabListGroups(t *testing.T) { // no lint
if enabled { // nolint
- // Get the client
- gitLabClient, err := gitlab2.NewGitlabOauthClientFromAccessToken(accessToken)
- assert.Nil(t, err, "GitLab OAuth Client")
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
// Need to look up the GitLab Group/Organization to obtain the ID
opts := &gitlab.ListGroupsOptions{
- ListOptions: gitlab.ListOptions{},
+ ListOptions: gitlab.ListOptions{
+ Page: 1,
+ PerPage: 100,
+ },
}
groups, resp, searchErr := gitLabClient.Groups.ListGroups(opts)
- assert.Nil(t, searchErr, "GitLab OAuth Client")
+ assert.Nil(t, searchErr, "GitLab List Groups Error is Nil")
+ if searchErr != nil {
+ t.Logf("list groups error: %+v", searchErr)
+ }
if resp.StatusCode < 200 || resp.StatusCode > 299 {
- assert.Fail(t, "unable to locate GitLab group by name: %s, status code: %d", group, resp.StatusCode)
+ respBody, readErr := io.ReadAll(resp.Body)
+ assert.Nil(t, readErr, "GitLab Response Body Read is Nil")
+ assert.Fail(t, fmt.Sprintf("unable to list GitLab groups, status code: %d, body: %s", resp.StatusCode, respBody))
}
for _, g := range groups {
- t.Logf("group name: %s, ID: %d, path: %s", g.Name, g.ID, g.Path)
- }
- if len(groups) != 1 {
-
- assert.Fail(t, fmt.Sprintf("expecting 1 result for GitLab group name '%s' search, found: %d - %+v", group, len(groups), groups))
+ t.Logf("name: %s, id: %d, web url: %s, path: %s, full path: %s", g.Name, g.ID, g.WebURL, g.Path, g.FullPath)
}
}
}
@@ -48,9 +126,33 @@ func TestGitLabSearchGroup(t *testing.T) { // no lint
func TestGitLabListProjects(t *testing.T) { // no lint
if enabled { // nolint
- // Get the client
- gitLabClient, err := gitlab2.NewGitlabOauthClientFromAccessToken(accessToken)
- assert.Nil(t, err, "GitLab OAuth Client")
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
// Query GitLab for repos - fetch the list of repositories available to the GitLab App
listProjectsOpts := &gitlab.ListProjectsOptions{
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index d3fc221bd..2e284076c 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -1,20 +1,22 @@
-package common
-
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT
+package common
+
import (
models2 "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
)
-// GitlabOrganization is data model for gitlab organizations
-type GitlabOrganization struct {
+// GitLabOrganization is data model for gitlab organizations
+type GitLabOrganization struct {
OrganizationID string `json:"organization_id"`
ExternalGroupID int `json:"external_gitlab_group_id"`
DateCreated string `json:"date_created,omitempty"`
DateModified string `json:"date_modified,omitempty"`
OrganizationName string `json:"organization_name,omitempty"`
OrganizationNameLower string `json:"organization_name_lower,omitempty"`
+ OrganizationFullPath string `json:"organization_full_path,omitempty"`
+ OrganizationURL string `json:"organization_url,omitempty"`
OrganizationSFID string `json:"organization_sfid,omitempty"`
ProjectSFID string `json:"project_sfid"`
Enabled bool `json:"enabled"`
@@ -27,27 +29,49 @@ type GitlabOrganization struct {
}
// ToModel converts to models.GitlabOrganization
-func ToModel(in *GitlabOrganization) *models2.GitlabOrganization {
+func ToModel(in *GitLabOrganization) *models2.GitlabOrganization {
return &models2.GitlabOrganization{
OrganizationID: in.OrganizationID,
DateCreated: in.DateCreated,
DateModified: in.DateModified,
OrganizationName: in.OrganizationName,
+ OrganizationFullPath: in.OrganizationFullPath,
+ OrganizationURL: in.OrganizationURL,
OrganizationSfid: in.OrganizationSFID,
Version: in.Version,
Enabled: in.Enabled,
AutoEnabled: in.AutoEnabled,
AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
- ProjectSFID: in.ProjectSFID,
+ ProjectSfid: in.ProjectSFID,
// Not exposing ExternalGroupID
}
}
// ToModels converts a list of GitLab organizations to a list of external GitLab organization response models
-func ToModels(input []*GitlabOrganization) []*models2.GitlabOrganization {
+func ToModels(input []*GitLabOrganization) []*models2.GitlabOrganization {
out := make([]*models2.GitlabOrganization, 0)
for _, in := range input {
out = append(out, ToModel(in))
}
return out
}
+
+// GitLabAddOrganization is data model for GitLab add organization requests
+type GitLabAddOrganization struct {
+ OrganizationID string `json:"organization_id"`
+ ExternalGroupID int `json:"external_gitlab_group_id"`
+ DateCreated string `json:"date_created,omitempty"`
+ DateModified string `json:"date_modified,omitempty"`
+ OrganizationName string `json:"organization_name,omitempty"`
+ OrganizationNameLower string `json:"organization_name_lower,omitempty"`
+ OrganizationURL string `json:"organization_url,omitempty"`
+ OrganizationSFID string `json:"organization_sfid,omitempty"`
+ ProjectSFID string `json:"project_sfid"`
+ Enabled bool `json:"enabled"`
+ AutoEnabled bool `json:"auto_enabled"`
+ BranchProtectionEnabled bool `json:"branch_protection_enabled"`
+ AutoEnabledClaGroupID string `json:"auto_enabled_cla_group_id,omitempty"`
+ AuthInfo string `json:"auth_info"`
+ AuthState string `json:"auth_state"`
+ Version string `json:"version,omitempty"`
+}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index d868732df..8949da8a5 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -248,7 +248,7 @@ func getAuthorInfo(gitlabUser *gitlab.User) string {
return fmt.Sprintf("%d:%s", gitlabUser.ID, gitlabUser.Username)
}
-func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*common.GitlabOrganization, error) {
+func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*common.GitLabOrganization, error) {
repositoryPath := mergeEvent.Project.PathWithNamespace
parts := strings.Split(repositoryPath, "/")
organizationName := parts[0]
diff --git a/cla-backend-go/v2/gitlab_organizations/constants.go b/cla-backend-go/v2/gitlab_organizations/constants.go
new file mode 100644
index 000000000..d84966b2a
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_organizations/constants.go
@@ -0,0 +1,37 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_organizations
+
+const (
+ // GitLabOrganizationsOrganizationIDColumn constant
+ GitLabOrganizationsOrganizationIDColumn = "organization_id"
+ // GitLabOrganizationsOrganizationSFIDColumn constant
+ GitLabOrganizationsOrganizationSFIDColumn = "organization_sfid"
+ // GitLabOrganizationsOrganizationNameColumn constant
+ GitLabOrganizationsOrganizationNameColumn = "organization_name"
+ // GitLabOrganizationsOrganizationNameLowerColumn constant
+ GitLabOrganizationsOrganizationNameLowerColumn = "organization_name_lower"
+ // GitLabOrganizationsEnabledColumn constant
+ GitLabOrganizationsEnabledColumn = "enabled"
+ // GitLabOrganizationsAutoEnabledColumn constant
+ GitLabOrganizationsAutoEnabledColumn = "auto_enabled"
+ // GitLabOrganizationsAutoEnabledCLAGroupIDColumn constant
+ GitLabOrganizationsAutoEnabledCLAGroupIDColumn = "auto_enabled_cla_group_id"
+ // GitLabOrganizationsBranchProtectionEnabledColumn constant
+ GitLabOrganizationsBranchProtectionEnabledColumn = "branch_protection_enabled"
+ // GitLabOrganizationsAuthInfoColumn constant
+ GitLabOrganizationsAuthInfoColumn = "auth_info"
+ // GitLabOrganizationsOrganizationURLColumn constant
+ GitLabOrganizationsOrganizationURLColumn = "organization_url"
+ // GitLabOrganizationsOrganizationFullPathColumn constant
+ GitLabOrganizationsOrganizationFullPathColumn = "organization_full_path"
+ // GitLabOrganizationsNoteColumn constant
+ GitLabOrganizationsNoteColumn = "note"
+ // GitLabOrganizationsDateCreatedColumn constant
+ GitLabOrganizationsDateCreatedColumn = "date_created"
+ // GitLabOrganizationsDateModifiedColumn constant
+ GitLabOrganizationsDateModifiedColumn = "date_modified"
+ // GitLabOrganizationsExternalGitLabGroupIDColumn constant
+ GitLabOrganizationsExternalGitLabGroupIDColumn = "external_gitlab_group_id"
+)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index db9097b97..d848f6bf8 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -37,9 +37,9 @@ const (
type RepositoryInterface interface {
AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
- GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitlabOrganization, error)
+ GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models2.GitlabOrganization, error)
- UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, gitLabGroupID int, authInfo string) error
+ UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
@@ -81,7 +81,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
if existingRecord != nil {
log.WithFields(f).Debugf("An existing GitLab organization with name %s exists in our database", gitLabOrganizationName)
// If everything matches...
- if projectSFID == existingRecord.ProjectSFID {
+ if projectSFID == existingRecord.ProjectSfid {
log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
enabledFlag := true
updateErr := repo.UpdateGitlabOrganization(ctx, projectSFID, gitLabOrganizationName,
@@ -113,7 +113,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
}
enabled := true
- gitlabOrg := &common.GitlabOrganization{
+ gitlabOrg := &common.GitLabOrganization{
OrganizationID: organizationID.String(),
DateCreated: currentTime,
DateModified: currentTime,
@@ -127,6 +127,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
BranchProtectionEnabled: aws.BoolValue(input.BranchProtectionEnabled),
AuthState: authStateNonce.String(),
Version: "v1",
+ // OrganizationURL: set later when we can authenticate to the API
}
log.WithFields(f).Debug("Encoding GitLab organization record for adding to the database...")
@@ -165,7 +166,7 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
"projectSFID": projectSFID,
}
- condition := expression.Key("organization_sfid").Equal(expression.Value(projectSFID))
+ condition := expression.Key(GitLabOrganizationsOrganizationSFIDColumn).Equal(expression.Value(projectSFID))
builder := expression.NewBuilder().WithKeyCondition(condition)
filter := expression.Name("enabled").Equal(expression.Value(true))
@@ -202,7 +203,7 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
}, nil
}
- var resultOutput []*common.GitlabOrganization
+ var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
return nil, err
@@ -223,7 +224,7 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOr
gitLabOrganizationName = strings.ToLower(gitLabOrganizationName)
- condition := expression.Key("organization_name_lower").Equal(expression.Value(strings.ToLower(gitLabOrganizationName)))
+ condition := expression.Key(GitLabOrganizationsOrganizationNameLowerColumn).Equal(expression.Value(strings.ToLower(gitLabOrganizationName)))
builder := expression.NewBuilder().WithKeyCondition(condition)
// Use the nice builder to create the expression
expr, err := builder.Build()
@@ -252,7 +253,7 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOr
return nil, nil
}
- var resultOutput []*common.GitlabOrganization
+ var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
@@ -265,7 +266,7 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOr
}
// GetGitlabOrganization by organization name
-func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitlabOrganization, error) {
+func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.GetGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -275,7 +276,7 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
log.WithFields(f).Debug("Querying for GitLab organization by name...")
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
Key: map[string]*dynamodb.AttributeValue{
- "organization_id": {
+ GitLabOrganizationsOrganizationIDColumn: {
S: aws.String(gitlabOrganizationID),
},
},
@@ -289,7 +290,7 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
return nil, nil
}
- var org common.GitlabOrganization
+ var org common.GitLabOrganization
err = dynamodbattribute.UnmarshalMap(result.Item, &org)
if err != nil {
log.WithFields(f).Warnf("error unmarshalling organization table data, error: %v", err)
@@ -299,35 +300,40 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
}
// UpdateGitlabOrganizationAuth updates the specified Gitlab organization oauth info
-func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, gitLabGroupID int, authInfo string) error {
+func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationID,
+ "organizationID": organizationID,
+ "organizationFullPath": organizationFullPath,
+ "organizationURL": organizationURL,
"tableName": repo.gitlabOrgTableName,
}
_, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
- if lookupErr != nil {
- log.WithFields(f).Warnf("error looking up Gitlab organization by id, error: %+v", lookupErr)
- return lookupErr
- }
- if gitlabOrg == nil {
- lookupErr := errors.New("unable to lookup Gitlab organization by id")
- log.WithFields(f).Warnf("error looking up Gitlab organization, error: %+v", lookupErr)
+ gitlabOrg, lookupErr := repo.GetGitlabOrganization(ctx, organizationID)
+ if lookupErr != nil || gitlabOrg == nil {
+ log.WithFields(f).Warnf("error looking up Gitlab organization by id: %s, error: %+v", organizationID, lookupErr)
return lookupErr
}
expressionAttributeNames := map[string]*string{
- "#A": aws.String("auth_info"),
- "#M": aws.String("date_modified"),
- "#P": aws.String("external_gitlab_group_id"),
+ "#A": aws.String(GitLabOrganizationsAuthInfoColumn),
+ "#U": aws.String(GitLabOrganizationsOrganizationURLColumn),
+ "#FP": aws.String(GitLabOrganizationsOrganizationFullPathColumn),
+ "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#P": aws.String(GitLabOrganizationsExternalGitLabGroupIDColumn),
}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{
":a": {
S: aws.String(authInfo),
},
+ ":u": {
+ S: aws.String(organizationURL),
+ },
+ ":fp": {
+ S: aws.String(organizationFullPath),
+ },
":m": {
S: aws.String(currentTime),
},
@@ -336,11 +342,11 @@ func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabO
},
}
- updateExpression := "SET #A = :a, #M = :m, #P = :p"
+ updateExpression := "SET #A = :a, #U = :u, #FP = :fp, #M = :m, #P = :p"
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
- "organization_id": {
+ GitLabOrganizationsOrganizationIDColumn: {
S: aws.String(gitlabOrg.OrganizationID),
},
},
@@ -384,10 +390,10 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
}
expressionAttributeNames := map[string]*string{
- "#A": aws.String("auto_enabled"),
- "#C": aws.String("auto_enabled_cla_group_id"),
- "#B": aws.String("branch_protection_enabled"),
- "#M": aws.String("date_modified"),
+ "#A": aws.String(GitLabOrganizationsAutoEnabledColumn),
+ "#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
+ "#B": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
+ "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{
":a": {
@@ -415,7 +421,7 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
- "organization_id": {
+ GitLabOrganizationsOrganizationIDColumn: {
S: aws.String(gitlabOrg.OrganizationID),
},
},
@@ -465,14 +471,14 @@ func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID
_, err := repo.dynamoDBClient.UpdateItem(
&dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
- "organization_id": {
+ GitLabOrganizationsOrganizationIDColumn: {
S: aws.String(gitlabOrganizationID),
},
},
ExpressionAttributeNames: map[string]*string{
- "#E": aws.String("enabled"),
- "#N": aws.String("note"),
- "#D": aws.String("date_modified"),
+ "#E": aws.String(GitLabOrganizationsEnabledColumn),
+ "#N": aws.String(GitLabOrganizationsNoteColumn),
+ "#D": aws.String(GitLabOrganizationsDateModifiedColumn),
},
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":e": {
@@ -498,7 +504,7 @@ func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID
return nil
}
-func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*common.GitlabOrganization) []*models2.GitlabOrganization {
+func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*common.GitLabOrganization) []*models2.GitlabOrganization {
f := logrus.Fields{
"functionName": "buildGitlabOrganizationListModels",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 838cf3341..821340811 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -36,7 +36,7 @@ import (
type ServiceInterface interface {
AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
- GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitlabOrganization, error)
+ GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
@@ -116,7 +116,7 @@ func (s *Service) GetGitlabOrganization(ctx context.Context, gitlabOrganizationI
}
// GetGitlabOrganizationByID returns the record associated with the GitLab Organization ID
-func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitlabOrganization, error) {
+func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -203,8 +203,11 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
AutoEnabled: org.AutoEnabled,
AutoEnableCLAGroupID: org.AutoEnabledClaGroupID,
AutoEnabledCLAGroupName: strings.TrimSpace(autoEnabledCLAGroupName),
- GitlabOrganizationName: org.OrganizationName,
+ OrganizationName: org.OrganizationName,
+ OrganizationURL: org.OrganizationURL,
+ OrganizationFullPath: org.OrganizationFullPath,
InstallationURL: buildInstallationURL(org.OrganizationID, orgDetailed.AuthState),
+ BranchProtectionEnabled: false,
ConnectionStatus: "", // updated below
Repositories: []*models.GitlabProjectRepository{},
}
@@ -242,7 +245,7 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
// Sort everything nicely
sort.Slice(out.List, func(i, j int) bool {
- return strings.ToLower(out.List[i].GitlabOrganizationName) < strings.ToLower(out.List[j].GitlabOrganizationName)
+ return strings.ToLower(out.List[i].OrganizationName) < strings.ToLower(out.List[j].OrganizationName)
})
for _, orgList := range out.List {
sort.Slice(orgList.Repositories, func(i, j int) bool {
@@ -318,7 +321,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
}
for _, g := range groups {
if g.Name == gitLabOrgModel.OrganizationName {
- return s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted)
+ return s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
}
}
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index ed6d4d5b5..f300a62d8 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -32,7 +32,7 @@ func (s *Service) GitLabAddRepository(ctx context.Context, projectSFID string, i
}
// GitLabAddRepositoriesByApp adds the GitLab repositories based on the application credentials
-func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *v2GitLabOrg.GitlabOrganization) ([]*v2Models.GitlabRepository, error) {
+func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *v2GitLabOrg.GitLabOrganization) ([]*v2Models.GitlabRepository, error) {
f := logrus.Fields{
"functionName": "v2.repositories.gitlab_services.GitLabAddRepositoriesByApp",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index a4b8bda69..bf4619c2e 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -56,7 +56,7 @@ type ServiceInterface interface {
GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabListRepositories, error)
GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabListRepositories, error)
GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*v2Models.GitlabRepository, error)
- GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitlabOrganization) ([]*v2Models.GitlabRepository, error)
+ GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
GitLabEnableRepository(ctx context.Context, repositoryID string) error
GitLabDisableRepository(ctx context.Context, repositoryID string) error
GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
From 1785bc5f5e92a660600ad13fe0b27b7c8f0160ea Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 17 Aug 2021 18:26:50 -0700
Subject: [PATCH 0429/1276] Updated GitLab Repository Data Models/Responses
(#3166)
---
cla-backend-go/repositories/models.go | 1 +
cla-backend-go/swagger/cla.v2.yaml | 2 +-
.../swagger/common/gitlab-add-repository.yaml | 9 +++++++++
.../common/gitlab-project-organization.yaml | 8 ++++----
.../common/gitlab-project-repository.yaml | 9 +++++++++
.../swagger/common/gitlab-repository-info.yaml | 17 ++++++++++++++---
.../swagger/common/gitlab-repository.yaml | 4 ++++
.../v2/gitlab_organizations/service.go | 4 ++--
.../v2/repositories/gitlab_services.go | 4 +++-
cla-backend-go/v2/repositories/repository.go | 2 ++
10 files changed, 49 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/repositories/models.go b/cla-backend-go/repositories/models.go
index 2390e7ce4..30d3b0c46 100644
--- a/cla-backend-go/repositories/models.go
+++ b/cla-backend-go/repositories/models.go
@@ -17,6 +17,7 @@ type RepositoryDBModel struct {
RepositoryExternalID string `dynamodbav:"repository_external_id" json:"repository_external_id,omitempty"` // Integer value from GitHub
RepositoryID string `dynamodbav:"repository_id" json:"repository_id,omitempty"`
RepositoryName string `dynamodbav:"repository_name" json:"repository_name,omitempty"`
+ RepositoryFullPath string `dynamodbav:"repository_full_path" json:"repository_full_path,omitempty"`
RepositoryOrganizationName string `dynamodbav:"repository_organization_name" json:"repository_organization_name,omitempty"`
RepositoryCLAGroupID string `dynamodbav:"repository_project_id" json:"repository_project_id,omitempty"`
RepositorySfdcID string `dynamodbav:"repository_sfdc_id" json:"repository_sfdc_id,omitempty"`
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index d8f01d922..b448ba864 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -4455,7 +4455,7 @@ definitions:
$ref: './common/gitlab-organization.yaml'
gitlab-repository:
- $ref: './common/github-repository.yaml'
+ $ref: './common/gitlab-repository.yaml'
gitlab-create-organization:
$ref: './common/gitlab-create-organization.yaml'
diff --git a/cla-backend-go/swagger/common/gitlab-add-repository.yaml b/cla-backend-go/swagger/common/gitlab-add-repository.yaml
index 7e908ead9..f3a2d115d 100644
--- a/cla-backend-go/swagger/common/gitlab-add-repository.yaml
+++ b/cla-backend-go/swagger/common/gitlab-add-repository.yaml
@@ -7,6 +7,7 @@ required:
- repository_project_sfid
- repository_cla_group_id
- repository_name
+ - repository_full_path
- repository_organization_name
- repository_url
properties:
@@ -24,14 +25,22 @@ properties:
repository_name:
type: string
description: The repository name
+ minLength: 3
example: 'easycla-test-repo-4'
+ repository_full_path:
+ type: string
+ description: The repository full path
+ minLength: 3
+ example: 'linuxfoundation/product/easycla/easycla-test-repo-4'
repository_organization_name:
type: string
description: The organization name associated with this repository
+ minLength: 3
example: 'The Linux Foundation/product/EasyCLA'
repository_url:
type: string
description: The external repository URL
+ minLength: 8
example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
enabled:
type: boolean
diff --git a/cla-backend-go/swagger/common/gitlab-project-organization.yaml b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
index f1062c3c1..badacff1b 100644
--- a/cla-backend-go/swagger/common/gitlab-project-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
@@ -8,17 +8,17 @@ properties:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
x-omitempty: false
- autoEnableCLAGroupID:
+ auto_enable_cla_group_id:
type: string
description: The CLA Group ID which is attached to the auto-enabled flag
- autoEnabledCLAGroupName:
+ auto_enabled_cla_group_name:
type: string
description: The CLA Group name which is attached to the auto-enabled flag
- branchProtectionEnabled:
+ branch_protection_enabled:
type: boolean
description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
x-omitempty: false
- installationURL:
+ installation_url:
type: string
x-nullable: true
format: uri
diff --git a/cla-backend-go/swagger/common/gitlab-project-repository.yaml b/cla-backend-go/swagger/common/gitlab-project-repository.yaml
index ac3ebdee1..d5828ce47 100644
--- a/cla-backend-go/swagger/common/gitlab-project-repository.yaml
+++ b/cla-backend-go/swagger/common/gitlab-project-repository.yaml
@@ -16,10 +16,19 @@ properties:
repository_name:
type: string
description: 'GitLab Repository/Project name'
+ example: 'easycla-test-repo-4'
+ x-omitempty: false
+ repository_full_path:
+ type: string
+ description: The repository full path
+ example: 'linuxfoundation/product/easycla/easycla-test-repo-4'
+ minLength: 3
x-omitempty: false
repository_url:
type: string
description: 'GitLab Repository/Project URL'
+ minLength: 8
+ example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
x-omitempty: false
cla_group_id:
description: CLA Group ID
diff --git a/cla-backend-go/swagger/common/gitlab-repository-info.yaml b/cla-backend-go/swagger/common/gitlab-repository-info.yaml
index b88c4d414..3bf9699b1 100644
--- a/cla-backend-go/swagger/common/gitlab-repository-info.yaml
+++ b/cla-backend-go/swagger/common/gitlab-repository-info.yaml
@@ -5,13 +5,24 @@ type: object
properties:
repository_gitlab_id:
type: integer
- example: 250509778
+ description: 'Repository GitLab ID value'
+ minimum: 1
+ example: 2292
repository_name:
type: string
- example: "communitybridge/easycla"
+ description: 'GitLab Repository/Project name'
+ example: 'easycla-test-repo-4'
+ minLength: 3
repository_type:
type: string
example: "github"
repository_url:
type: string
- example: "https://github.com/communitybridge/easycla"
+ description: 'GitLab Repository/Project URL'
+ minLength: 8
+ example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
+ repository_full_path:
+ type: string
+ description: The repository full path
+ example: 'linuxfoundation/product/easycla/easycla-test-repo-4'
+ minLength: 3
diff --git a/cla-backend-go/swagger/common/gitlab-repository.yaml b/cla-backend-go/swagger/common/gitlab-repository.yaml
index 6af477008..8628be883 100644
--- a/cla-backend-go/swagger/common/gitlab-repository.yaml
+++ b/cla-backend-go/swagger/common/gitlab-repository.yaml
@@ -21,6 +21,10 @@ properties:
type: string
description: The repository name
example: 'easycla-test-repo-4'
+ repository_full_path:
+ type: string
+ description: The repository full path
+ example: 'linuxfoundation/product/easycla/easycla-test-repo-4'
repository_organization_name:
type: string
description: The organization name associated with this repository
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 821340811..fb0da1513 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -201,8 +201,8 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
rorg := &models.GitlabProjectOrganization{
AutoEnabled: org.AutoEnabled,
- AutoEnableCLAGroupID: org.AutoEnabledClaGroupID,
- AutoEnabledCLAGroupName: strings.TrimSpace(autoEnabledCLAGroupName),
+ AutoEnableClaGroupID: org.AutoEnabledClaGroupID,
+ AutoEnabledClaGroupName: strings.TrimSpace(autoEnabledCLAGroupName),
OrganizationName: org.OrganizationName,
OrganizationURL: org.OrganizationURL,
OrganizationFullPath: org.OrganizationFullPath,
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index f300a62d8..56b810e86 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -100,7 +100,8 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
Enabled: false, // default is false
Note: fmt.Sprintf("Added during onboarding of organization: %s", gitLabOrgModel.OrganizationName),
RepositoryExternalID: utils.Int64(int64(proj.ID)),
- RepositoryName: utils.StringRef(proj.PathWithNamespace),
+ RepositoryName: utils.StringRef(proj.Name),
+ RepositoryFullPath: utils.StringRef(proj.PathWithNamespace),
RepositoryOrganizationName: utils.StringRef(gitLabOrgModel.OrganizationName),
RepositoryProjectSfid: utils.StringRef(gitLabOrgModel.ProjectSFID),
RepositoryURL: utils.StringRef(proj.WebURL),
@@ -225,6 +226,7 @@ func dbModelToGitLabRepository(dbModel *repoModels.RepositoryDBModel) (*v2Models
RepositoryClaGroupID: dbModel.RepositoryCLAGroupID, // CLA Group ID
RepositoryExternalID: gitLabExternalID, // GitLab unique gitV1Repository ID
RepositoryName: dbModel.RepositoryName, // Short repository name
+ RepositoryFullPath: dbModel.RepositoryFullPath, // Full repository path
RepositoryOrganizationName: dbModel.RepositoryOrganizationName, // Group/Organization name
RepositoryURL: dbModel.RepositoryURL, // full url
RepositoryType: dbModel.RepositoryType, // gitlab
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index 6db94ad38..f21a107d3 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -233,6 +233,7 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
"repositoryExternalID": utils.Int64Value(input.RepositoryExternalID),
"repositoryURL": utils.StringValue(input.RepositoryURL),
"repositoryName": utils.StringValue(input.RepositoryName),
+ "repositoryFullPath": utils.StringValue(input.RepositoryFullPath),
"repositoryType": utils.GitLabLower,
"repositoryCLAGroupID": utils.StringValue(input.RepositoryClaGroupID),
"repositoryProjectSFID": utils.StringValue(input.RepositoryProjectSfid),
@@ -271,6 +272,7 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
DateModified: currentTime,
RepositoryExternalID: repositoryExternalIDString,
RepositoryName: utils.StringValue(input.RepositoryName),
+ RepositoryFullPath: utils.StringValue(input.RepositoryFullPath),
RepositoryURL: utils.StringValue(input.RepositoryURL),
RepositoryOrganizationName: utils.StringValue(input.RepositoryOrganizationName), // gitlab group/organization
RepositoryCLAGroupID: utils.StringValue(input.RepositoryClaGroupID),
From 0e5867a45fa7a2a886aecdc02093b6329bdd6303 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 17 Aug 2021 18:48:37 -0700
Subject: [PATCH 0430/1276] Added Repository Full Path (#3167)
---
cla-backend-go/v2/gitlab_organizations/service.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index fb0da1513..0e7717498 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -410,6 +410,7 @@ func toGitLabProjectResponse(gitLabListRepos *models.GitlabListRepositories) []*
RepositoryGitlabID: repo.RepositoryExternalID,
RepositoryID: repo.RepositoryID,
RepositoryName: repo.RepositoryName,
+ RepositoryFullPath: repo.RepositoryFullPath,
RepositoryURL: repo.RepositoryURL,
})
}
From 19061e9d46e5e43cd09449afcca21a4b554c8d87 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 18 Aug 2021 17:10:25 +0300
Subject: [PATCH 0431/1276] gitlab repository webhook setup (#3168)
---
.../cmd/dynamo_events_lambda/main.go | 9 +-
cla-backend-go/gitlab/repository.go | 4 -
cla-backend-go/gitlab/webhook.go | 81 +++++++
.../v2/dynamo_events/gitlab_webhooks.go | 212 ++++++++++++++++++
cla-backend-go/v2/dynamo_events/service.go | 19 +-
cla-backend-go/v2/gitlab-activity/service.go | 14 +-
.../v2/gitlab_organizations/repository.go | 16 +-
.../v2/gitlab_organizations/service.go | 18 ++
8 files changed, 349 insertions(+), 24 deletions(-)
delete mode 100644 cla-backend-go/gitlab/repository.go
create mode 100644 cla-backend-go/gitlab/webhook.go
create mode 100644 cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index ceb81722d..3b3b8a3c5 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -8,6 +8,8 @@ import (
"encoding/json"
"os"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
@@ -90,11 +92,12 @@ func init() {
claManagerRequestsRepo := cla_manager.NewRepository(awsSession, stage)
approvalListRequestsRepo := approval_list.NewRepository(awsSession, stage)
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
+ gitlabOrganizationRepo := gitlab_organizations.NewRepository(awsSession, stage)
token.Init(configFile.Auth0Platform.ClientID, configFile.Auth0Platform.ClientSecret, configFile.Auth0Platform.URL, configFile.Auth0Platform.Audience)
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- _ = gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
+ gitlabApp := gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
user_service.InitClient(configFile.APIGatewayURL, configFile.AcsAPIKey)
project_service.InitClient(configFile.APIGatewayURL)
@@ -138,12 +141,14 @@ func init() {
projectClaGroupRepo,
eventsRepo,
projectRepo,
+ gitlabOrganizationRepo,
projectService,
githubOrganizationsService,
repositoriesService,
gerritService,
claManagerRequestsRepo,
- approvalListRequestsRepo)
+ approvalListRequestsRepo,
+ gitlabApp)
}
func handler(ctx context.Context, event events.DynamoDBEvent) {
diff --git a/cla-backend-go/gitlab/repository.go b/cla-backend-go/gitlab/repository.go
deleted file mode 100644
index 90f42708c..000000000
--- a/cla-backend-go/gitlab/repository.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-package gitlab
diff --git a/cla-backend-go/gitlab/webhook.go b/cla-backend-go/gitlab/webhook.go
new file mode 100644
index 000000000..1efd1f0e2
--- /dev/null
+++ b/cla-backend-go/gitlab/webhook.go
@@ -0,0 +1,81 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "fmt"
+
+ "github.com/xanzy/go-gitlab"
+)
+
+// SetWebHook is responsible for adding the webhook for given projectID, if webhook is there already
+// tries to set the attributes if anything is missing, should be idempotent operation
+func SetWebHook(gitLabClient *gitlab.Client, hookURL string, projectID int, token string) error {
+ existingWebHook, err := findExistingWebHook(gitLabClient, hookURL, projectID)
+ if err != nil {
+ return err
+ }
+
+ if existingWebHook == nil {
+ _, _, err = gitLabClient.Projects.AddProjectHook(projectID, &gitlab.AddProjectHookOptions{
+ URL: gitlab.String(hookURL),
+ MergeRequestsEvents: gitlab.Bool(true),
+ PushEvents: gitlab.Bool(true),
+ EnableSSLVerification: gitlab.Bool(true),
+ Token: gitlab.String(token),
+ })
+ if err != nil {
+ return fmt.Errorf("adding web hook for project : %d, failed : %v", projectID, err)
+ }
+ return nil
+ }
+
+ if !existingWebHook.EnableSSLVerification || !existingWebHook.MergeRequestsEvents || !existingWebHook.PushEvents {
+ _, _, err = gitLabClient.Projects.EditProjectHook(projectID, existingWebHook.ID, &gitlab.EditProjectHookOptions{
+ URL: gitlab.String(hookURL),
+ MergeRequestsEvents: gitlab.Bool(true),
+ PushEvents: gitlab.Bool(true),
+ EnableSSLVerification: gitlab.Bool(true),
+ Token: gitlab.String(token),
+ })
+ if err != nil {
+ return fmt.Errorf("editing web hook for project : %d, failed : %v", projectID, err)
+ }
+ }
+
+ return nil
+}
+
+// RemoveWebHook removes existing webhook from the given project
+func RemoveWebHook(gitLabClient *gitlab.Client, hookURL string, projectID int) error {
+ existingWebHook, err := findExistingWebHook(gitLabClient, hookURL, projectID)
+ if err != nil {
+ return err
+ }
+
+ if existingWebHook == nil {
+ return nil
+ }
+
+ _, err = gitLabClient.Projects.DeleteProjectHook(projectID, existingWebHook.ID)
+ return err
+
+}
+
+func findExistingWebHook(gitLabClient *gitlab.Client, hookURL string, projectID int) (*gitlab.ProjectHook, error) {
+ hooks, _, err := gitLabClient.Projects.ListProjectHooks(projectID, &gitlab.ListProjectHooksOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("fetching hooks for project : %d, failed : %v", projectID, err)
+ }
+
+ var existingWebHook *gitlab.ProjectHook
+ for _, hook := range hooks {
+ if hook.URL == hookURL {
+ existingWebHook = hook
+ break
+ }
+ }
+
+ return existingWebHook, nil
+}
diff --git a/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
new file mode 100644
index 000000000..12e5276be
--- /dev/null
+++ b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
@@ -0,0 +1,212 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package dynamo_events
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/aws/aws-lambda-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+ "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+)
+
+func (s *service) GitLabRepoAddedWebhookEventHandler(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
+ f := logrus.Fields{
+ "functionName": "GitLabRepoAddedWebhookEventHandler",
+ "eventID": event.EventID,
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ var newRepoModel repositories.RepositoryDBModel
+
+ log.WithFields(f).Debugf("processing record %s event...", event.EventName)
+ err := unmarshalStreamImage(event.Change.NewImage, &newRepoModel)
+ if err != nil {
+ log.WithFields(f).Warnf("problem unmarshalling the new repository model event, error: %+v", err)
+ return err
+ }
+
+ if !s.isGitlabRepo(log.WithFields(f), &newRepoModel) {
+ return nil
+ }
+
+ if !newRepoModel.Enabled {
+ log.WithFields(f).Debugf("gitlab repo is not enabled, nothing to do at this point")
+ return nil
+ }
+
+ repositoryID := newRepoModel.RepositoryID
+ repositoryName := newRepoModel.RepositoryName
+ repositoryExternalID := newRepoModel.RepositoryExternalID
+
+ log.WithFields(f).Debugf("adding webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
+
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, newRepoModel.RepositoryOrganizationName)
+ if err != nil {
+ return fmt.Errorf("fetching gitlab org : %s failed : %v", newRepoModel.RepositoryOrganizationName, err)
+ }
+
+ gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ if err != nil {
+ return fmt.Errorf("initializing GitLab client failed : %v", err)
+ }
+
+ repositoryExternalIDInt, err := strconv.Atoi(repositoryExternalID)
+ if err != nil {
+ return fmt.Errorf("parding external repository id failed : %v", err)
+ }
+
+ conf := config.GetConfig()
+ if err := gitlab.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
+ log.WithFields(f).Errorf("adding gitlab webhook failed : %v", err)
+ }
+
+ log.WithFields(f).Debugf("gitlab webhhok added succesfully for repository")
+ return nil
+}
+
+func (s *service) GitlabRepoModifiedWebhookEventHandler(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
+ f := logrus.Fields{
+ "functionName": "GitlabRepoModifiedWebhookEventHandler",
+ "eventID": event.EventID,
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ var newRepoModel repositories.RepositoryDBModel
+ var oldRepoModel repositories.RepositoryDBModel
+
+ log.WithFields(f).Debugf("processing record %s event...", event.EventName)
+ err := unmarshalStreamImage(event.Change.OldImage, &oldRepoModel)
+ if err != nil {
+ log.WithFields(f).Warnf("problem unmarshalling the new repository model event, error: %+v", err)
+ return err
+ }
+
+ err = unmarshalStreamImage(event.Change.NewImage, &newRepoModel)
+ if err != nil {
+ log.WithFields(f).Warnf("problem unmarshalling the old repository model event, error: %+v", err)
+ return err
+ }
+
+ if !s.isGitlabRepo(log.WithFields(f), &newRepoModel) {
+ return nil
+ }
+
+ if newRepoModel.Enabled == oldRepoModel.Enabled {
+ log.WithFields(f).Debugf("only changes of Enabled field are processed")
+ return nil
+ }
+
+ repositoryID := oldRepoModel.RepositoryID
+ repositoryName := oldRepoModel.RepositoryName
+ repositoryExternalID := oldRepoModel.RepositoryExternalID
+
+ if newRepoModel.Enabled {
+ log.WithFields(f).Debugf("adding webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
+ } else {
+ log.WithFields(f).Debugf("removing webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
+ }
+
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
+ if err != nil {
+ return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
+ }
+
+ gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ if err != nil {
+ return fmt.Errorf("initializing GitLab client failed : %v", err)
+ }
+
+ repositoryExternalIDInt, err := strconv.Atoi(repositoryExternalID)
+ if err != nil {
+ return fmt.Errorf("parding external repository id failed : %v", err)
+ }
+
+ conf := config.GetConfig()
+
+ if newRepoModel.Enabled {
+ if err := gitlab.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
+ log.WithFields(f).Errorf("adding gitlab webhook failed : %v", err)
+ }
+ } else {
+ if err := gitlab.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
+ log.WithFields(f).Errorf("removing gitlab webhook failed : %v", err)
+ }
+ }
+
+ log.WithFields(f).Debugf("gitlab webhhok processed succesfully for repository")
+ return nil
+}
+
+func (s *service) GitLabRepoRemovedWebhookEventHandler(event events.DynamoDBEventRecord) error {
+ ctx := utils.NewContext()
+ f := logrus.Fields{
+ "functionName": "GitLabRepoRemovedWebhookEventHandler",
+ "eventID": event.EventID,
+ "eventName": event.EventName,
+ "eventSource": event.EventSource,
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ var oldRepoModel repositories.RepositoryDBModel
+
+ log.WithFields(f).Debugf("processing record %s event...", event.EventName)
+ err := unmarshalStreamImage(event.Change.OldImage, &oldRepoModel)
+ if err != nil {
+ log.WithFields(f).Warnf("problem unmarshalling the old repository model event, error: %+v", err)
+ return err
+ }
+
+ if !s.isGitlabRepo(log.WithFields(f), &oldRepoModel) {
+ return nil
+ }
+
+ repositoryID := oldRepoModel.RepositoryID
+ repositoryName := oldRepoModel.RepositoryName
+ repositoryExternalID := oldRepoModel.RepositoryExternalID
+
+ log.WithFields(f).Debugf("removing webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
+
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
+ if err != nil {
+ return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
+ }
+
+ gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ if err != nil {
+ return fmt.Errorf("initializing GitLab client failed : %v", err)
+ }
+
+ repositoryExternalIDInt, err := strconv.Atoi(repositoryExternalID)
+ if err != nil {
+ return fmt.Errorf("parding external repository id failed : %v", err)
+ }
+
+ conf := config.GetConfig()
+ if err := gitlab.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
+ log.WithFields(f).Errorf("removing gitlab webhook failed : %v", err)
+ }
+
+ log.WithFields(f).Debugf("gitlab webhhok removed succesfully for repository")
+ return nil
+}
+
+func (s *service) isGitlabRepo(logEntry *logrus.Entry, repoModel *repositories.RepositoryDBModel) bool {
+ if repoModel.RepositoryType != utils.GitLabLower {
+ logEntry.Debugf("only processing gitlab instances")
+ return false
+ }
+ return true
+}
diff --git a/cla-backend-go/v2/dynamo_events/service.go b/cla-backend-go/v2/dynamo_events/service.go
index 896167d40..83a27c194 100644
--- a/cla-backend-go/v2/dynamo_events/service.go
+++ b/cla-backend-go/v2/dynamo_events/service.go
@@ -11,6 +11,9 @@ import (
"strings"
"sync"
+ gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+
"github.com/communitybridge/easycla/cla-backend-go/gerrits"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
@@ -56,6 +59,7 @@ type service struct {
companyService v2Company.Service
projectsClaGroupRepo projects_cla_groups.Repository
eventsRepo claevent.Repository
+ gitLabOrgRepo gitlab_organizations.RepositoryInterface
projectRepo project.ProjectRepository
projectService project.Service
githubOrgService github_organizations.ServiceInterface
@@ -64,6 +68,7 @@ type service struct {
autoEnableService *autoEnableServiceProvider
claManagerRequestsRepo cla_manager.IRepository
approvalListRequestsRepo approval_list.IRepository
+ gitLabApp *gitlab2.App
}
// Service implements DynamoDB stream event handler service
@@ -79,12 +84,14 @@ func NewService(stage string,
pcgRepo projects_cla_groups.Repository,
eventsRepo claevent.Repository,
projectRepo project.ProjectRepository,
+ gitLabOrgRepo gitlab_organizations.RepositoryInterface,
projService project.Service,
githubOrgService github_organizations.ServiceInterface,
repositoryService repositories.Service,
gerritService gerrits.Service,
claManagerRequestsRepo cla_manager.IRepository,
- approvalListRequestsRepo approval_list.IRepository) Service {
+ approvalListRequestsRepo approval_list.IRepository,
+ gitLabApp *gitlab2.App) Service {
signaturesTable := fmt.Sprintf("cla-%s-signatures", stage)
eventsTable := fmt.Sprintf("cla-%s-events", stage)
@@ -102,6 +109,7 @@ func NewService(stage string,
projectsClaGroupRepo: pcgRepo,
eventsRepo: eventsRepo,
projectRepo: projectRepo,
+ gitLabOrgRepo: gitLabOrgRepo,
projectService: projService,
githubOrgService: githubOrgService,
repositoryService: repositoryService,
@@ -109,6 +117,7 @@ func NewService(stage string,
autoEnableService: &autoEnableServiceProvider{repositoryService: repositoryService},
claManagerRequestsRepo: claManagerRequestsRepo,
approvalListRequestsRepo: approvalListRequestsRepo,
+ gitLabApp: gitLabApp,
}
s.registerCallback(signaturesTable, Modify, s.SignatureSignedEvent)
@@ -140,6 +149,10 @@ func NewService(stage string,
s.registerCallback(repositoryTableName, Modify, s.GithubRepoModifyAddEvent)
s.registerCallback(repositoryTableName, Remove, s.GithubRepoModifyAddEvent)
+ s.registerCallback(repositoryTableName, Insert, s.GitLabRepoAddedWebhookEventHandler)
+ s.registerCallback(repositoryTableName, Modify, s.GitlabRepoModifiedWebhookEventHandler)
+ s.registerCallback(repositoryTableName, Remove, s.GitLabRepoRemovedWebhookEventHandler)
+
// Check and enable/disable the branch protection when a project
s.registerCallback(repositoryTableName, Insert, s.EnableBranchProtectionServiceHandler)
s.registerCallback(repositoryTableName, Remove, s.DisableBranchProtectionServiceHandler)
@@ -168,9 +181,7 @@ func (s *service) ProcessEvents(dynamoDBEvents events.DynamoDBEvent) {
// Dumping the event is super verbose
// "event": event,
}
- // Generates a ton of output
- // b, _ := json.Marshal(events) // nolint
- //fields["events_data"] = string(b)
+
log.WithFields(fields).Debug("processing event record")
key := fmt.Sprintf("%s:%s", tableName, event.EventName)
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 8949da8a5..222653083 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -113,7 +113,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
}
// try to find the repository via the external id
- gitlabRepo, err := s.getGitlabRepoByName(ctx, repositoryPath)
+ gitlabRepo, err := s.getGitlabRepoByName(ctx, repositoryName)
if err != nil {
return fmt.Errorf("finding internal repository for gitlab org name failed : %v", err)
}
@@ -253,18 +253,18 @@ func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeE
parts := strings.Split(repositoryPath, "/")
organizationName := parts[0]
- gitlabOrgs, err := s.gitlabRepository.GetGitlabOrganizationByName(ctx, organizationName)
- if err != nil || gitlabOrgs == nil {
+ gitlabOrg, err := s.gitlabRepository.GetGitlabOrganizationByName(ctx, organizationName)
+ if err != nil || gitlabOrg == nil {
// try getting it with project name as well
- gitlabOrgs, err = s.gitlabRepository.GetGitlabOrganizationByName(ctx, mergeEvent.Project.Namespace)
- if err != nil || gitlabOrgs == nil {
+ gitlabOrg, err = s.gitlabRepository.GetGitlabOrganizationByName(ctx, mergeEvent.Project.Namespace)
+ if err != nil || gitlabOrg == nil {
return nil, fmt.Errorf("gitlab org : %s doesn't exist : %v", organizationName, err)
}
}
- gitlabOrg, err := s.gitlabRepository.GetGitlabOrganization(ctx, gitlabOrgs.OrganizationID)
+ gitlabOrg, err = s.gitlabRepository.GetGitlabOrganization(ctx, gitlabOrg.OrganizationID)
if err != nil {
- return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", gitlabOrgs.OrganizationID, err)
+ return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", gitlabOrg.OrganizationID, err)
}
return gitlabOrg, nil
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index d848f6bf8..09dc1ac1b 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -38,7 +38,7 @@ type RepositoryInterface interface {
AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models2.GitlabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
@@ -81,7 +81,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
if existingRecord != nil {
log.WithFields(f).Debugf("An existing GitLab organization with name %s exists in our database", gitLabOrganizationName)
// If everything matches...
- if projectSFID == existingRecord.ProjectSfid {
+ if projectSFID == existingRecord.ProjectSFID {
log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
enabledFlag := true
updateErr := repo.UpdateGitlabOrganization(ctx, projectSFID, gitLabOrganizationName,
@@ -91,7 +91,11 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
}
// Return the updated record
- return repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName)
+ if gitlabOrg, err := repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName); err != nil {
+ return nil, err
+ } else {
+ return common.ToModel(gitlabOrg), nil
+ }
}
log.WithFields(f).Debug("Existing GitLab organization with different project SFID - won't be able to update it - will return conflict")
@@ -215,7 +219,7 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
}
// GetGitlabOrganizationByName get GitLab organization by name
-func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models2.GitlabOrganization, error) {
+func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -260,9 +264,7 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOr
return nil, err
}
- log.WithFields(f).Debug("building response model...")
- gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
- return gitlabOrgList[0], nil
+ return resultOutput[0], nil
}
// GetGitlabOrganization by organization name
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 0e7717498..387f5a879 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -37,6 +37,7 @@ type ServiceInterface interface {
AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, gitlabOrganizationName string) (*models.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
@@ -132,6 +133,23 @@ func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizat
return dbModel, nil
}
+func (s *Service) GetGitlabOrganizationByName(ctx context.Context, gitlabOrganizationName string) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitlabOrganizationID": gitlabOrganizationName,
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationName)
+ dbModel, err := s.repo.GetGitlabOrganizationByName(ctx, gitlabOrganizationName)
+ if err != nil {
+ return nil, err
+ }
+
+ return common.ToModel(dbModel), nil
+
+}
+
// GetGitlabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
From 9167d57f20cc77f10c7028d82df0f5cb97d7b2a5 Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 18 Aug 2021 18:34:43 +0300
Subject: [PATCH 0432/1276] Feature/Gitlab Redirect Contributor console (#3164)
Co-authored-by: Harold Wanyama
---
cla-backend-go/cmd/server.go | 7 +
cla-backend-go/config/config.go | 2 +
cla-backend-go/config/ssm.go | 3 +
cla-backend-go/swagger/cla.v2.yaml | 40 ++++-
cla-backend-go/v2/gitlab_sign/handlers.go | 47 ++++++
cla-backend-go/v2/gitlab_sign/service.go | 190 ++++++++++++++++++++++
cla-backend-go/v2/store/repository.go | 82 ++++++++++
7 files changed, 370 insertions(+), 1 deletion(-)
create mode 100644 cla-backend-go/v2/gitlab_sign/handlers.go
create mode 100644 cla-backend-go/v2/gitlab_sign/service.go
create mode 100644 cla-backend-go/v2/store/repository.go
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 47a3fb09c..a6888892d 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -21,6 +21,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_sign"
"github.com/communitybridge/easycla/cla-backend-go/emails"
@@ -92,6 +93,7 @@ import (
v2ClaManager "github.com/communitybridge/easycla/cla-backend-go/v2/cla_manager"
v2Company "github.com/communitybridge/easycla/cla-backend-go/v2/company"
v2Health "github.com/communitybridge/easycla/cla-backend-go/v2/health"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/store"
v2Template "github.com/communitybridge/easycla/cla-backend-go/v2/template"
"github.com/go-openapi/loads"
@@ -255,6 +257,7 @@ func server(localMode bool) http.Handler {
githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
gitlabOrganizationRepo := gitlab_organizations.NewRepository(awsSession, stage)
claManagerReqRepo := cla_manager.NewRepository(awsSession, stage)
+ storeRepository := store.NewRepository(awsSession, stage)
// Our service layer handlers
eventsService := events.NewService(eventsRepo, combinedRepo{
@@ -306,6 +309,7 @@ func server(localMode bool) http.Handler {
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, gitV1Repository, v1ProjectClaGroupRepo)
gitlabOrganizationsService := gitlab_organizations.NewService(gitlabOrganizationRepo, v2RepositoriesService, v1ProjectClaGroupRepo)
gitlabActivityService := gitlab_activity.NewService(gitlabOrganizationRepo, gitV1Repository, gitV2Repository, usersRepo, signaturesRepo, v1ProjectClaGroupRepo, v1CompanyRepo, signaturesRepo)
+ gitlabSignService := gitlab_sign.NewService(v2RepositoriesService, gitlabOrganizationRepo, usersService, storeRepository)
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, gitV1Repository, v1ProjectClaGroupRepo, githubOrganizationsService)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(gitV1Repository, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
@@ -345,6 +349,9 @@ func server(localMode bool) http.Handler {
v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
+ gitlab_organizations.Configure(v2API, gitlabOrganizationsService, v2RepositoriesService, eventsService)
+ gitlab_sign.Configure(v2API, gitlabSignService, eventsService, configFile.CLAContributorv2Base)
+ gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
gitlab_organizations.Configure(v2API, gitlabOrganizationsService, v2RepositoriesService, eventsService)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 47407cf23..42e55caec 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -67,6 +67,8 @@ type Config struct {
CorporateConsoleV1URL string `json:"corporateConsoleV1URL"`
CorporateConsoleV2URL string `json:"corporateConsoleV2URL"`
+ CLAContributorv2Base string `json:"cla-contributor-v2-base"`
+
// SNSEventTopic the topic ARN for events
SNSEventTopicARN string `json:"snsEventTopicARN"`
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index b0f80e695..c28129a13 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -78,6 +78,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-corporate-base-%s", stage),
fmt.Sprintf("cla-corporate-v1-base-%s", stage),
fmt.Sprintf("cla-corporate-v2-base-%s", stage),
+ fmt.Sprintf("cla-contributor-v2-base-%s", stage),
fmt.Sprintf("cla-doc-raptor-api-key-%s", stage),
fmt.Sprintf("cla-session-store-table-%s", stage),
fmt.Sprintf("cla-ses-sender-email-address-%s", stage),
@@ -172,6 +173,8 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.Gitlab.RedirectURI = resp.value
case fmt.Sprintf("cla-gitlab-app-web-hook-uri-%s", stage):
config.Gitlab.WebHookURI = resp.value
+ case fmt.Sprintf("cla-contributor-v2-base-%s", stage):
+ config.CLAContributorv2Base = resp.value
case fmt.Sprintf("cla-corporate-base-%s", stage):
config.CorporateConsoleURL = resp.value
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index b448ba864..3a6dcad53 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -4000,7 +4000,27 @@ paths:
$ref: '#/responses/internal-server-error'
tags:
- gitlab-activity
-
+
+ /repository-provider/gitlab/sign/{organizationID}/{gitlabRepositoryID}/{mergeRequestID}:
+ get:
+ summary: Gitlab sign request handler
+ description: Endpoint that will initiate a CLA Signature for the User
+ security: [ ]
+ operationId: signRequest
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - $ref: "#/parameters/path-gitlabOrganizationID"
+ - $ref: "#/parameters/path-gitlabRepositoryID"
+ - $ref: "#/parameters/path-mergeRequestID"
+ responses:
+ '200':
+ description: 'Success'
+ '400':
+ $ref: '#/responses/invalid-request'
+ '500':
+ $ref: '#/responses/internal-server-error'
+ tags:
+ - gitlab-sign
responses:
unauthorized:
@@ -4276,6 +4296,24 @@ parameters:
pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
minLength: 5
maxLength: 255
+ path-gitlabOrganizationID:
+ name: organizationID
+ description: GitLab organization ID
+ type: string
+ in: path
+ required: true
+ path-mergeRequestID:
+ name: mergeRequestID
+ description: GitLab Merge Request identifier
+ type: string
+ in: path
+ required: true
+ path-gitlabRepositoryID:
+ name: gitlabRepositoryID
+ type: string
+ description: GitLab Repository/Project identifier
+ in: path
+ required: true
gerritHost:
name: gerritHost
description: host of the gerrit server
diff --git a/cla-backend-go/v2/gitlab_sign/handlers.go b/cla-backend-go/v2/gitlab_sign/handlers.go
new file mode 100644
index 000000000..acd2ebeed
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_sign/handlers.go
@@ -0,0 +1,47 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_sign
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_sign"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/sirupsen/logrus"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+)
+
+func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service, contributorConsoleV2Base string) {
+ api.GitlabSignSignRequestHandler = gitlab_sign.SignRequestHandlerFunc(
+ func(srp gitlab_sign.SignRequestParams) middleware.Responder {
+ reqID := utils.GetRequestID(srp.XREQUESTID)
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID)
+
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_sign.handlers.GitlabSignSignRequestHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "installationID": srp.OrganizationID,
+ "repositoryID": srp.GitlabRepositoryID,
+ "mergeRequestID": srp.MergeRequestID,
+ }
+
+ log.WithFields(f).Debugf("Initiating Gitlab sign request for : %+v ", srp)
+
+ err := service.GitlabSignRequest(ctx, srp.HTTPRequest, srp.OrganizationID, srp.GitlabRepositoryID, srp.MergeRequestID, contributorConsoleV2Base, eventService)
+
+ if err != nil {
+ msg := fmt.Sprintf("problem initiating sign request for :%+v", srp)
+ log.WithFields(f).Debugf(msg)
+ return gitlab_sign.NewSignRequestBadRequest().WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ return gitlab_sign.NewSignRequestOK()
+ })
+}
diff --git a/cla-backend-go/v2/gitlab_sign/service.go b/cla-backend-go/v2/gitlab_sign/service.go
new file mode 100644
index 000000000..5edc8e2f1
--- /dev/null
+++ b/cla-backend-go/v2/gitlab_sign/service.go
@@ -0,0 +1,190 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab_sign
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "time"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
+ v2Gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/users"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/store"
+ "github.com/xanzy/go-gitlab"
+)
+
+type service struct {
+ repoService repositories.ServiceInterface
+ gitlabOrgRepo gitlab_organizations.RepositoryInterface
+ userService users.Service
+ gitlabApp v2Gitlab.App
+ storeRepo store.Repository
+}
+
+type Service interface {
+ GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) error
+}
+
+func NewService(gitlabRepositoryService repositories.ServiceInterface, gitlabOrgRepository gitlab_organizations.RepositoryInterface, userService users.Service, storeRepo store.Repository) Service {
+ return &service{
+ repoService: gitlabRepositoryService,
+ gitlabOrgRepo: gitlabOrgRepository,
+ userService: userService,
+ storeRepo: storeRepo,
+ }
+}
+
+func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) error {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_sign.service.GitlabSignRequest",
+ "organizationID": organizationID,
+ "repositoryID": repositoryID,
+ "mergeRequestID": mergeRequestID,
+ }
+
+ organization, err := s.gitlabOrgRepo.GetGitlabOrganization(ctx, organizationID)
+ if err != nil {
+ log.WithFields(f).Debugf("unable to get gitlab organiztion by ID: %s, error: %+v ", organizationID, err)
+ return nil
+ }
+
+ if organization.AuthInfo == "" {
+ msg := fmt.Sprintf("organization: %s has no auth details", organizationID)
+ log.WithFields(f).Debug(msg)
+ return nil
+ }
+ gitlabClient, err := v2Gitlab.NewGitlabOauthClient(organization.AuthInfo, &s.gitlabApp)
+ if err != nil {
+ log.WithFields(f).Debugf("initializaing gitlab client for gitlab org: %s failed: %v", organizationID, err)
+ return nil
+ }
+
+ mergeRequestIDInt, err := strconv.Atoi(mergeRequestID)
+ if err != nil {
+ log.WithFields(f).Debugf("unable to convert organization string value : %s to Int", organizationID)
+ return err
+ }
+
+ log.WithFields(f).Debug("Determining return URL from the inbound request ...")
+ mergeRequest, _, err := gitlabClient.MergeRequests.GetMergeRequest(repositoryID, mergeRequestIDInt, &gitlab.GetMergeRequestsOptions{})
+ if err != nil || mergeRequest == nil {
+ log.WithFields(f).Debugf("unable to fetch MR Web URL: mergeRequestID: %s ", mergeRequestID)
+ return err
+ }
+
+ originURL := mergeRequest.WebURL
+ log.WithFields(f).Debugf("Return URL from the inbound request is : %s ", originURL)
+
+ err = s.redirectToConsole(ctx, req, gitlabClient, repositoryID, mergeRequestID, originURL, contributorConsoleV2Base, eventService)
+ if err != nil {
+ log.WithFields(f).Debug("unable to redirect to contributor console")
+ return err
+ }
+
+ return nil
+}
+
+func (s service) redirectToConsole(ctx context.Context, req *http.Request, gitlabClient *gitlab.Client, repositoryID, mergeRequestID, originURL, contributorBaseURL string, eventService events.Service) error {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_sign.service.redirectToConsole",
+ "repositoryID": repositoryID,
+ "mergeRequestID": mergeRequestID,
+ "originURL": originURL,
+ }
+
+ claUser, err := s.getOrCreateUser(ctx, gitlabClient, eventService)
+ if err != nil {
+ msg := fmt.Sprintf("unable to get or create user : %+v ", err)
+ log.WithFields(f).Warn(msg)
+ return err
+ }
+
+ gitlabRepo, err := s.repoService.GitHubGetRepository(ctx, repositoryID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to find repository by ID: %s , error: %+v ", repositoryID, err)
+ log.WithFields(f).Warn(msg)
+ return err
+ }
+
+ // set active signature metadata to track the user signing process
+ key := fmt.Sprintf("active_signature:%s", claUser.UserID)
+ var value map[string]string
+ value["user_id"] = claUser.UserID
+ value["project_id"] = gitlabRepo.RepositoryClaGroupID
+ value["repository_id"] = repositoryID
+ value["merge_request_id"] = mergeRequestID
+ expire := time.Now().AddDate(0, 0, 1).Unix()
+
+ jsonVal, _ := json.Marshal(value)
+
+ err = s.storeRepo.SetActiveSignatureMetaData(ctx, key, expire, jsonVal)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to save signature metadata")
+ }
+
+ params := "redirect=" + url.QueryEscape(originURL)
+ consoleURL := fmt.Sprintf("https://%s/#/cla/project/%s/user/%s?%s", contributorBaseURL, gitlabRepo.RepositoryClaGroupID, claUser.UserID, params)
+ _, err = http.Get(consoleURL)
+
+ if err != nil {
+ msg := fmt.Sprintf("unable to redirect to : %s , error: %+v ", consoleURL, err)
+ log.WithFields(f).Warn(msg)
+ return err
+ }
+
+ return nil
+}
+
+func (s service) getOrCreateUser(ctx context.Context, gitlabClient *gitlab.Client, eventsService events.Service) (*models.User, error) {
+
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_sign.service.getOrCreateUser",
+ }
+
+ gitlabUser, _, err := gitlabClient.Users.CurrentUser()
+ if err != nil {
+ log.WithFields(f).Debugf("getting gitlab current user for failed : %v ", err)
+ return nil, err
+ }
+
+ claUser, err := s.userService.GetUserByGitlabID(gitlabUser.ID)
+ if err != nil {
+ log.WithFields(f).Debugf("unable to get CLA user by github ID: %d , error: %+v ", gitlabUser.ID, err)
+ log.WithFields(f).Infof("creating user record for gitlab user : %+v ", gitlabUser)
+ user := &models.User{
+ GitlabID: fmt.Sprintf("%d", gitlabUser.ID),
+ GitlabUsername: gitlabUser.Username,
+ Emails: []string{gitlabUser.Email},
+ }
+ claUser, userErr := s.userService.CreateUser(user, nil)
+ if err != nil {
+ log.WithFields(f).Debugf("unable to create claUser with details : %+v, error: %+v", user, userErr)
+ return nil, userErr
+ }
+
+ // Log the event
+ eventsService.LogEvent(&events.LogEventArgs{
+ EventType: events.UserCreated,
+ UserID: user.UserID,
+ UserModel: user,
+ EventData: &events.UserCreatedEventData{},
+ })
+ return claUser, nil
+
+ }
+
+ return claUser, nil
+
+}
diff --git a/cla-backend-go/v2/store/repository.go b/cla-backend-go/v2/store/repository.go
new file mode 100644
index 000000000..64785faad
--- /dev/null
+++ b/cla-backend-go/v2/store/repository.go
@@ -0,0 +1,82 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package store
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/dynamodb"
+ "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+)
+
+//DBStore represents DB Model for the store table
+type DBStore struct {
+ Key string
+ Value []byte
+ Expire int64
+}
+
+// Repository interface
+type Repository interface {
+ SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value []byte) error
+}
+
+type repo struct {
+ stage string
+ dynamoDBClient *dynamodb.DynamoDB
+ storeTableName string
+}
+
+//NewRepository initiates Store repository instance
+func NewRepository(awsSession *session.Session, stage string) Repository {
+ return repo{
+ stage: stage,
+ dynamoDBClient: dynamodb.New(awsSession),
+ storeTableName: fmt.Sprintf("cla-%s-store", stage),
+ }
+}
+
+// SetActiveSignatureMetaData sets active signature meta data
+func (r repo) SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value []byte) error {
+ f := logrus.Fields{
+ "functionName": "v2.store.repository.SetActiveSignatureMetaData",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "key": key,
+ "value": value,
+ "expire": expire,
+ }
+
+ store := DBStore{
+ Key: key,
+ Value: value,
+ Expire: expire,
+ }
+
+ v, err := dynamodbattribute.MarshalMap(store)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem marshalling store record")
+ return err
+ }
+
+ _, err = r.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
+ Item: v,
+ TableName: &r.storeTableName,
+ })
+
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to save store record")
+ return err
+ }
+
+ log.WithFields(f).Debugf("Signature meta record data saved: %+v ", store)
+
+ return nil
+}
From 2c34f4a4888d94118b78e14bf415f192075004a4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 18 Aug 2021 17:24:59 -0700
Subject: [PATCH 0433/1276] Updated GitLab Add Repo to CLA Group API (#3169)
---
.../cmd/dynamo_events_lambda/main.go | 2 +-
cla-backend-go/cmd/server.go | 16 +-
cla-backend-go/gitlab/organization.go | 78 --------
cla-backend-go/{gitlab => gitlab_api}/auth.go | 0
.../{gitlab => gitlab_api}/client.go | 10 +-
cla-backend-go/gitlab_api/client_groups.go | 139 ++++++++++++++
cla-backend-go/gitlab_api/client_projects.go | 114 ++++++++++++
.../{gitlab => gitlab_api}/client_test.go | 0
cla-backend-go/{gitlab => gitlab_api}/init.go | 0
cla-backend-go/{gitlab => gitlab_api}/mr.go | 0
.../{gitlab => gitlab_api}/webhook.go | 0
cla-backend-go/swagger/cla.v1.yaml | 6 +-
cla-backend-go/swagger/cla.v2.yaml | 38 ++--
...n.yaml => github-organization-create.yaml} | 0
...n.yaml => github-organization-update.yaml} | 0
...ies.yaml => github-repositories-list.yaml} | 0
.../swagger/common/gitlab-add-repository.yaml | 51 ------
...n.yaml => gitlab-organization-create.yaml} | 0
...n.yaml => gitlab-organization-update.yaml} | 0
.../swagger/common/gitlab-organization.yaml | 3 +
.../common/gitlab-repositories-add.yaml | 22 +++
...ies.yaml => gitlab-repositories-list.yaml} | 0
cla-backend-go/tests/gitlab_client_test.go | 14 +-
cla-backend-go/utils/constants.go | 3 +
.../v2/dynamo_events/gitlab_webhooks.go | 16 +-
cla-backend-go/v2/dynamo_events/service.go | 6 +-
cla-backend-go/v2/gitlab-activity/service.go | 20 +-
.../v2/gitlab_organizations/handlers.go | 12 +-
.../v2/gitlab_organizations/service.go | 58 +++---
cla-backend-go/v2/gitlab_sign/service.go | 6 +-
.../v2/repositories/gitlab_services.go | 171 ++++++++++--------
cla-backend-go/v2/repositories/handlers.go | 44 ++---
cla-backend-go/v2/repositories/repository.go | 54 ++----
cla-backend-go/v2/repositories/service.go | 47 +++--
34 files changed, 540 insertions(+), 390 deletions(-)
delete mode 100644 cla-backend-go/gitlab/organization.go
rename cla-backend-go/{gitlab => gitlab_api}/auth.go (100%)
rename cla-backend-go/{gitlab => gitlab_api}/client.go (94%)
create mode 100644 cla-backend-go/gitlab_api/client_groups.go
create mode 100644 cla-backend-go/gitlab_api/client_projects.go
rename cla-backend-go/{gitlab => gitlab_api}/client_test.go (100%)
rename cla-backend-go/{gitlab => gitlab_api}/init.go (100%)
rename cla-backend-go/{gitlab => gitlab_api}/mr.go (100%)
rename cla-backend-go/{gitlab => gitlab_api}/webhook.go (100%)
rename cla-backend-go/swagger/common/{github-create-organization.yaml => github-organization-create.yaml} (100%)
rename cla-backend-go/swagger/common/{github-update-organization.yaml => github-organization-update.yaml} (100%)
rename cla-backend-go/swagger/common/{github-list-repositories.yaml => github-repositories-list.yaml} (100%)
delete mode 100644 cla-backend-go/swagger/common/gitlab-add-repository.yaml
rename cla-backend-go/swagger/common/{gitlab-create-organization.yaml => gitlab-organization-create.yaml} (100%)
rename cla-backend-go/swagger/common/{gitlab-update-organization.yaml => gitlab-organization-update.yaml} (100%)
create mode 100644 cla-backend-go/swagger/common/gitlab-repositories-add.yaml
rename cla-backend-go/swagger/common/{gitlab-list-repositories.yaml => gitlab-repositories-list.yaml} (100%)
diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go
index 3b3b8a3c5..7226e2699 100644
--- a/cla-backend-go/cmd/dynamo_events_lambda/main.go
+++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go
@@ -10,7 +10,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
- "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index a6888892d..2d38fed75 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -20,7 +20,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
- "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_sign"
"github.com/communitybridge/easycla/cla-backend-go/emails"
@@ -56,7 +56,7 @@ import (
lfxAuth "github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/docs"
- "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ v1Repositories "github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/communitybridge/easycla/cla-backend-go/utils"
v2Docs "github.com/communitybridge/easycla/cla-backend-go/v2/docs"
v2Events "github.com/communitybridge/easycla/cla-backend-go/v2/events"
@@ -244,7 +244,7 @@ func server(localMode bool) http.Handler {
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
usersRepo := users.NewRepository(awsSession, stage)
- gitV1Repository := repositories.NewRepository(awsSession, stage)
+ gitV1Repository := v1Repositories.NewRepository(awsSession, stage)
gitV2Repository := v2Repositories.NewRepository(awsSession, stage)
gerritRepo := gerrits.NewRepository(awsSession, stage)
templateRepo := template.NewRepository(awsSession, stage)
@@ -300,8 +300,8 @@ func server(localMode bool) http.Handler {
v1SignaturesService := signatures.NewService(signaturesRepo, v1CompanyService, usersService, eventsService, githubOrgValidation)
v2SignatureService := v2Signatures.NewService(awsSession, configFile.SignatureFilesBucket, v1ProjectService, v1CompanyService, v1SignaturesService, v1ProjectClaGroupRepo, signaturesRepo, usersService)
v1ClaManagerService := cla_manager.NewService(claManagerReqRepo, v1ProjectClaGroupRepo, v1CompanyService, v1ProjectService, usersService, v1SignaturesService, eventsService, emailTemplateService, configFile.CorporateConsoleV1URL)
- v1RepositoriesService := repositories.NewService(gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo)
- v2RepositoriesService := v2Repositories.NewService(gitV1Repository, gitV2Repository, v1ProjectClaGroupRepo, githubOrganizationsRepo)
+ v1RepositoriesService := v1Repositories.NewService(gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo)
+ v2RepositoriesService := v2Repositories.NewService(gitV1Repository, gitV2Repository, v1ProjectClaGroupRepo, githubOrganizationsRepo, gitlabOrganizationRepo, eventsService)
v2ClaManagerService := v2ClaManager.NewService(emailTemplateService, v1CompanyService, v1ProjectService, v1ClaManagerService, usersService, v1RepositoriesService, v2CompanyService, eventsService, v1ProjectClaGroupRepo)
v1ApprovalListService := approval_list.NewService(approvalListRepo, v1ProjectClaGroupRepo, v1ProjectService, usersRepo, v1CompanyRepo, v1CLAGroupRepo, signaturesRepo, emailTemplateService, configFile.CorporateConsoleV2URL, http.DefaultClient)
authorizer := auth.NewAuthorizer(authValidator, userRepo)
@@ -349,13 +349,11 @@ func server(localMode bool) http.Handler {
v2Metrics.Configure(v2API, v2MetricsService, v1CompanyRepo)
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
- gitlab_organizations.Configure(v2API, gitlabOrganizationsService, v2RepositoriesService, eventsService)
+ gitlab_organizations.Configure(v2API, gitlabOrganizationsService, eventsService)
gitlab_sign.Configure(v2API, gitlabSignService, eventsService, configFile.CLAContributorv2Base)
gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
- repositories.Configure(api, v1RepositoriesService, eventsService)
+ v1Repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
- gitlab_organizations.Configure(v2API, gitlabOrganizationsService, v2RepositoriesService, eventsService)
- gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
v2Gerrits.Configure(v2API, gerritService, v1ProjectService, eventsService, v1ProjectClaGroupRepo)
v2Company.Configure(v2API, v2CompanyService, v1ProjectClaGroupRepo, configFile.LFXPortalURL, configFile.CorporateConsoleV1URL)
diff --git a/cla-backend-go/gitlab/organization.go b/cla-backend-go/gitlab/organization.go
deleted file mode 100644
index f09b096dc..000000000
--- a/cla-backend-go/gitlab/organization.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright The Linux Foundation and each contributor to CommunityBridge.
-// SPDX-License-Identifier: MIT
-
-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{})
- if err != nil {
- return nil, fmt.Errorf("fetching groups failed : %v", err)
- }
-
- for _, group := range groups {
- if group.Name == name {
- return group, nil
- }
- }
-
- 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
-}
diff --git a/cla-backend-go/gitlab/auth.go b/cla-backend-go/gitlab_api/auth.go
similarity index 100%
rename from cla-backend-go/gitlab/auth.go
rename to cla-backend-go/gitlab_api/auth.go
diff --git a/cla-backend-go/gitlab/client.go b/cla-backend-go/gitlab_api/client.go
similarity index 94%
rename from cla-backend-go/gitlab/client.go
rename to cla-backend-go/gitlab_api/client.go
index 145a9e73c..f976eb857 100644
--- a/cla-backend-go/gitlab/client.go
+++ b/cla-backend-go/gitlab_api/client.go
@@ -15,7 +15,7 @@ import (
log "github.com/communitybridge/easycla/cla-backend-go/logging"
- "github.com/xanzy/go-gitlab"
+ goGitLab "github.com/xanzy/go-gitlab"
)
// OauthSuccessResponse is success response from Gitlab
@@ -28,19 +28,19 @@ type OauthSuccessResponse struct {
}
// NewGitlabOauthClient creates a new gitlab client from the given oauth info, authInfo is encrypted
-func NewGitlabOauthClient(authInfo string, gitLabApp *App) (*gitlab.Client, error) {
+func NewGitlabOauthClient(authInfo string, gitLabApp *App) (*goGitLab.Client, error) {
oauthResp, err := DecryptAuthInfo(authInfo, gitLabApp)
if err != nil {
return nil, err
}
log.Infof("creating oauth client with access token : %s", oauthResp.AccessToken)
- return gitlab.NewOAuthClient(oauthResp.AccessToken)
+ return goGitLab.NewOAuthClient(oauthResp.AccessToken)
}
// NewGitlabOauthClientFromAccessToken creates a new gitlab client from the given access token
-func NewGitlabOauthClientFromAccessToken(accessToken string) (*gitlab.Client, error) {
- return gitlab.NewOAuthClient(accessToken)
+func NewGitlabOauthClientFromAccessToken(accessToken string) (*goGitLab.Client, error) {
+ return goGitLab.NewOAuthClient(accessToken)
}
// EncryptAuthInfo encrypts the oauth response into a string
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
new file mode 100644
index 000000000..7f2dda5d7
--- /dev/null
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -0,0 +1,139 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
+ goGitLab "github.com/xanzy/go-gitlab"
+)
+
+// UserGroup represents gitlab group
+type UserGroup struct {
+ Name string
+ FullPath string
+}
+
+// GetGroupsListAll returns a complete list of GitLab groups for which the client as authorization/visibility
+func GetGroupsListAll(ctx context.Context, client *goGitLab.Client) ([]*goGitLab.Group, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.GetGroupsListAll",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ // https://docs.gitlab.com/ee/api/groups.html#list-groups
+ // Query GitLab for repos - fetch the list of repositories available to the GitLab App
+ listGroupsOpts := &goGitLab.ListGroupsOptions{
+ ListOptions: goGitLab.ListOptions{
+ Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
+ PerPage: 100, // max is 100
+ },
+ AllAvailable: utils.Bool(true), // Show all the groups you have access to (defaults to false for authenticated users, true for administrators); Attributes owned and min_access_level have precedence
+ MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
+ }
+
+ var groupList []*goGitLab.Group
+ for {
+ groups, resp, listGroupsErr := client.Groups.ListGroups(listGroupsOpts)
+ if listGroupsErr != nil {
+ msg := fmt.Sprintf("unable to list groups, error: %+v", listGroupsErr)
+ log.WithFields(f).WithError(listGroupsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to list groups, status code: %d", resp.StatusCode)
+ log.WithFields(f).WithError(listGroupsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Append to our response
+ groupList = append(groupList, groups...)
+
+ // Do we have any records to process?
+ if resp.NextPage == 0 {
+ break
+ }
+ }
+
+ return groupList, nil
+}
+
+// GetGroupByName gets a gitlab Group by the given name
+func GetGroupByName(ctx context.Context, client *goGitLab.Client, name string) (*goGitLab.Group, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.GetGroupByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ groups, _, err := client.Groups.ListGroups(&goGitLab.ListGroupsOptions{})
+ if err != nil {
+ msg := fmt.Sprintf("problem fetching groups, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ for _, group := range groups {
+ if group.Name == name {
+ return group, nil
+ }
+ }
+
+ 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(ctx context.Context, client *goGitLab.Client, userID int) ([]*UserGroup, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.ListUserProjectGroups",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ listOptions := &goGitLab.ListProjectsOptions{
+ ListOptions: goGitLab.ListOptions{
+ PerPage: 100,
+ }}
+
+ userGroupsMap := map[string]*UserGroup{}
+ for {
+ log.WithFields(f).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.Debugf("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
+}
diff --git a/cla-backend-go/gitlab_api/client_projects.go b/cla-backend-go/gitlab_api/client_projects.go
new file mode 100644
index 000000000..9e7c6d654
--- /dev/null
+++ b/cla-backend-go/gitlab_api/client_projects.go
@@ -0,0 +1,114 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package gitlab
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+
+ goGitLab "github.com/xanzy/go-gitlab"
+)
+
+// GetProjectListAll returns a complete list of GitLab projects for which the client as authorization/visibility
+func GetProjectListAll(ctx context.Context, client *goGitLab.Client) ([]*goGitLab.Project, error) {
+ // https://docs.gitlab.com/ce/api/projects.html#list-projects
+ // Query GitLab for repos - fetch the list of repositories available to the GitLab App
+ listProjectsOpts := &goGitLab.ListProjectsOptions{
+ ListOptions: goGitLab.ListOptions{
+ Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
+ PerPage: 100, // max is 100
+ },
+ SearchNamespaces: utils.Bool(true), // Include ancestor namespaces when matching search criteria. Default is false.
+ Membership: utils.Bool(true), // Limit by projects that the current user is a member of.
+ MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
+ }
+
+ return getProjectListWithOptions(ctx, client, listProjectsOpts)
+}
+
+// GetProjectListByOrgName returns a list of GitLab projects under the specified Organization
+func GetProjectListByOrgName(ctx context.Context, client *goGitLab.Client, organizationName string) ([]*goGitLab.Project, error) {
+ // Query GitLab for repos - fetch the list of repositories available to the GitLab App
+ listProjectsOpts := &goGitLab.ListProjectsOptions{
+ ListOptions: goGitLab.ListOptions{
+ Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
+ PerPage: 100, // max is 100
+ },
+ Search: utils.StringRef(organizationName), // filter by our organization name
+ SearchNamespaces: utils.Bool(true), // Include ancestor namespaces when matching search criteria. Default is false.
+ Membership: utils.Bool(true), // Limit by projects that the current user is a member of.
+ MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
+ }
+
+ return getProjectListWithOptions(ctx, client, listProjectsOpts)
+}
+
+// getProjectListWithOptions returns a list of GitLab projects using the specified filter
+func getProjectListWithOptions(ctx context.Context, client *goGitLab.Client, opts *goGitLab.ListProjectsOptions) ([]*goGitLab.Project, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab.client.getProjectListWithOptions",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ var projectList []*goGitLab.Project
+ for {
+ // Need to use this func to get the list of projects the user has access to, see: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63811
+ projects, resp, listProjectsErr := client.Projects.ListProjects(opts)
+ if listProjectsErr != nil {
+ msg := fmt.Sprintf("unable to list projects, error: %+v", listProjectsErr)
+ log.WithFields(f).WithError(listProjectsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to list projects, status code: %d", resp.StatusCode)
+ log.WithFields(f).WithError(listProjectsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Append to our response
+ projectList = append(projectList, projects...)
+
+ // Do we have any records to process?
+ if resp.NextPage == 0 {
+ break
+ }
+ }
+
+ return projectList, nil
+}
+
+// GetProjectByID returns the GitLab project for the specified ID
+func GetProjectByID(ctx context.Context, client *goGitLab.Client, gitLabProjectID int) (*goGitLab.Project, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab.client.GetProjectByID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabProjectID": gitLabProjectID,
+ }
+
+ // Query GitLab for repos - fetch the list of repositories available to the GitLab App
+ project, resp, getProjectErr := client.Projects.GetProject(gitLabProjectID, &goGitLab.GetProjectOptions{})
+ if getProjectErr != nil {
+ msg := fmt.Sprintf("unable to get project by ID: %d, error: %+v", gitLabProjectID, getProjectErr)
+ log.WithFields(f).WithError(getProjectErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to get project by ID: %d, status code: %d", gitLabProjectID, resp.StatusCode)
+ log.WithFields(f).WithError(getProjectErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if project == nil {
+ msg := fmt.Sprintf("unable to get project by ID: %d, project is empty", gitLabProjectID)
+ log.WithFields(f).WithError(getProjectErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ return project, nil
+}
diff --git a/cla-backend-go/gitlab/client_test.go b/cla-backend-go/gitlab_api/client_test.go
similarity index 100%
rename from cla-backend-go/gitlab/client_test.go
rename to cla-backend-go/gitlab_api/client_test.go
diff --git a/cla-backend-go/gitlab/init.go b/cla-backend-go/gitlab_api/init.go
similarity index 100%
rename from cla-backend-go/gitlab/init.go
rename to cla-backend-go/gitlab_api/init.go
diff --git a/cla-backend-go/gitlab/mr.go b/cla-backend-go/gitlab_api/mr.go
similarity index 100%
rename from cla-backend-go/gitlab/mr.go
rename to cla-backend-go/gitlab_api/mr.go
diff --git a/cla-backend-go/gitlab/webhook.go b/cla-backend-go/gitlab_api/webhook.go
similarity index 100%
rename from cla-backend-go/gitlab/webhook.go
rename to cla-backend-go/gitlab_api/webhook.go
diff --git a/cla-backend-go/swagger/cla.v1.yaml b/cla-backend-go/swagger/cla.v1.yaml
index 5b57cca9d..b94962ddb 100644
--- a/cla-backend-go/swagger/cla.v1.yaml
+++ b/cla-backend-go/swagger/cla.v1.yaml
@@ -2857,7 +2857,7 @@ definitions:
$ref: './common/create-cla-group-template.yaml'
update-github-organization:
- $ref: './common/github-update-organization.yaml'
+ $ref: './common/github-organization-update.yaml'
template-pdfs:
$ref: './common/template-pdfs.yaml'
@@ -3104,7 +3104,7 @@ definitions:
$ref: './common/github-organizations.yaml'
github-create-organization:
- $ref: './common/github-create-organization.yaml'
+ $ref: './common/github-organization-create.yaml'
github-organization:
$ref: './common/github-organization.yaml'
@@ -3113,7 +3113,7 @@ definitions:
$ref: './common/github-repository-info.yaml'
github-list-repositories:
- $ref: './common/github-list-repositories.yaml'
+ $ref: './common/github-repositories-list.yaml'
org-list:
$ref: './common/org-list.yaml'
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 3a6dcad53..05ee68c21 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1707,7 +1707,7 @@ paths:
- in: body
name: body
schema:
- $ref: '#/definitions/gitlab-update-organization'
+ $ref: '#/definitions/gitlab-organization-update'
required: true
responses:
'200':
@@ -1777,9 +1777,9 @@ paths:
type: string
required: true
- in: body
- name: gitlab-add-repository
+ name: gitlab-repositories-add
schema:
- $ref: '#/definitions/gitlab-add-repository'
+ $ref: '#/definitions/gitlab-repositories-add'
required: true
responses:
'200':
@@ -1789,7 +1789,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/gitlab-repository'
+ $ref: '#/definitions/gitlab-repositories-list'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -1825,7 +1825,7 @@ paths:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
- $ref: '#/definitions/gitlab-list-repositories'
+ $ref: '#/definitions/gitlab-repositories-list'
'400':
$ref: '#/responses/invalid-request'
'401':
@@ -4000,7 +4000,7 @@ paths:
$ref: '#/responses/internal-server-error'
tags:
- gitlab-activity
-
+
/repository-provider/gitlab/sign/{organizationID}/{gitlabRepositoryID}/{mergeRequestID}:
get:
summary: Gitlab sign request handler
@@ -4478,13 +4478,13 @@ definitions:
$ref: './common/github-repository.yaml'
github-create-organization:
- $ref: './common/github-create-organization.yaml'
+ $ref: './common/github-organization-create.yaml'
github-update-organization:
- $ref: './common/github-update-organization.yaml'
+ $ref: './common/github-organization-update.yaml'
github-list-repositories:
- $ref: './common/github-list-repositories.yaml'
+ $ref: './common/github-repositories-list.yaml'
# ---------------------------------------------------------------------------
# GitLab Definitions
@@ -4492,20 +4492,20 @@ definitions:
gitlab-organization:
$ref: './common/gitlab-organization.yaml'
- gitlab-repository:
- $ref: './common/gitlab-repository.yaml'
-
gitlab-create-organization:
- $ref: './common/gitlab-create-organization.yaml'
+ $ref: './common/gitlab-organization-create.yaml'
- gitlab-update-organization:
- $ref: './common/github-update-organization.yaml'
+ gitlab-organization-update:
+ $ref: './common/gitlab-organization-update.yaml'
+
+ gitlab-repository:
+ $ref: './common/gitlab-repository.yaml'
- gitlab-list-repositories:
- $ref: './common/gitlab-list-repositories.yaml'
+ gitlab-repositories-list:
+ $ref: './common/gitlab-repositories-list.yaml'
- gitlab-add-repository:
- $ref: './common/gitlab-add-repository.yaml'
+ gitlab-repositories-add:
+ $ref: './common/gitlab-repositories-add.yaml'
# ---------------------------------------------------------------------------
# CLA Group Definitions
diff --git a/cla-backend-go/swagger/common/github-create-organization.yaml b/cla-backend-go/swagger/common/github-organization-create.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/github-create-organization.yaml
rename to cla-backend-go/swagger/common/github-organization-create.yaml
diff --git a/cla-backend-go/swagger/common/github-update-organization.yaml b/cla-backend-go/swagger/common/github-organization-update.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/github-update-organization.yaml
rename to cla-backend-go/swagger/common/github-organization-update.yaml
diff --git a/cla-backend-go/swagger/common/github-list-repositories.yaml b/cla-backend-go/swagger/common/github-repositories-list.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/github-list-repositories.yaml
rename to cla-backend-go/swagger/common/github-repositories-list.yaml
diff --git a/cla-backend-go/swagger/common/gitlab-add-repository.yaml b/cla-backend-go/swagger/common/gitlab-add-repository.yaml
deleted file mode 100644
index f3a2d115d..000000000
--- a/cla-backend-go/swagger/common/gitlab-add-repository.yaml
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright The Linux Foundation and each contributor to CommunityBridge.
-# SPDX-License-Identifier: MIT
-
-type: object
-required:
- - repository_external_id
- - repository_project_sfid
- - repository_cla_group_id
- - repository_name
- - repository_full_path
- - repository_organization_name
- - repository_url
-properties:
- repository_external_id:
- type: integer
- description: The repository ID from the external service, such as GitHub or GitLab
- minimum: 1
- example: 7
- repository_project_sfid:
- description: Project SFID
- $ref: './common/properties/external-id.yaml'
- repository_cla_group_id:
- description: CLA Group ID
- $ref: './common/properties/internal-id.yaml'
- repository_name:
- type: string
- description: The repository name
- minLength: 3
- example: 'easycla-test-repo-4'
- repository_full_path:
- type: string
- description: The repository full path
- minLength: 3
- example: 'linuxfoundation/product/easycla/easycla-test-repo-4'
- repository_organization_name:
- type: string
- description: The organization name associated with this repository
- minLength: 3
- example: 'The Linux Foundation/product/EasyCLA'
- repository_url:
- type: string
- description: The external repository URL
- minLength: 8
- example: 'https://gitlab.com/linuxfoundation/product/easycla/easycla-test-repo-4'
- enabled:
- type: boolean
- description: Flag to indicate if this repository is enabled or not. Repositories may become disabled if they have been moved or deleted from GitHub or GitLab.
- x-omitempty: false
- note:
- description: optional note added to the record
- type: string
diff --git a/cla-backend-go/swagger/common/gitlab-create-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/gitlab-create-organization.yaml
rename to cla-backend-go/swagger/common/gitlab-organization-create.yaml
diff --git a/cla-backend-go/swagger/common/gitlab-update-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization-update.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/gitlab-update-organization.yaml
rename to cla-backend-go/swagger/common/gitlab-organization-update.yaml
diff --git a/cla-backend-go/swagger/common/gitlab-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization.yaml
index 5e407708a..faecadbee 100644
--- a/cla-backend-go/swagger/common/gitlab-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization.yaml
@@ -51,6 +51,9 @@ properties:
auto_enabled_cla_group_id:
type: string
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
+ auth_info:
+ type: string
+ description: auth info
gitlab_info:
type: object
properties:
diff --git a/cla-backend-go/swagger/common/gitlab-repositories-add.yaml b/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
new file mode 100644
index 000000000..fa1e32043
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
@@ -0,0 +1,22 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+required:
+ - gitlab_organization_name
+ - cla_group_id
+properties:
+ repository_gitlab_ids:
+ type: array
+ items:
+ description: the repository external identifier, such as the GitLab ID of the repository
+ type: integer
+ minimum: 1
+ example: 7
+ gitlab_organization_name:
+ type: string
+ description: The organization name associated with this repository
+ example: 'The Linux Foundation/product/EasyCLA'
+ cla_group_id:
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
diff --git a/cla-backend-go/swagger/common/gitlab-list-repositories.yaml b/cla-backend-go/swagger/common/gitlab-repositories-list.yaml
similarity index 100%
rename from cla-backend-go/swagger/common/gitlab-list-repositories.yaml
rename to cla-backend-go/swagger/common/gitlab-repositories-list.yaml
diff --git a/cla-backend-go/tests/gitlab_client_test.go b/cla-backend-go/tests/gitlab_client_test.go
index 851c0b61a..46eda1e9e 100644
--- a/cla-backend-go/tests/gitlab_client_test.go
+++ b/cla-backend-go/tests/gitlab_client_test.go
@@ -16,7 +16,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/stretchr/testify/assert"
"github.com/xanzy/go-gitlab"
)
@@ -51,10 +51,10 @@ func TestGitLabGetGroup(t *testing.T) { // no lint
config := ini.GetConfig()
// Create a new GitLab App client instance
- gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
// Create a new client
- gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
@@ -93,10 +93,10 @@ func TestGitLabListGroups(t *testing.T) { // no lint
config := ini.GetConfig()
// Create a new GitLab App client instance
- gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
// Create a new client
- gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
@@ -147,10 +147,10 @@ func TestGitLabListProjects(t *testing.T) { // no lint
config := ini.GetConfig()
// Create a new GitLab App client instance
- gitLabApp := gitlab2.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
// Create a new client
- gitLabClient, err := gitlab2.NewGitlabOauthClient(accessInfo, gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
diff --git a/cla-backend-go/utils/constants.go b/cla-backend-go/utils/constants.go
index 1f29cf8bd..e3be79cca 100644
--- a/cla-backend-go/utils/constants.go
+++ b/cla-backend-go/utils/constants.go
@@ -60,6 +60,9 @@ const ProjectFundedSupportedByParent = "Supported by Parent Project"
// XREQUESTID is the client request id - used to trace a client request through the system/logs
const XREQUESTID = "x-request-id"
+// CtxAuthUser the key for the authenticated user in the context
+const CtxAuthUser = "authUser"
+
// CLAProjectManagerRole CLA project manager role identifier
const CLAProjectManagerRole = "project-manager"
diff --git a/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
index 12e5276be..49c3160ba 100644
--- a/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
+++ b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
@@ -9,7 +9,7 @@ import (
"github.com/aws/aws-lambda-go/events"
"github.com/communitybridge/easycla/cla-backend-go/config"
- "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -55,7 +55,7 @@ func (s *service) GitLabRepoAddedWebhookEventHandler(event events.DynamoDBEventR
return fmt.Errorf("fetching gitlab org : %s failed : %v", newRepoModel.RepositoryOrganizationName, err)
}
- gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
if err != nil {
return fmt.Errorf("initializing GitLab client failed : %v", err)
}
@@ -66,7 +66,7 @@ func (s *service) GitLabRepoAddedWebhookEventHandler(event events.DynamoDBEventR
}
conf := config.GetConfig()
- if err := gitlab.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
+ if err := gitlab_api.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
log.WithFields(f).Errorf("adding gitlab webhook failed : %v", err)
}
@@ -124,7 +124,7 @@ func (s *service) GitlabRepoModifiedWebhookEventHandler(event events.DynamoDBEve
return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
}
- gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
if err != nil {
return fmt.Errorf("initializing GitLab client failed : %v", err)
}
@@ -137,11 +137,11 @@ func (s *service) GitlabRepoModifiedWebhookEventHandler(event events.DynamoDBEve
conf := config.GetConfig()
if newRepoModel.Enabled {
- if err := gitlab.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
+ if err := gitlab_api.SetWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt, gitlabOrg.AuthState); err != nil {
log.WithFields(f).Errorf("adding gitlab webhook failed : %v", err)
}
} else {
- if err := gitlab.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
+ if err := gitlab_api.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
log.WithFields(f).Errorf("removing gitlab webhook failed : %v", err)
}
}
@@ -184,7 +184,7 @@ func (s *service) GitLabRepoRemovedWebhookEventHandler(event events.DynamoDBEven
return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
}
- gitLabClient, err := gitlab.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
if err != nil {
return fmt.Errorf("initializing GitLab client failed : %v", err)
}
@@ -195,7 +195,7 @@ func (s *service) GitLabRepoRemovedWebhookEventHandler(event events.DynamoDBEven
}
conf := config.GetConfig()
- if err := gitlab.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
+ if err := gitlab_api.RemoveWebHook(gitLabClient, conf.Gitlab.WebHookURI, repositoryExternalIDInt); err != nil {
log.WithFields(f).Errorf("removing gitlab webhook failed : %v", err)
}
diff --git a/cla-backend-go/v2/dynamo_events/service.go b/cla-backend-go/v2/dynamo_events/service.go
index 83a27c194..ce2d01794 100644
--- a/cla-backend-go/v2/dynamo_events/service.go
+++ b/cla-backend-go/v2/dynamo_events/service.go
@@ -11,7 +11,7 @@ import (
"strings"
"sync"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/gerrits"
@@ -68,7 +68,7 @@ type service struct {
autoEnableService *autoEnableServiceProvider
claManagerRequestsRepo cla_manager.IRepository
approvalListRequestsRepo approval_list.IRepository
- gitLabApp *gitlab2.App
+ gitLabApp *gitlab_api.App
}
// Service implements DynamoDB stream event handler service
@@ -91,7 +91,7 @@ func NewService(stage string,
gerritService gerrits.Service,
claManagerRequestsRepo cla_manager.IRepository,
approvalListRequestsRepo approval_list.IRepository,
- gitLabApp *gitlab2.App) Service {
+ gitLabApp *gitlab_api.App) Service {
signaturesTable := fmt.Sprintf("cla-%s-signatures", stage)
eventsTable := fmt.Sprintf("cla-%s-events", stage)
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 222653083..6d4d61340 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -20,7 +20,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/communitybridge/easycla/cla-backend-go/signatures"
@@ -58,7 +58,7 @@ type service struct {
projectsCLAGroupsRepository projects_cla_groups.Repository
companyRepository company.IRepository
signatureRepository signatures.SignatureRepository
- gitLabApp *gitlab2.App
+ gitLabApp *gitlab_api.App
}
func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRepository repositories.RepositoryInterface, gitV2Repository gitV2Repositories.RepositoryInterface, usersRepository users.UserRepository, signaturesRepository signatures.SignatureRepository, projectsCLAGroupsRepository projects_cla_groups.Repository,
@@ -72,7 +72,7 @@ func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRe
projectsCLAGroupsRepository: projectsCLAGroupsRepository,
companyRepository: companyRepository,
signatureRepository: signatureRepository,
- gitLabApp: gitlab2.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitlab_api.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -102,12 +102,12 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
log.WithFields(f).Debugf("internal gitlab org : %s:%s is associated with external path : %s", gitlabOrg.OrganizationID, gitlabOrg.OrganizationName, repositoryPath)
- gitlabClient, err := gitlab2.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
+ gitlabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
if err != nil {
return fmt.Errorf("initializing gitlab client : %v", err)
}
- _, err = gitlab2.FetchMrInfo(gitlabClient, projectID, mergeID)
+ _, err = gitlab_api.FetchMrInfo(gitlabClient, projectID, mergeID)
if err != nil {
return fmt.Errorf("fetching info for mr : %d and project : %d: %s, failed : %v", mergeID, projectID, projectName, err)
}
@@ -119,7 +119,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
}
log.WithFields(f).Debugf("internal gitlab repository found with id : %s", gitlabRepo.RepositoryID)
- participants, err := gitlab2.FetchMrParticipants(gitlabClient, projectID, mergeID, true)
+ participants, err := gitlab_api.FetchMrParticipants(gitlabClient, projectID, mergeID, true)
if err != nil {
return fmt.Errorf("fetching mr participants : %v", err)
}
@@ -158,22 +158,22 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
mrCommentContent := PrepareMrCommentContent(missingUsers, signedUsers, signURL)
if len(missingUsers) > 0 {
log.WithFields(f).Errorf("mr faild with following users : %s", mrCommentContent)
- if err := gitlab2.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Failed, missingCLAMsg, signURL); err != nil {
+ if err := gitlab_api.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Failed, missingCLAMsg, signURL); err != nil {
return fmt.Errorf("setting commit status failed : %v", err)
}
- if err := gitlab2.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Failed, mrCommentContent, signURL); err != nil {
+ if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Failed, mrCommentContent, signURL); err != nil {
return fmt.Errorf("setting comment failed : %v", err)
}
return nil
}
- err = gitlab2.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Success, signedCLAMsg, "")
+ err = gitlab_api.SetCommitStatus(gitlabClient, projectID, lastCommitSha, gitlab.Success, signedCLAMsg, "")
if err != nil {
return fmt.Errorf("setting commit status failed : %v", err)
}
- if err := gitlab2.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Success, mrCommentContent, signURL); err != nil {
+ if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Success, mrCommentContent, signURL); err != nil {
return fmt.Errorf("setting comment failed : %v", err)
}
return err
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index e46897e1b..1fa4d713b 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -15,7 +15,7 @@ import (
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
- "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/gofrs/uuid"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -29,12 +29,11 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/utils"
- v2GitRepo "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"
"github.com/go-openapi/runtime/middleware"
)
// Configure setups handlers on api with service
-func Configure(api *operations.EasyclaAPI, service ServiceInterface, gitV2Service v2GitRepo.ServiceInterface, eventService events.Service) {
+func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventService events.Service) {
api.GitlabOrganizationsGetProjectGitlabOrganizationsHandler = gitlab_organizations.GetProjectGitlabOrganizationsHandlerFunc(
func(params gitlab_organizations.GetProjectGitlabOrganizationsParams, authUser *auth.User) middleware.Responder {
@@ -293,7 +292,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, gitV2Servic
}
// now fetch the oauth credentials and store to db
- oauthResp, err := gitlab.FetchOauthCredentials(params.Code)
+ oauthResp, err := gitlab_api.FetchOauthCredentials(params.Code)
if err != nil {
msg := fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
log.WithFields(f).Errorf(msg)
@@ -316,11 +315,6 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, gitV2Servic
return NewServerError(reqID, "", errors.New(msg))
}
- _, err = gitV2Service.GitLabAddRepositoriesByApp(ctx, updatedGitLabOrgDBModel)
- if err != nil {
- return NewServerError(reqID, updatedGitLabOrgDBModel.OrganizationName, err)
- }
-
return NewSuccessResponse(reqID, updatedGitLabOrgDBModel.ProjectSFID, updatedGitLabOrgDBModel.OrganizationName)
})
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 387f5a879..be847e2c9 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -17,13 +17,12 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
"github.com/communitybridge/easycla/cla-backend-go/config"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/go-openapi/strfmt"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
- "github.com/communitybridge/easycla/cla-backend-go/gitlab"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -41,7 +40,7 @@ type ServiceInterface interface {
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
- UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab.OauthSuccessResponse) error
+ UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
}
@@ -50,7 +49,7 @@ type Service struct {
repo RepositoryInterface
v2GitRepoService repositories.ServiceInterface
claGroupRepository projects_cla_groups.Repository
- gitLabApp *gitlab.App
+ gitLabApp *gitlab_api.App
}
// NewService creates a new gitlab organization service
@@ -59,7 +58,7 @@ func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceI
repo: repo,
v2GitRepoService: v2GitRepoService,
claGroupRepository: claGroupRepository,
- gitLabApp: gitlab.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitlab_api.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -238,7 +237,7 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
rorg.ConnectionStatus = utils.ConnectionFailure
} else {
// We've been authenticated by the user - great, see if we can determine the list of repos...
- glClient, clientErr := gitlab.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
+ glClient, clientErr := gitlab_api.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
if clientErr != nil {
log.WithFields(f).Errorf("using gitlab client for gitlab org : %s failed : %v", org.OrganizationID, clientErr)
rorg.ConnectionStatus = utils.ConnectionFailure
@@ -297,7 +296,7 @@ func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrgani
}
// UpdateGitlabOrganizationAuth updates the GitLab organization authentication information
-func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab.OauthSuccessResponse) error {
+func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.UpdateGitlabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -305,7 +304,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
}
log.WithFields(f).Debugf("updating gitlab org auth")
- authInfoEncrypted, err := gitlab.EncryptAuthInfo(oauthResp, s.gitLabApp)
+ authInfoEncrypted, err := gitlab_api.EncryptAuthInfo(oauthResp, s.gitLabApp)
if err != nil {
return fmt.Errorf("encrypt failed : %v", err)
}
@@ -316,30 +315,37 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
}
// Get the client
- gitLabClient, err := gitlab2.NewGitlabOauthClientFromAccessToken(oauthResp.AccessToken)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClientFromAccessToken(oauthResp.AccessToken)
if err != nil {
return fmt.Errorf("initializing gitlab client : %v", err)
}
- // Need to look up the GitLab Group/Organization to obtain the ID
- //groups, resp, searchErr := gitLabClient.Groups.SearchGroup(gitLabOrgModel.OrganizationName)
- // Need to look up the GitLab Group/Organization to obtain the ID
- opts := &goGitLab.ListGroupsOptions{
- ListOptions: goGitLab.ListOptions{
- Page: 1,
- PerPage: 100,
- },
- }
- groups, resp, searchErr := gitLabClient.Groups.ListGroups(opts)
- if searchErr != nil {
- return fmt.Errorf("GitLab search error while locating Group by name: %s, error: %v", gitLabOrgModel.OrganizationName, searchErr)
- }
- if resp.StatusCode < 200 || resp.StatusCode > 299 {
- return fmt.Errorf("unable to locate GitLab group by name: %s, status code: %d", gitLabOrgModel.OrganizationName, resp.StatusCode)
+ // Query the groups list
+ groups, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient)
+ if groupListErr != nil {
+ return groupListErr
}
+
for _, g := range groups {
if g.Name == gitLabOrgModel.OrganizationName {
- return s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
+ updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
+ if updateGitLabOrgErr != nil {
+ return updateGitLabOrgErr
+ }
+
+ log.WithFields(f).Debugf("fetching updated GitLab group/organization record")
+ updatedDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ if getErr != nil {
+ return getErr
+ }
+
+ log.WithFields(f).Debugf("adding GitLab repositories for this group/organization")
+ _, err = s.v2GitRepoService.GitLabAddRepositoriesByApp(ctx, updatedDBModel)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
}
@@ -403,7 +409,7 @@ func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI
return &installationURL
}
-func toGitLabProjectResponse(gitLabListRepos *models.GitlabListRepositories) []*models.GitlabProjectRepository {
+func toGitLabProjectResponse(gitLabListRepos *models.GitlabRepositoriesList) []*models.GitlabProjectRepository {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.toGitLabProjectResponse",
}
diff --git a/cla-backend-go/v2/gitlab_sign/service.go b/cla-backend-go/v2/gitlab_sign/service.go
index 5edc8e2f1..5cd106464 100644
--- a/cla-backend-go/v2/gitlab_sign/service.go
+++ b/cla-backend-go/v2/gitlab_sign/service.go
@@ -16,7 +16,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
- v2Gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/users"
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
@@ -29,7 +29,7 @@ type service struct {
repoService repositories.ServiceInterface
gitlabOrgRepo gitlab_organizations.RepositoryInterface
userService users.Service
- gitlabApp v2Gitlab.App
+ gitlabApp gitlab_api.App
storeRepo store.Repository
}
@@ -65,7 +65,7 @@ func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organ
log.WithFields(f).Debug(msg)
return nil
}
- gitlabClient, err := v2Gitlab.NewGitlabOauthClient(organization.AuthInfo, &s.gitlabApp)
+ gitlabClient, err := gitlab_api.NewGitlabOauthClient(organization.AuthInfo, &s.gitlabApp)
if err != nil {
log.WithFields(f).Debugf("initializaing gitlab client for gitlab org: %s failed: %v", organizationID, err)
return nil
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 56b810e86..1ba103000 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -5,30 +5,88 @@ package repositories
import (
"context"
+ "errors"
"fmt"
"strconv"
+ "github.com/LF-Engineering/lfx-kit/auth"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+
v2GitLabOrg "github.com/communitybridge/easycla/cla-backend-go/v2/common"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
- "github.com/xanzy/go-gitlab"
-
"github.com/sirupsen/logrus"
v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
repoModels "github.com/communitybridge/easycla/cla-backend-go/repositories"
)
-// GitLabAddRepository service function
-func (s *Service) GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*v2Models.GitlabRepository, error) {
- dbModel, err := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, input)
+// GitLabAddRepositories service function
+func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesAdd) (*v2Models.GitlabRepositoriesList, error) {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "organizationName": utils.StringValue(input.GitlabOrganizationName),
+ "claGroupID": utils.StringValue(input.ClaGroupID),
+ }
+
+ gitLabOrgModel, orgErr := s.glOrgRepo.GetGitlabOrganizationByName(ctx, utils.StringValue(input.GitlabOrganizationName))
+ if orgErr != nil {
+ msg := fmt.Sprintf("problem loading gitlab organization by name: %s, error: %v", utils.StringValue(input.GitlabOrganizationName), orgErr)
+ log.WithFields(f).WithError(orgErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Get the client
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("initializing gitlab client : %v", err)
}
- return dbModelToGitLabRepository(dbModel)
+ for _, gitLabProjectID := range input.RepositoryGitlabIds {
+ project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, int(gitLabProjectID)) // ok to down-cast as the IDs are not 64 bit
+ if getProjectErr != nil {
+ return nil, fmt.Errorf("unable to load project by ID: %d, error: %v", int(gitLabProjectID), getProjectErr)
+ }
+
+ // Convert int to string
+ repositoryExternalIDString := strconv.Itoa(project.ID)
+
+ inputDBModel := &repoModels.RepositoryDBModel{
+ RepositorySfdcID: projectSFID,
+ ProjectSFID: projectSFID,
+ RepositoryExternalID: repositoryExternalIDString,
+ RepositoryName: project.Name,
+ RepositoryFullPath: project.PathWithNamespace,
+ RepositoryURL: project.WebURL,
+ RepositoryOrganizationName: utils.StringValue(input.GitlabOrganizationName), // gitlab group/organization
+ RepositoryCLAGroupID: utils.StringValue(input.ClaGroupID),
+ RepositoryType: utils.GitLabLower, // should always be gitlab
+ Enabled: true,
+ }
+
+ _, addErr := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, inputDBModel)
+ if addErr != nil {
+ log.WithFields(f).WithError(addErr).Warnf("problem adding GitLab repository with name: %s, error: %+v", project.PathWithNamespace, addErr)
+ return nil, addErr
+ }
+
+ // Log the event
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryAdded,
+ ProjectSFID: projectSFID,
+ CLAGroupID: utils.StringValue(input.ClaGroupID),
+ LfUsername: ctx.Value(utils.CtxAuthUser).(*auth.User).UserName,
+ EventData: &events.RepositoryAddedEventData{
+ RepositoryName: project.PathWithNamespace, // give the full path/name
+ },
+ })
+ }
+
+ return s.GitLabGetRepositoriesByOrganizationName(ctx, utils.StringValue(input.GitlabOrganizationName))
}
// GitLabAddRepositoriesByApp adds the GitLab repositories based on the application credentials
@@ -39,8 +97,9 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
"projectSFID": gitLabOrgModel.ProjectSFID,
"organizationName": gitLabOrgModel.OrganizationName,
}
+
// Get the client
- gitlabClient, err := gitlab2.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
if err != nil {
return nil, fmt.Errorf("initializing gitlab client : %v", err)
}
@@ -51,65 +110,29 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
return nil, fmt.Errorf("unable to locate Project CLAGroup using projectSFID: %s for GitLab repositories group ID: %d, error: %+v", gitLabOrgModel.ProjectSFID, gitLabOrgModel.ExternalGroupID, projectCLAGroupLookupErr)
}
- user, resp, userErr := gitlabClient.Users.CurrentUser()
- if userErr != nil {
- return nil, fmt.Errorf("unable to locate current user, error: %+v", userErr)
- }
- if resp.StatusCode < 200 || resp.StatusCode > 299 {
- return nil, fmt.Errorf("unable to locate current user, status code: %d", resp.StatusCode)
- }
-
- log.WithFields(f).Debugf("fetched current username: %s with name: %s with email: %s", user.Username, user.Name, user.PublicEmail)
-
- // Query GitLab for repos - fetch the list of repositories available to the GitLab App
- listProjectsOpts := &gitlab.ListProjectsOptions{
- ListOptions: gitlab.ListOptions{
- Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
- PerPage: 100, // max is 100
- },
- Search: utils.StringRef(gitLabOrgModel.OrganizationName), // filter by our organization
- SearchNamespaces: utils.Bool(true),
- Membership: utils.Bool(true),
- MinAccessLevel: gitlab.AccessLevel(gitlab.MaintainerPermissions),
- }
-
- // TODO - DAD - loop until no more repos, could be more than 100
- log.WithFields(f).Debugf("searching for GitLab projects based on the search critera: %s", gitLabOrgModel.OrganizationName)
- // Need to use this func to get the list of projects the user has access to, see: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63811
- projects, resp, listProjectsErr := gitlabClient.Projects.ListProjects(listProjectsOpts)
- //projects, resp, listProjectsErr := gitlabClient.Projects.ListUserProjects(user.ID, listProjectsOpts)
- if listProjectsErr != nil {
- return nil, fmt.Errorf("unable to list projects for current user, error: %+v", listProjectsErr)
- }
- if resp.StatusCode < 200 || resp.StatusCode > 299 {
- return nil, fmt.Errorf("unable to list projects for current user, status code: %d", resp.StatusCode)
- }
-
- // Add repos to table
- for _, proj := range projects {
- log.WithFields(f).Debugf("Repository: %s, path: %s, id: %d", proj.Name, proj.Path, proj.ID)
-
- // TODO - make sure we don't have duplicates?
- /*
- Name: "easycla-test-repo-demo-1",
- NameWithNamespace: "The Linux Foundation / product / EasyCLA / Demo / easycla-test-repo-demo-1",
- Path: "easycla-test-repo-demo-1",
- PathWithNamespace: "linuxfoundation/product/easycla/demo/easycla-test-repo-demo-1",
- */
- _, addRepoErr := s.GitLabAddRepository(ctx, gitLabOrgModel.ProjectSFID, &v2Models.GitlabAddRepository{
- Enabled: false, // default is false
- Note: fmt.Sprintf("Added during onboarding of organization: %s", gitLabOrgModel.OrganizationName),
- RepositoryExternalID: utils.Int64(int64(proj.ID)),
- RepositoryName: utils.StringRef(proj.Name),
- RepositoryFullPath: utils.StringRef(proj.PathWithNamespace),
- RepositoryOrganizationName: utils.StringRef(gitLabOrgModel.OrganizationName),
- RepositoryProjectSfid: utils.StringRef(gitLabOrgModel.ProjectSFID),
- RepositoryURL: utils.StringRef(proj.WebURL),
- RepositoryClaGroupID: utils.StringRef(projectCLAGroupModel.ClaGroupID),
- })
- if addRepoErr != nil {
- return nil, addRepoErr
- }
+ // Query the project list by organization name
+ projectList, projectListErr := gitlab_api.GetProjectListByOrgName(ctx, gitLabClient, gitLabOrgModel.OrganizationName)
+ if projectListErr != nil {
+ return nil, projectListErr
+ }
+
+ var listProjectIDs []int64
+
+ // Build a list of project IDs
+ for _, proj := range projectList {
+ log.WithFields(f).Debugf("repo: %s, path: %s, id: %d, weburl: %s", proj.Name, proj.Path, proj.ID, proj.WebURL)
+ listProjectIDs = append(listProjectIDs, int64(proj.ID))
+ }
+
+ // Build input to the add function
+ input := &v2Models.GitlabRepositoriesAdd{
+ ClaGroupID: utils.StringRef(projectCLAGroupModel.ClaGroupID),
+ GitlabOrganizationName: utils.StringRef(gitLabOrgModel.OrganizationName),
+ RepositoryGitlabIds: listProjectIDs,
+ }
+ _, addRepoErr := s.GitLabAddRepositories(ctx, gitLabOrgModel.ProjectSFID, input)
+ if addRepoErr != nil {
+ return nil, addRepoErr
}
// Return the list of repos to caller
@@ -141,7 +164,7 @@ func (s *Service) GitLabGetRepositoryByName(ctx context.Context, repositoryName
}
// GitLabGetRepositoriesByProjectSFID service function
-func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabListRepositories, error) {
+func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error) {
dbModel, err := s.gitV2Repository.GitHubGetRepositoriesByProjectSFID(ctx, projectSFID)
if err != nil {
return nil, err
@@ -152,13 +175,13 @@ func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projec
return nil, err
}
- return &v2Models.GitlabListRepositories{
+ return &v2Models.GitlabRepositoriesList{
List: responses,
}, nil
}
// GitLabGetRepositoriesByCLAGroup service function
-func (s *Service) GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabListRepositories, error) {
+func (s *Service) GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabRepositoriesList, error) {
var dbModels []*repoModels.RepositoryDBModel
var err error
if enabled {
@@ -175,13 +198,13 @@ func (s *Service) GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupI
return nil, err
}
- return &v2Models.GitlabListRepositories{
+ return &v2Models.GitlabRepositoriesList{
List: responses,
}, nil
}
// GitLabGetRepositoriesByOrganizationName returns the list of repositories associated with the Organization/Group name
-func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabListRepositories, error) {
+func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error) {
dbModels, err := s.gitV2Repository.GitHubGetRepositoriesByOrganizationName(ctx, orgName)
if err != nil {
return nil, err
@@ -192,7 +215,7 @@ func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, o
return nil, err
}
- return &v2Models.GitlabListRepositories{
+ return &v2Models.GitlabRepositoriesList{
List: responses,
}, nil
}
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index fc1824aae..79760d51b 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -396,7 +396,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_repositories.NewGetProjectGitLabRepositoriesBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- response := &models.GitlabListRepositories{}
+ response := &models.GitlabRepositoriesList{}
err = copier.Copy(response, result)
if err != nil {
msg := fmt.Sprintf("problem converting response for projectSFID: %s", params.ProjectSFID)
@@ -411,19 +411,15 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_repositories.AddProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := context.WithValue(context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID), "authUser", authUser) // nolint
f := logrus.Fields{
- "functionName": "v2.repositories.handlers.GitlabRepositoriesAddProjectGitLabRepositoryHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
- "projectSFID": params.ProjectSFID,
- "repositoryExternalID": utils.Int64Value(params.GitlabAddRepository.RepositoryExternalID),
- "repositoryName": utils.StringValue(params.GitlabAddRepository.RepositoryName),
- "repositoryURL": utils.StringValue(params.GitlabAddRepository.RepositoryURL),
- "repositoryOrganizationName": utils.StringValue(params.GitlabAddRepository.RepositoryOrganizationName),
- "repositoryCLAGroupID": utils.StringValue(params.GitlabAddRepository.RepositoryClaGroupID),
- "repositoryProjectSFID": utils.StringValue(params.GitlabAddRepository.RepositoryProjectSfid),
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesAddProjectGitLabRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "organizationName": utils.StringValue(params.GitlabRepositoriesAdd.GitlabOrganizationName),
+ "claGroupID": utils.StringValue(params.GitlabRepositoriesAdd.ClaGroupID),
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
@@ -433,19 +429,16 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_repositories.NewAddProjectGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- // If no repository GitLab ID values provided...
- // RepositoryGitlabID - provided by the older retool UI which provides only one value
- // RepositoryGitlabIds - provided by new PCC which passes multiple values
- if params.GitlabAddRepository.RepositoryExternalID == nil {
- msg := "missing repository GitLab ID value"
+ if len(params.GitlabRepositoriesAdd.RepositoryGitlabIds) == 0 {
+ msg := "missing repository GitLab ID values"
return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
log.WithFields(f).Debugf("Adding GitLab repository for project: %s", params.ProjectSFID)
- result, err := service.GitLabAddRepository(ctx, params.ProjectSFID, params.GitlabAddRepository)
+ result, err := service.GitLabAddRepositories(ctx, params.ProjectSFID, params.GitlabRepositoriesAdd)
if err != nil {
if _, ok := err.(*utils.GitLabRepositoryExists); ok {
- msg := fmt.Sprintf("unable to add repository - repository with name: %s already exists for projectSFID: %s", utils.StringValue(params.GitlabAddRepository.RepositoryName), params.ProjectSFID)
+ msg := fmt.Sprintf("unable to add repository - repository already exists for projectSFID: %s, err: %+v", params.ProjectSFID, err)
log.WithFields(f).WithError(err).Warn(msg)
return gitlab_repositories.NewAddProjectGitLabRepositoryConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
}
@@ -454,17 +447,6 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- // Log the event
- eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryAdded,
- ProjectSFID: params.ProjectSFID,
- CLAGroupID: utils.StringValue(params.GitlabAddRepository.RepositoryClaGroupID),
- LfUsername: authUser.UserName,
- EventData: &events.RepositoryAddedEventData{
- RepositoryName: utils.StringValue(params.GitlabAddRepository.RepositoryName),
- },
- })
-
return gitlab_repositories.NewAddProjectGitLabRepositoryOK().WithPayload(result)
})
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index f21a107d3..c246b9c7d 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -6,7 +6,6 @@ package repositories
import (
"context"
"fmt"
- "strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
@@ -14,7 +13,6 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
repoModels "github.com/communitybridge/easycla/cla-backend-go/repositories"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -31,7 +29,7 @@ type RepositoryInterface interface {
GitHubGetRepositoriesByCLAGroupDisabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByOrganizationName(ctx context.Context, orgName string) ([]*repoModels.RepositoryDBModel, error)
- GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*repoModels.RepositoryDBModel, error)
+ GitLabAddRepository(ctx context.Context, projectSFID string, input *repoModels.RepositoryDBModel) (*repoModels.RepositoryDBModel, error)
GitLabEnableRepositoryByID(ctx context.Context, repositoryID string) error
GitLabDisableRepositoryByID(ctx context.Context, repositoryID string) error
GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
@@ -225,23 +223,23 @@ func (r *Repository) GitHubGetRepositoriesByOrganizationName(ctx context.Context
}
// GitLabAddRepository creates a new entry in the repositories table using the specified input parameters
-func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*repoModels.RepositoryDBModel, error) {
+func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string, input *repoModels.RepositoryDBModel) (*repoModels.RepositoryDBModel, error) {
f := logrus.Fields{
"functionName": "v2.repositories.repositories.GitHubAddRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
- "repositoryExternalID": utils.Int64Value(input.RepositoryExternalID),
- "repositoryURL": utils.StringValue(input.RepositoryURL),
- "repositoryName": utils.StringValue(input.RepositoryName),
- "repositoryFullPath": utils.StringValue(input.RepositoryFullPath),
+ "repositoryExternalID": input.RepositoryExternalID,
+ "repositoryURL": input.RepositoryURL,
+ "repositoryName": input.RepositoryName,
+ "repositoryFullPath": input.RepositoryFullPath,
"repositoryType": utils.GitLabLower,
- "repositoryCLAGroupID": utils.StringValue(input.RepositoryClaGroupID),
- "repositoryProjectSFID": utils.StringValue(input.RepositoryProjectSfid),
- "repositoryOrganizationName": utils.StringValue(input.RepositoryOrganizationName),
+ "repositoryCLAGroupID": input.RepositoryCLAGroupID,
+ "repositoryProjectSFID": input.RepositorySfdcID,
+ "repositoryOrganizationName": input.RepositoryOrganizationName,
}
// Check first to see if the repository already exists
- _, err := r.GitLabGetRepositoryByName(ctx, utils.StringValue(input.RepositoryName))
+ _, err := r.GitLabGetRepositoryByName(ctx, input.RepositoryName)
if err != nil {
// Expecting Not found - no issue if not found - all other error we throw
if _, ok := err.(*utils.GitLabRepositoryNotFound); !ok {
@@ -249,7 +247,7 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
}
} else {
return nil, &utils.GitLabRepositoryExists{
- Message: fmt.Sprintf("GitLab repository with name: %s has alerady been registered", utils.StringValue(input.RepositoryName)),
+ Message: fmt.Sprintf("GitLab repository with name: %s has alerady been registered", input.RepositoryName),
RepositoryName: "",
Err: nil,
}
@@ -261,27 +259,13 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
return nil, err
}
- // Convert int64* to string
- repositoryExternalIDString := strconv.FormatInt(utils.Int64Value(input.RepositoryExternalID), 10)
-
- repository := &repoModels.RepositoryDBModel{
- RepositoryID: repoID.String(), // internal ID that we assign
- RepositorySfdcID: projectSFID,
- ProjectSFID: projectSFID,
- DateCreated: currentTime,
- DateModified: currentTime,
- RepositoryExternalID: repositoryExternalIDString,
- RepositoryName: utils.StringValue(input.RepositoryName),
- RepositoryFullPath: utils.StringValue(input.RepositoryFullPath),
- RepositoryURL: utils.StringValue(input.RepositoryURL),
- RepositoryOrganizationName: utils.StringValue(input.RepositoryOrganizationName), // gitlab group/organization
- RepositoryCLAGroupID: utils.StringValue(input.RepositoryClaGroupID),
- RepositoryType: utils.GitLabLower, // should always be gitlab
- Enabled: input.Enabled, // default is enabled
- Note: fmt.Sprintf("created on %s", currentTime),
- Version: "v1",
- }
- av, err := dynamodbattribute.MarshalMap(repository)
+ input.RepositoryID = repoID.String()
+ input.DateCreated = currentTime
+ input.DateModified = currentTime
+ input.Note = fmt.Sprintf("created on %s", currentTime)
+ input.Version = "v1"
+
+ av, err := dynamodbattribute.MarshalMap(input)
if err != nil {
log.WithFields(f).Warnf("problem marshalling the input, error: %+v", err)
return nil, err
@@ -296,7 +280,7 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
return nil, err
}
- return repository, nil
+ return input, nil
}
// GitLabEnableRepositoryByID enables the specified repository
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index bf4619c2e..34cf4225c 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -9,10 +9,13 @@ import (
"fmt"
"strconv"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
+
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
"github.com/communitybridge/easycla/cla-backend-go/config"
- gitlab2 "github.com/communitybridge/easycla/cla-backend-go/gitlab"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
@@ -52,21 +55,25 @@ type ServiceInterface interface {
GitLabGetRepository(ctx context.Context, repositoryID string) (*v2Models.GitlabRepository, error)
GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*v2Models.GitlabRepository, error)
- GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabListRepositories, error)
- GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabListRepositories, error)
- GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabListRepositories, error)
- GitLabAddRepository(ctx context.Context, projectSFID string, input *v2Models.GitlabAddRepository) (*v2Models.GitlabRepository, error)
+ GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error)
+ GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabRepositoriesList, error)
+ GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error)
+ GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesAdd) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
GitLabEnableRepository(ctx context.Context, repositoryID string) error
GitLabDisableRepository(ctx context.Context, repositoryID string) error
GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
}
-// GithubOrgRepo provide method to get GitHub organization by name
+// GithubOrgRepo redefine the interface here to avoid circular dependency issues
type GithubOrgRepo interface {
- GetGitHubOrganizationByName(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganizations, error)
- GetGitHubOrganization(ctx context.Context, githubOrganizationName string) (*v1Models.GithubOrganization, error)
- GetGitHubOrganizations(ctx context.Context, projectSFID string) (*v1Models.GithubOrganizations, error)
+ AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *v2Models.GitlabCreateOrganization) (*v2Models.GitlabOrganization, error)
+ GetGitlabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
+ GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
+ UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
+ UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
+ DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
// Service is the service model/structure
@@ -74,8 +81,10 @@ type Service struct {
gitV1Repository v1Repositories.RepositoryInterface
gitV2Repository RepositoryInterface
projectsClaGroupsRepo projects_cla_groups.Repository
- ghOrgRepo GithubOrgRepo
- gitLabApp *gitlab2.App
+ ghOrgRepo github_organizations.RepositoryInterface
+ glOrgRepo GithubOrgRepo
+ gitLabApp *gitlab_api.App
+ eventService events.Service
}
var (
@@ -85,13 +94,15 @@ var (
)
// NewService creates a new githubOrganizations service
-func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository RepositoryInterface, pcgRepo projects_cla_groups.Repository, ghOrgRepo GithubOrgRepo) ServiceInterface {
+func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository RepositoryInterface, pcgRepo projects_cla_groups.Repository, ghOrgRepo github_organizations.RepositoryInterface, glOrgRepo GithubOrgRepo, eventService events.Service) ServiceInterface {
return &Service{
gitV1Repository: gitV1Repository,
gitV2Repository: gitV2Repository,
projectsClaGroupsRepo: pcgRepo,
ghOrgRepo: ghOrgRepo,
- gitLabApp: gitlab2.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
+ glOrgRepo: glOrgRepo,
+ eventService: eventService,
+ gitLabApp: gitlab_api.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
@@ -143,9 +154,6 @@ func (s *Service) GitHubAddRepositories(ctx context.Context, projectSFID string,
log.WithFields(f).WithError(err).Warn("unable to get organization by name")
return nil, err
}
- if len(org.List) == 0 {
- return nil, errors.New("github app not installed on github organization")
- }
// Updated to process a list of repository IDs - take the list (may be empty) and add the single repository GH ID if it was set
repositoryIDList := input.RepositoryGithubIds
@@ -551,13 +559,16 @@ func (s *Service) getBranchProtectionRepositoryForOrgName(ctx context.Context, g
"githubOrgName": githubOrgName,
}
- githubOrg, err := s.ghOrgRepo.GetGitHubOrganization(ctx, githubOrgName)
+ githubOrg, err := s.ghOrgRepo.GetGitHubOrganizationByName(ctx, githubOrgName)
if err != nil {
log.WithFields(f).Warnf("fetching githubOrg %s failed, error: %v", githubOrgName, err)
return nil, err
}
+ if len(githubOrg.List) == 0 {
+ return nil, errors.New("github app not installed on github organization")
+ }
- branchProtectionRepo, err := branch_protection.NewBranchProtectionRepository(githubOrg.OrganizationInstallationID, branch_protection.EnableNonBlockingLimiter())
+ branchProtectionRepo, err := branch_protection.NewBranchProtectionRepository(githubOrg.List[0].OrganizationInstallationID, branch_protection.EnableNonBlockingLimiter())
if err != nil {
return nil, err
}
From 482e3eceae0d8fb19fa0af5b42c7441261115f46 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 18 Aug 2021 18:58:11 -0700
Subject: [PATCH 0434/1276] Added Context Helper Functions (#3170)
- Added helper functions for adding authUser to the context and fetching the values
Signed-off-by: David Deal
---
cla-backend-go/tests/utils_context_test.go | 38 ++++++++++++++
cla-backend-go/utils/context.go | 52 +++++++++++++++++++
.../v2/gitlab_organizations/handlers.go | 9 ++--
.../v2/repositories/gitlab_services.go | 3 +-
cla-backend-go/v2/repositories/handlers.go | 17 +++---
5 files changed, 103 insertions(+), 16 deletions(-)
create mode 100644 cla-backend-go/tests/utils_context_test.go
diff --git a/cla-backend-go/tests/utils_context_test.go b/cla-backend-go/tests/utils_context_test.go
new file mode 100644
index 000000000..70f4c864d
--- /dev/null
+++ b/cla-backend-go/tests/utils_context_test.go
@@ -0,0 +1,38 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "context"
+ "testing"
+
+ "github.com/LF-Engineering/lfx-kit/auth"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+// TestGetUserNameFromContext is a test for the GetUserNameFromContext
+func TestGetUserNameFromContext(t *testing.T) {
+ reqID := "foo123"
+ authUser := &auth.User{
+ UserName: "ddeal1",
+ Email: "ddeal1@foo.com",
+ ACL: auth.ACL{},
+ }
+ ctx := utils.ContextWithRequestAndUser(context.Background(), reqID, authUser) // nolint
+ assert.Equal(t, "ddeal1", utils.GetUserNameFromContext(ctx))
+}
+
+// TestGetUserEmailFromContext is a test for the GetUserNameFromContext
+func TestGetUserEmailFromContext(t *testing.T) {
+ reqID := "foo566"
+ authUser := &auth.User{
+ UserName: "ddeal2",
+ Email: "ddeal2@foo.com",
+ ACL: auth.ACL{},
+ }
+ ctx := utils.ContextWithRequestAndUser(context.Background(), reqID, authUser) // nolint
+ assert.Equal(t, "ddeal2@foo.com", utils.GetUserEmailFromContext(ctx))
+}
diff --git a/cla-backend-go/utils/context.go b/cla-backend-go/utils/context.go
index e2283ffe1..e1be4a955 100644
--- a/cla-backend-go/utils/context.go
+++ b/cla-backend-go/utils/context.go
@@ -6,6 +6,8 @@ package utils
import (
"context"
+ "github.com/LF-Engineering/lfx-kit/auth"
+
"github.com/sirupsen/logrus"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -25,3 +27,53 @@ func NewContext() context.Context {
return context.WithValue(context.Background(), XREQUESTID, requestID.String()) // nolint
}
+
+// NewContextWithUser returns a new context with a newly generated request ID and the specified user
+func NewContextWithUser(authUser *auth.User) context.Context {
+ f := logrus.Fields{
+ "functionName": "utils.NewContextWithUser",
+ }
+ requestID, err := uuid.NewV4()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to generate a UUID for x-request-id")
+ return context.Background()
+ }
+
+ return context.WithValue(context.WithValue(context.Background(), XREQUESTID, requestID), "authUser", authUser) // nolint
+}
+
+// ContextWithRequestAndUser returns a new context with the specified request ID and user
+func ContextWithRequestAndUser(ctx context.Context, reqID string, authUser *auth.User) context.Context {
+ return context.WithValue(context.WithValue(ctx, XREQUESTID, reqID), "authUser", authUser) // nolint
+}
+
+// ContextWithUser returns a new context with the specified user
+func ContextWithUser(ctx context.Context, authUser *auth.User) context.Context {
+ return context.WithValue(ctx, "authUser", authUser) // nolint
+}
+
+// GetUserNameFromContext returns the user's name from the context
+func GetUserNameFromContext(ctx context.Context) string {
+ val := ctx.Value(CtxAuthUser)
+ if val != nil {
+ authUser := val.(*auth.User) // nolint
+ if authUser != nil {
+ return authUser.UserName
+ }
+ }
+
+ return ""
+}
+
+// GetUserEmailFromContext returns the user's email from the context
+func GetUserEmailFromContext(ctx context.Context) string {
+ val := ctx.Value(CtxAuthUser)
+ if val != nil {
+ authUser := val.(*auth.User) // nolint
+ if authUser != nil {
+ return authUser.Email
+ }
+ }
+
+ return ""
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 1fa4d713b..55e4dbdbd 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -4,7 +4,6 @@
package gitlab_organizations
import (
- "context"
"errors"
"fmt"
"net/http"
@@ -39,7 +38,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_organizations.GetProjectGitlabOrganizationsParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsGetProjectGitlabOrganizationsHandler",
@@ -87,7 +86,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_organizations.AddProjectGitlabOrganizationParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsAddProjectGitlabOrganizationHandler",
@@ -162,7 +161,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
api.GitlabOrganizationsUpdateProjectGitlabOrganizationConfigHandler = gitlab_organizations.UpdateProjectGitlabOrganizationConfigHandlerFunc(func(params gitlab_organizations.UpdateProjectGitlabOrganizationConfigParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
- ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
if params.Body.AutoEnabled == nil {
return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
Code: "400",
@@ -202,7 +201,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
api.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler = gitlab_organizations.DeleteProjectGitlabOrganizationHandlerFunc(func(params gitlab_organizations.DeleteProjectGitlabOrganizationParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 1ba103000..90d2210cf 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -9,7 +9,6 @@ import (
"fmt"
"strconv"
- "github.com/LF-Engineering/lfx-kit/auth"
"github.com/communitybridge/easycla/cla-backend-go/events"
v2GitLabOrg "github.com/communitybridge/easycla/cla-backend-go/v2/common"
@@ -79,7 +78,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
EventType: events.RepositoryAdded,
ProjectSFID: projectSFID,
CLAGroupID: utils.StringValue(input.ClaGroupID),
- LfUsername: ctx.Value(utils.CtxAuthUser).(*auth.User).UserName,
+ LfUsername: utils.GetUserNameFromContext(ctx),
EventData: &events.RepositoryAddedEventData{
RepositoryName: project.PathWithNamespace, // give the full path/name
},
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 79760d51b..b919a9c37 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -4,7 +4,6 @@
package repositories
import (
- "context"
"errors"
"fmt"
"strings"
@@ -36,7 +35,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params github_repositories.GetProjectGithubRepositoriesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitHubRepositoriesGetProjectGithubRepositoriesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -84,7 +83,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params github_repositories.AddProjectGithubRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitHubRepositoriesAddProjectGithubRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -166,7 +165,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params github_repositories.DeleteProjectGithubRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitHubRepositoriesDeleteProjectGithubRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -224,7 +223,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params github_repositories.GetProjectGithubRepositoryBranchProtectionParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitHubRepositoriesGetProjectGithubRepositoryBranchProtectionHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -285,7 +284,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params github_repositories.UpdateProjectGithubRepositoryBranchProtectionParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitHubRepositoriesUpdateProjectGitHubRepositoryBranchProtectionHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -367,7 +366,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_repositories.GetProjectGitLabRepositoriesParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitlabRepositoriesGetProjectGitLabRepositoriesHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -411,7 +410,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_repositories.AddProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID), "authUser", authUser) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitlabRepositoriesAddProjectGitLabRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -454,7 +453,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
func(params gitlab_repositories.DeleteProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqID) // nolint
+ ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
"functionName": "v2.repositories.handlers.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
From 8739e342d2c87f92e94d9d33b2cd665ef73b0cc6 Mon Sep 17 00:00:00 2001
From: Mike Dolan
Date: Thu, 19 Aug 2021 13:45:22 -0400
Subject: [PATCH 0435/1276] Prioritize link visually in email to CLA Managers
(#3121)
Co-authored-by: David Deal
---
cla-backend-go/emails/v2_cla_manager_templates.go | 2 +-
cla-backend-go/tests/v2_cla_manager_templates_test.go | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/cla-backend-go/emails/v2_cla_manager_templates.go b/cla-backend-go/emails/v2_cla_manager_templates.go
index db23d5730..28b91acd0 100644
--- a/cla-backend-go/emails/v2_cla_manager_templates.go
+++ b/cla-backend-go/emails/v2_cla_manager_templates.go
@@ -139,7 +139,7 @@ const (
{{.Project.ExternalProjectName}}
Before the contribution can be accepted, your organization must sign a CLA.
-Either you or someone whom you designate from your company can login to this portal ({{.CorporateConsole}}) and sign the CLA for this project {{.Project.GetProjectFullURL}}
+Either you or someone whom you designate from your company can login and sign the CLA for this project {{.Project.GetProjectFullURL}}
If you are not the CLA Manager, please forward this email to the appropriate person so that they can start the CLA process.
Please notify the user once CLA setup is complete.
`
diff --git a/cla-backend-go/tests/v2_cla_manager_templates_test.go b/cla-backend-go/tests/v2_cla_manager_templates_test.go
index 21c655640..d22994121 100644
--- a/cla-backend-go/tests/v2_cla_manager_templates_test.go
+++ b/cla-backend-go/tests/v2_cla_manager_templates_test.go
@@ -138,8 +138,7 @@ func TestV2CLAManagerDesigneeCorporateTemplate(t *testing.T) {
assert.Contains(t, result, "SenderNameValue SenderEmailValue has identified you")
assert.Contains(t, result, "Corporate CLA for the organization JohnsCompany")
assert.Contains(t, result, "
JohnsProject
")
- assert.Contains(t, result, "can login to this portal (http://CorporateConsole.com)")
- assert.Contains(t, result, `sign the CLA for this project JohnsProject`)
+ assert.Contains(t, result, "can login and sign the CLA for this project JohnsProject")
}
func TestV2ToCLAManagerDesigneeTemplate(t *testing.T) {
From b41dc6cc54ba4b16ea545caf19895fb89b7c686e Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 14:00:42 -0700
Subject: [PATCH 0436/1276] Updated GitLab Onboarding API (#3172)
---
cla-backend-go/gitlab_api/client.go | 12 ++
cla-backend-go/gitlab_api/client_groups.go | 85 +++++++-
cla-backend-go/gitlab_api/client_projects.go | 2 +-
.../common/gitlab-organization-create.yaml | 31 +--
.../common/gitlab-project-organization.yaml | 5 +
cla-backend-go/tests/gitlab_client_test.go | 122 +++++++++--
.../v2/gitlab_organizations/handlers.go | 29 +--
.../v2/gitlab_organizations/repository.go | 203 ++++++++++++++----
.../v2/gitlab_organizations/service.go | 27 ++-
.../v2/repositories/gitlab_services.go | 6 +-
cla-backend-go/v2/repositories/service.go | 6 +-
11 files changed, 426 insertions(+), 102 deletions(-)
diff --git a/cla-backend-go/gitlab_api/client.go b/cla-backend-go/gitlab_api/client.go
index f976eb857..9566aefeb 100644
--- a/cla-backend-go/gitlab_api/client.go
+++ b/cla-backend-go/gitlab_api/client.go
@@ -10,6 +10,7 @@ import (
"encoding/base64"
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"io"
@@ -29,11 +30,22 @@ type OauthSuccessResponse struct {
// NewGitlabOauthClient creates a new gitlab client from the given oauth info, authInfo is encrypted
func NewGitlabOauthClient(authInfo string, gitLabApp *App) (*goGitLab.Client, error) {
+ if authInfo == "" {
+ return nil, errors.New("unable to decrypt auth info - authentication info input is nil")
+ }
+ if gitLabApp == nil || gitLabApp.gitLabAppID == "" || gitLabApp.gitLabAppPrivateKey == "" || gitLabApp.gitLabAppSecret == "" {
+ return nil, errors.New("unable to decrypt auth info - GitLab app structure is nil or empty")
+ }
+
oauthResp, err := DecryptAuthInfo(authInfo, gitLabApp)
if err != nil {
return nil, err
}
+ if oauthResp == nil {
+ return nil, errors.New("unable to decrypt auth info - value is nil")
+ }
+
log.Infof("creating oauth client with access token : %s", oauthResp.AccessToken)
return goGitLab.NewOAuthClient(oauthResp.AccessToken)
}
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
index 7f2dda5d7..c64a5f2f3 100644
--- a/cla-backend-go/gitlab_api/client_groups.go
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -73,22 +73,96 @@ func GetGroupByName(ctx context.Context, client *goGitLab.Client, name string) (
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
- groups, _, err := client.Groups.ListGroups(&goGitLab.ListGroupsOptions{})
+ groups, resp, err := client.Groups.SearchGroup(name)
+ //groups, _, err := client.Groups.ListGroups(&goGitLab.ListGroupsOptions{})
if err != nil {
msg := fmt.Sprintf("problem fetching groups, error: %+v", err)
log.WithFields(f).WithError(err).Warn(msg)
return nil, errors.New(msg)
}
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to search groups using query: %s, status code: %d", name, resp.StatusCode)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
for _, group := range groups {
+ log.WithFields(f).Debugf("testing %s == %s or %s", name, group.Name, group.FullPath)
if group.Name == name {
return group, nil
}
+ if group.FullPath == name {
+ return group, nil
+ }
}
return nil, nil
}
+// GetGroupByID gets a gitlab Group by the given name
+func GetGroupByID(ctx context.Context, client *goGitLab.Client, groupID int) (*goGitLab.Group, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.GetGroupByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ group, resp, err := client.Groups.GetGroup(groupID)
+ if err != nil {
+ msg := fmt.Sprintf("problem fetching group by ID: %d, error: %+v", groupID, err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to find group by ID: %d, status code: %d", groupID, resp.StatusCode)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ return group, nil
+}
+
+// GetGroupProjectListByGroupID returns a list of GitLab projects under the specified Organization
+func GetGroupProjectListByGroupID(ctx context.Context, client *goGitLab.Client, groupID int) ([]*goGitLab.Project, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.GetGroupProjectListByGroupID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+ opts := &goGitLab.ListGroupProjectsOptions{
+ ListOptions: goGitLab.ListOptions{
+ Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
+ PerPage: 100, // max is 100
+ },
+ IncludeSubgroups: utils.Bool(true), // Include projects in subgroups of this group. Default is false
+ MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
+ }
+
+ var projectList []*goGitLab.Project
+ for {
+ // https://docs.gitlab.com/ee/api/groups.html#list-a-groups-projects
+ projects, resp, listProjectsErr := client.Groups.ListGroupProjects(groupID, opts)
+ if listProjectsErr != nil {
+ msg := fmt.Sprintf("unable to list projects, error: %+v", listProjectsErr)
+ log.WithFields(f).WithError(listProjectsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to list projects, status code: %d", resp.StatusCode)
+ log.WithFields(f).WithError(listProjectsErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ // Append to our response
+ projectList = append(projectList, projects...)
+
+ // Do we have any records to process?
+ if resp.NextPage == 0 {
+ break
+ }
+ }
+
+ return projectList, 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(ctx context.Context, client *goGitLab.Client, userID int) ([]*UserGroup, error) {
@@ -107,7 +181,14 @@ func ListUserProjectGroups(ctx context.Context, client *goGitLab.Client, userID
log.WithFields(f).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)
+ msg := fmt.Sprintf("listing user : %d projects failed : %v", userID, err)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ msg := fmt.Sprintf("unable to list user projects using userID: %d, status code: %d", userID, resp.StatusCode)
+ log.WithFields(f).Warn(msg)
+ return nil, errors.New(msg)
}
log.Debugf("fetched %d projects for the user ", len(projects))
diff --git a/cla-backend-go/gitlab_api/client_projects.go b/cla-backend-go/gitlab_api/client_projects.go
index 9e7c6d654..5cb7cbf49 100644
--- a/cla-backend-go/gitlab_api/client_projects.go
+++ b/cla-backend-go/gitlab_api/client_projects.go
@@ -53,7 +53,7 @@ func GetProjectListByOrgName(ctx context.Context, client *goGitLab.Client, organ
// getProjectListWithOptions returns a list of GitLab projects using the specified filter
func getProjectListWithOptions(ctx context.Context, client *goGitLab.Client, opts *goGitLab.ListProjectsOptions) ([]*goGitLab.Project, error) {
f := logrus.Fields{
- "functionName": "gitlab.client.getProjectListWithOptions",
+ "functionName": "gitlab_api.client_projects.getProjectListWithOptions",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
diff --git a/cla-backend-go/swagger/common/gitlab-organization-create.yaml b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
index 0a9ea81c4..48384010b 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-create.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
@@ -3,25 +3,30 @@
type: object
required:
- - organizationName
+ - group_id
properties:
- organizationName:
- type: string
- description: The GitLab Group/Organization name
- example: "kubernetes"
- # Pattern aligns with UI and other platform services including Org Service
- # \w Any word character (alphanumeric & underscore), dashes, periods
- pattern: '^([\w\-\.]+){2,255}$'
- minLength: 2
- maxLength: 255
- autoEnabled:
+# organization_name:
+# type: string
+# description: The GitLab Group/Organization name
+# example: "kubernetes"
+# # Pattern aligns with UI and other platform services including Org Service
+# # \w Any word character (alphanumeric & underscore), dashes, periods
+# pattern: '^([\w\-\.]+){2,255}$'
+# minLength: 2
+# maxLength: 255
+ group_id:
+ type: integer
+ description: The GitLab Group ID
+ example: 13050017
+ minimum: 1
+ auto_enabled:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
default: false
- autoEnabledClaGroupID:
+ auto_enabled_cla_group_id:
type: string
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
- branchProtectionEnabled:
+ branch_protection_enabled:
type: boolean
description: Flag to indicate if this GitLab Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
default: false
diff --git a/cla-backend-go/swagger/common/gitlab-project-organization.yaml b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
index badacff1b..f33b7fcf5 100644
--- a/cla-backend-go/swagger/common/gitlab-project-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
@@ -39,6 +39,11 @@ properties:
type: string
description: The Gitlab Group/Organization full path
example: "linuxfoundation/product/easycla"
+ organization_external_id:
+ type: integer
+ description: The Gitlab Group/Organization external ID used by GitLab
+ example: 13050017
+ minimum: 1
connection_status:
type: string
enum:
diff --git a/cla-backend-go/tests/gitlab_client_test.go b/cla-backend-go/tests/gitlab_client_test.go
index 46eda1e9e..8303f9cbd 100644
--- a/cla-backend-go/tests/gitlab_client_test.go
+++ b/cla-backend-go/tests/gitlab_client_test.go
@@ -4,10 +4,8 @@
package tests
import (
- "encoding/json"
"fmt"
"io"
- "net/url"
"os"
"testing"
@@ -21,14 +19,13 @@ import (
"github.com/xanzy/go-gitlab"
)
-const enabled = false // nolint
-const group = "The Linux Foundation/product/EasyCLA"
+const enabled = false // nolint
+const group = "The Linux Foundation/product/EasyCLA" // nolint
const accessInfo = ""
-const easyCLAGroupName = "linuxfoundation/product/easycla"
-
-func TestGitLabGetGroup(t *testing.T) { // no lint
+const easyCLAGroupName = "linuxfoundation/product/easycla" // nolint
+func TestGetGroupByName(t *testing.T) { // no lint
if enabled { // nolint
// Need to initialize the system to load the configuration which contains a number of SSM parameters
stage := os.Getenv("STAGE")
@@ -58,14 +55,93 @@ func TestGitLabGetGroup(t *testing.T) { // no lint
assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
+ ctx := utils.NewContext()
// Need to look up the GitLab Group/Organization to obtain the ID
- groupModel, resp, getError := gitLabClient.Groups.GetGroup(url.QueryEscape(easyCLAGroupName))
- assert.Nil(t, getError, "GitLab GetGroup Error is Nil")
- if resp.StatusCode < 200 || resp.StatusCode > 299 {
- assert.Fail(t, fmt.Sprintf("unable to locate GitLab group by value: %s, status code: %d", easyCLAGroupName, resp.StatusCode))
+ //groupModel, getError := gitlab_api.GetGroupByName(ctx, gitLabClient, easyCLAGroupName)
+ //groupModel, getError := gitlab_api.GetGroupByName(ctx, gitLabClient, "EasyCLA")
+ groupModel, getError := gitlab_api.GetGroupByName(ctx, gitLabClient, "linuxfoundation/product/asitha")
+ assert.Nil(t, getError, "GitLab GetGroup Error should be nil", getError)
+ assert.NotNil(t, groupModel, "Group Model should not be nil")
+ t.Logf("group ID: %d, name: %s, path: %s, full path: %s", groupModel.ID, groupModel.Name, groupModel.Path, groupModel.FullPath)
+ }
+}
+
+func TestGetGroupByID(t *testing.T) { // no lint
+ if enabled { // nolint
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
+
+ ctx := utils.NewContext()
+ groupModel, getError := gitlab_api.GetGroupByID(ctx, gitLabClient, 13050017)
+ assert.Nil(t, getError, "GitLab GetGroup Error should be nil", getError)
+ assert.NotNil(t, groupModel, "Group Model should not be nil")
+ t.Logf("group ID: %d, name: %s, path: %s, full path: %s", groupModel.ID, groupModel.Name, groupModel.Path, groupModel.FullPath)
+ }
+}
+
+func TestGetGroupProjectListByGroupID(t *testing.T) { // no lint
+ if enabled { // nolint
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
+
+ ctx := utils.NewContext()
+ gitLabProjects, getError := gitlab_api.GetGroupProjectListByGroupID(ctx, gitLabClient, 13050017)
+ assert.Nil(t, getError, "Get Group Projects List by Group ID error should be nil", getError)
+ assert.NotNil(t, gitLabProjects, "Get Group Projects Array should not be nil")
+ assert.Greaterf(t, len(gitLabProjects), 0, "Get Group Projects Array greater than zero: %d", len(gitLabProjects))
+ for _, p := range gitLabProjects {
+ t.Logf("id: %d, name: %s, web url: %s, path: %s, full path: %s", p.ID, p.Name, p.WebURL, p.Path, p.PathWithNamespace)
}
- assert.NotNil(t, groupModel, "Group Model is not nil")
- t.Logf("group name: %s, ID: %d, path: %s", groupModel.Name, groupModel.ID, groupModel.Path)
}
}
@@ -193,18 +269,18 @@ func TestGitLabListProjects(t *testing.T) { // no lint
}
// DEBUG
- t.Logf("Recevied %d projects", len(projects))
- for _, p := range projects {
- t.Logf("project name: %s, ID: %d, path: %s", p.Name, p.ID, p.PathWithNamespace)
- }
+ //t.Logf("Recevied %d projects", len(projects))
+ //for _, p := range projects {
+ // t.Logf("project name: %s, ID: %d, path: %s", p.Name, p.ID, p.PathWithNamespace)
+ //}
// DEBUG
- t.Log("projects:")
- for _, p := range projects {
- byteArr, err := json.Marshal(p)
- assert.Nil(t, err)
- t.Logf("project: %s", byteArr)
- }
+ //t.Log("projects:")
+ //for _, p := range projects {
+ //byteArr, err := json.Marshal(p)
+ //assert.Nil(t, err)
+ //t.Logf("project: %s", byteArr)
+ //}
if len(projects) > 1 {
assert.Fail(t, fmt.Sprintf("expecting > 1 result for GitLab list projects, found: %d - %+v", len(projects), projects))
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 55e4dbdbd..643824cdb 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -113,13 +113,13 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Quick check of the parameters
- if params.Body == nil || params.Body.OrganizationName == nil {
- msg := fmt.Sprintf("missing organization name in body: %+v", params.Body)
+ if params.Body == nil || params.Body.GroupID == nil {
+ msg := fmt.Sprintf("missing group ID in body: %+v", params.Body)
log.WithFields(f).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
utils.ErrorResponseBadRequest(reqID, msg))
}
- f["organizationName"] = utils.StringValue(params.Body.OrganizationName)
+ f["groupID"] = utils.Int64Value(params.Body.GroupID)
if params.Body.AutoEnabled == nil {
msg := fmt.Sprintf("missing autoEnabled name in body: %+v", params.Body)
@@ -146,15 +146,20 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- // Log the event
- eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- LfUsername: authUser.UserName,
- EventType: events.GitlabOrganizationAdded,
- ProjectSFID: params.ProjectSFID,
- EventData: &events.GitlabOrganizationAddedEventData{
- GitlabOrganizationName: *params.Body.OrganizationName,
- },
- })
+ // Get the current group name for the event
+ for _, group := range result.List {
+ if group.OrganizationExternalID == *params.Body.GroupID {
+ // Log the event
+ eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ LfUsername: authUser.UserName,
+ EventType: events.GitlabOrganizationAdded,
+ ProjectSFID: params.ProjectSFID,
+ EventData: &events.GitlabOrganizationAddedEventData{
+ GitlabOrganizationName: group.OrganizationName,
+ },
+ })
+ }
+ }
return gitlab_organizations.NewAddProjectGitlabOrganizationOK().WithPayload(result)
})
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 09dc1ac1b..14e119f7a 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -35,12 +35,14 @@ const (
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
- AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error)
+ AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
+ GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
+ UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
@@ -53,45 +55,46 @@ type Repository struct {
// NewRepository creates a new instance of the gitlabOrganizations repository
func NewRepository(awsSession *session.Session, stage string) RepositoryInterface {
- return Repository{
+ return &Repository{
stage: stage,
dynamoDBClient: dynamodb.New(awsSession),
gitlabOrgTableName: fmt.Sprintf("cla-%s-gitlab-orgs", stage),
}
}
-func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *models2.GitlabCreateOrganization) (*models2.GitlabOrganization, error) {
- gitLabOrganizationName := utils.StringValue(input.OrganizationName)
+func (repo *Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.AddGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFID": projectSFID,
- "organizationName": gitLabOrganizationName,
- "autoEnabled": utils.BoolValue(input.AutoEnabled),
- "branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
+ "groupID": groupID,
+ "organizationName": organizationName,
+ "autoEnabled": autoEnabled,
+ "autoEnabledClaGroupID": autoEnabledClaGroupID,
+ "branchProtectionEnabled": branchProtectionEnabled,
+ "enabled": enabled,
}
// First, let's check to see if we have an existing gitlab organization with the same name
- existingRecord, getErr := repo.GetGitlabOrganizationByName(ctx, utils.StringValue(input.OrganizationName))
+ existingRecord, getErr := repo.GetGitlabOrganizationByExternalID(ctx, groupID)
if getErr != nil {
- log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab organization by name %s - ok to create a new record", gitLabOrganizationName)
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab organization by name %d - ok to create a new record", groupID)
}
if existingRecord != nil {
- log.WithFields(f).Debugf("An existing GitLab organization with name %s exists in our database", gitLabOrganizationName)
+ log.WithFields(f).Debugf("An existing GitLab organization with name %d exists in our database", groupID)
// If everything matches...
if projectSFID == existingRecord.ProjectSFID {
log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
- enabledFlag := true
- updateErr := repo.UpdateGitlabOrganization(ctx, projectSFID, gitLabOrganizationName,
- utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled), &enabledFlag)
+ updateErr := repo.UpdateGitlabOrganizationByExternalID(ctx, projectSFID, groupID, organizationName,
+ autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, enabled)
if updateErr != nil {
return nil, updateErr
}
// Return the updated record
- if gitlabOrg, err := repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName); err != nil {
+ if gitlabOrg, err := repo.GetGitlabOrganizationByExternalID(ctx, groupID); err != nil {
return nil, err
} else {
return common.ToModel(gitlabOrg), nil
@@ -116,19 +119,19 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
return nil, err
}
- enabled := true
gitlabOrg := &common.GitLabOrganization{
OrganizationID: organizationID.String(),
DateCreated: currentTime,
DateModified: currentTime,
- OrganizationName: *input.OrganizationName,
- OrganizationNameLower: strings.ToLower(*input.OrganizationName),
+ OrganizationName: organizationName,
+ OrganizationNameLower: strings.ToLower(organizationName),
+ ExternalGroupID: int(groupID),
OrganizationSFID: parentProjectSFID,
ProjectSFID: projectSFID,
- Enabled: aws.BoolValue(&enabled),
- AutoEnabled: aws.BoolValue(input.AutoEnabled),
- AutoEnabledClaGroupID: input.AutoEnabledClaGroupID,
- BranchProtectionEnabled: aws.BoolValue(input.BranchProtectionEnabled),
+ Enabled: enabled,
+ AutoEnabled: autoEnabled,
+ AutoEnabledClaGroupID: autoEnabledClaGroupID,
+ BranchProtectionEnabled: branchProtectionEnabled,
AuthState: authStateNonce.String(),
Version: "v1",
// OrganizationURL: set later when we can authenticate to the API
@@ -163,7 +166,7 @@ func (repo Repository) AddGitlabOrganization(ctx context.Context, parentProjectS
}
// GetGitlabOrganizations get GitLab organizations based on the project SFID
-func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
+func (repo *Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.GetGitlabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -219,7 +222,7 @@ func (repo Repository) GetGitlabOrganizations(ctx context.Context, projectSFID s
}
// GetGitlabOrganizationByName get GitLab organization by name
-func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error) {
+func (repo *Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -267,8 +270,55 @@ func (repo Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOr
return resultOutput[0], nil
}
+func (repo *Repository) GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByExternalID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabGroupID": gitLabGroupID,
+ }
+
+ condition := expression.Key(GitLabOrganizationsExternalGitLabGroupIDColumn).Equal(expression.Value(gitLabGroupID))
+ builder := expression.NewBuilder().WithKeyCondition(condition)
+ // Use the nice builder to create the expression
+ expr, err := builder.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ IndexName: aws.String(GitlabOrgLowerNameIndex),
+ }
+
+ log.WithFields(f).Debugf("querying for GitLab organization by external group ID: %d...", gitLabGroupID)
+ results, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving gitlab_organizations using external ID = %d", gitLabGroupID)
+ return nil, err
+ }
+ if len(results.Items) == 0 {
+ log.WithFields(f).Debugf("Unable to find GitLab organization by group ID: %d - no results", gitLabGroupID)
+ return nil, nil
+ }
+
+ var resultOutput []*common.GitLabOrganization
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
+ if err != nil {
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ return nil, err
+ }
+
+ return resultOutput[0], nil
+}
+
// GetGitlabOrganization by organization name
-func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
+func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.GetGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -302,7 +352,7 @@ func (repo Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiza
}
// UpdateGitlabOrganizationAuth updates the specified Gitlab organization oauth info
-func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
+func (repo *Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -368,7 +418,8 @@ func (repo Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organiz
return nil
}
-func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error {
+// UpdateGitlabOrganization updates the GitLab group based on the specified values
+func (repo *Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.UpdateGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -396,6 +447,7 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
"#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
"#B": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
"#M": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#E": aws.String(GitLabOrganizationsEnabledColumn),
}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{
":a": {
@@ -410,16 +462,11 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
":m": {
S: aws.String(currentTime),
},
+ ":e": {
+ BOOL: aws.Bool(enabled),
+ },
}
- updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m"
-
- if enabled != nil {
- expressionAttributeNames["#E"] = aws.String("enabled")
- expressionAttributeValues[":e"] = &dynamodb.AttributeValue{
- BOOL: aws.Bool(*enabled),
- }
- updateExpression = updateExpression + ", #E = :e "
- }
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m, #E = :e"
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
@@ -443,7 +490,89 @@ func (repo Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID
return nil
}
-func (repo Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
+// UpdateGitlabOrganizationByExternalID updates the GitLab group based on the specified values
+func (repo *Repository) UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
+ f := logrus.Fields{
+ "functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationByExternalID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "groupID": groupID,
+ "organizationName": organizationName,
+ "autoEnabled": autoEnabled,
+ "autoEnabledClaGroupID": autoEnabledClaGroupID,
+ "branchProtectionEnabled": branchProtectionEnabled,
+ "tableName": repo.gitlabOrgTableName,
+ }
+
+ _, currentTime := utils.CurrentTime()
+ gitlabOrg, lookupErr := repo.GetGitlabOrganizationByExternalID(ctx, groupID)
+ if lookupErr != nil {
+ log.WithFields(f).Warnf("error looking up GitLab group by ID: %d, error: %+v", groupID, lookupErr)
+ return lookupErr
+ }
+ if gitlabOrg == nil {
+ log.WithFields(f).Warn("error looking up GitLab group - no results")
+ return errors.New("unable to lookup GitLab group by ID")
+ }
+
+ expressionAttributeNames := map[string]*string{
+ "#A": aws.String(GitLabOrganizationsAutoEnabledColumn),
+ "#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
+ "#B": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
+ "#N": aws.String(GitLabOrganizationsOrganizationNameColumn),
+ "#NL": aws.String(GitLabOrganizationsOrganizationNameLowerColumn),
+ "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#E": aws.String(GitLabOrganizationsEnabledColumn),
+ }
+ expressionAttributeValues := map[string]*dynamodb.AttributeValue{
+ ":a": {
+ BOOL: aws.Bool(autoEnabled),
+ },
+ ":c": {
+ S: aws.String(autoEnabledClaGroupID),
+ },
+ ":b": {
+ BOOL: aws.Bool(branchProtectionEnabled),
+ },
+ ":n": {
+ S: aws.String(organizationName),
+ },
+ ":nl": {
+ S: aws.String(strings.ToLower(organizationName)),
+ },
+ ":m": {
+ S: aws.String(currentTime),
+ },
+ ":e": {
+ BOOL: aws.Bool(enabled),
+ },
+ }
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #N = :n, #NL = :nl, #M = :m, #E = :e "
+
+ input := &dynamodb.UpdateItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ GitLabOrganizationsOrganizationIDColumn: {
+ S: aws.String(gitlabOrg.OrganizationID),
+ },
+ },
+ ExpressionAttributeNames: expressionAttributeNames,
+ ExpressionAttributeValues: expressionAttributeValues,
+ UpdateExpression: &updateExpression,
+ TableName: aws.String(repo.gitlabOrgTableName),
+ }
+
+ log.WithFields(f).Debugf("updating GitLab organization record: %+v", input)
+ _, updateErr := repo.dynamoDBClient.UpdateItem(input)
+ if updateErr != nil {
+ log.WithFields(f).Warnf("unable to update GitLab organization record, error: %+v", updateErr)
+ return updateErr
+ }
+
+ return nil
+}
+
+// DeleteGitlabOrganization deletes the specified GitLab organization
+func (repo *Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
f := logrus.Fields{
"functionName": "v1.gitlab_organizations.repository.DeleteGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index be847e2c9..adac34e2b 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -70,7 +70,7 @@ func (s *Service) AddGitlabOrganization(ctx context.Context, projectSFID string,
"projectSFID": projectSFID,
"autoEnabled": utils.BoolValue(input.AutoEnabled),
"branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
- "organizationName": utils.StringValue(input.OrganizationName),
+ "groupID": utils.Int64Value(input.GroupID),
}
psc := v2ProjectService.GetClient()
@@ -90,8 +90,17 @@ func (s *Service) AddGitlabOrganization(ctx context.Context, projectSFID string,
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
- log.WithFields(f).Debug("adding gitlab organization...")
- resp, err := s.repo.AddGitlabOrganization(ctx, parentProjectSFID, projectSFID, input)
+ log.WithFields(f).Debug("adding GitLab organization...")
+ autoEnabled := false
+ if input.AutoEnabled != nil {
+ autoEnabled = utils.BoolValue(input.AutoEnabled)
+ }
+ branchProtectionEnabled := false
+ if input.BranchProtectionEnabled != nil {
+ branchProtectionEnabled = utils.BoolValue(input.BranchProtectionEnabled)
+ }
+
+ resp, err := s.repo.AddGitlabOrganization(ctx, parentProjectSFID, projectSFID, *input.GroupID, "", autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem adding gitlab organization for project")
return nil, err
@@ -314,7 +323,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
return fmt.Errorf("gitlab organization lookup error: %+v", err)
}
- // Get the client
+ // Get a reference to the GItLab client
gitLabClient, err := gitlab_api.NewGitlabOauthClientFromAccessToken(oauthResp.AccessToken)
if err != nil {
return fmt.Errorf("initializing gitlab client : %v", err)
@@ -327,20 +336,20 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
}
for _, g := range groups {
- if g.Name == gitLabOrgModel.OrganizationName {
+ if g.FullPath == gitLabOrgModel.OrganizationFullPath {
updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
}
- log.WithFields(f).Debugf("fetching updated GitLab group/organization record")
- updatedDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ log.WithFields(f).Debugf("fetching updated GitLab group/organization record which should now have all the details")
+ updatedOrgDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
if getErr != nil {
return getErr
}
log.WithFields(f).Debugf("adding GitLab repositories for this group/organization")
- _, err = s.v2GitRepoService.GitLabAddRepositoriesByApp(ctx, updatedDBModel)
+ _, err = s.v2GitRepoService.GitLabAddRepositoriesByApp(ctx, updatedOrgDBModel)
if err != nil {
return err
}
@@ -361,7 +370,7 @@ func (s *Service) UpdateGitlabOrganization(ctx context.Context, projectSFID stri
}
}
- return s.repo.UpdateGitlabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, nil)
+ return s.repo.UpdateGitlabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
}
// DeleteGitlabOrganization deletes the specified GitLab organization
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 90d2210cf..7229a5fd0 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -64,7 +64,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositoryOrganizationName: utils.StringValue(input.GitlabOrganizationName), // gitlab group/organization
RepositoryCLAGroupID: utils.StringValue(input.ClaGroupID),
RepositoryType: utils.GitLabLower, // should always be gitlab
- Enabled: true,
+ Enabled: false, // we don't enable by default
}
_, addErr := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, inputDBModel)
@@ -110,7 +110,7 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
}
// Query the project list by organization name
- projectList, projectListErr := gitlab_api.GetProjectListByOrgName(ctx, gitLabClient, gitLabOrgModel.OrganizationName)
+ projectList, projectListErr := gitlab_api.GetGroupProjectListByGroupID(ctx, gitLabClient, gitLabOrgModel.ExternalGroupID)
if projectListErr != nil {
return nil, projectListErr
}
@@ -119,7 +119,7 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
// Build a list of project IDs
for _, proj := range projectList {
- log.WithFields(f).Debugf("repo: %s, path: %s, id: %d, weburl: %s", proj.Name, proj.Path, proj.ID, proj.WebURL)
+ log.WithFields(f).Debugf("id: %d, repo: %s, path: %s, full path: %s, weburl: %s", proj.ID, proj.Name, proj.Path, proj.PathWithNamespace, proj.WebURL)
listProjectIDs = append(listProjectIDs, int64(proj.ID))
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 34cf4225c..c01224d08 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -67,12 +67,14 @@ type ServiceInterface interface {
// GithubOrgRepo redefine the interface here to avoid circular dependency issues
type GithubOrgRepo interface {
- AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, input *v2Models.GitlabCreateOrganization) (*v2Models.GitlabOrganization, error)
+ AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
+ GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled *bool) error
+ UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
From 032707bc9fec24f10d3531118b4cb8adacf2cbfe Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 14:59:41 -0700
Subject: [PATCH 0437/1276] Addeed GitLab External ID to Response & Index
Update (#3173)
---
.../swagger/common/gitlab-organization.yaml | 5 ++
cla-backend-go/v2/common/models.go | 26 ++++-----
.../v2/gitlab_organizations/handlers.go | 18 +++----
.../v2/gitlab_organizations/repository.go | 14 ++---
.../v2/gitlab_organizations/service.go | 53 ++++++++++---------
5 files changed, 62 insertions(+), 54 deletions(-)
diff --git a/cla-backend-go/swagger/common/gitlab-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization.yaml
index faecadbee..1f577200f 100644
--- a/cla-backend-go/swagger/common/gitlab-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization.yaml
@@ -6,6 +6,11 @@ properties:
organization_id:
type: string
description: internal id of the gitlab organization
+ organization_external_id:
+ type: integer
+ description: The Gitlab Group/Organization external ID used by GitLab
+ example: 13050017
+ minimum: 1
date_created:
type: string
example: "2020-02-06T09:31:49.245630+0000"
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index 2e284076c..6fbd36291 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -31,19 +31,19 @@ type GitLabOrganization struct {
// ToModel converts to models.GitlabOrganization
func ToModel(in *GitLabOrganization) *models2.GitlabOrganization {
return &models2.GitlabOrganization{
- OrganizationID: in.OrganizationID,
- DateCreated: in.DateCreated,
- DateModified: in.DateModified,
- OrganizationName: in.OrganizationName,
- OrganizationFullPath: in.OrganizationFullPath,
- OrganizationURL: in.OrganizationURL,
- OrganizationSfid: in.OrganizationSFID,
- Version: in.Version,
- Enabled: in.Enabled,
- AutoEnabled: in.AutoEnabled,
- AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
- ProjectSfid: in.ProjectSFID,
- // Not exposing ExternalGroupID
+ OrganizationID: in.OrganizationID,
+ DateCreated: in.DateCreated,
+ DateModified: in.DateModified,
+ OrganizationName: in.OrganizationName,
+ OrganizationFullPath: in.OrganizationFullPath,
+ OrganizationURL: in.OrganizationURL,
+ OrganizationSfid: in.OrganizationSFID,
+ Version: in.Version,
+ Enabled: in.Enabled,
+ AutoEnabled: in.AutoEnabled,
+ AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
+ ProjectSfid: in.ProjectSFID,
+ OrganizationExternalID: int64(in.ExternalGroupID),
}
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 643824cdb..66d8e1ff9 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -268,20 +268,20 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
reqID := requestID.String()
if params.Code == "" {
msg := "missing code parameter"
- log.WithFields(f).Errorf(msg)
+ log.WithFields(f).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
if params.State == "" {
msg := "missing state parameter"
- log.WithFields(f).Errorf(msg)
+ log.WithFields(f).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
codeParts := strings.Split(params.State, ":")
if len(codeParts) != 2 {
msg := fmt.Sprintf("invalid state variable passed : %s", params.State)
- log.WithFields(f).Errorf(msg)
+ log.WithFields(f).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
@@ -291,7 +291,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
gitLabOrg, err := service.GetGitlabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
if err != nil {
msg := fmt.Sprintf("fetching gitlab model failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
+ log.WithFields(f).WithError(err).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
@@ -299,15 +299,15 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
oauthResp, err := gitlab_api.FetchOauthCredentials(params.Code)
if err != nil {
msg := fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
+ log.WithFields(f).WithError(err).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
log.WithFields(f).Debugf("oauth resp is like : %+v", oauthResp)
- err = service.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
- if err != nil {
- msg := fmt.Sprintf("updating gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
- log.WithFields(f).Errorf(msg)
+ updateErr := service.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
+ if updateErr != nil {
+ msg := fmt.Sprintf("installation of GitLab Group and Repositories, error: %v", updateErr)
+ log.WithFields(f).WithError(updateErr).Warn(msg)
return NewServerError(reqID, "", errors.New(msg))
}
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 14e119f7a..d6929d98a 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -31,6 +31,8 @@ const (
GitlabOrgSFIDIndex = "gitlab-org-sfid-index"
GitlabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
GitlabProjectSFIDOrganizationNameIndex = "gitlab-project-sfid-organization-name-index"
+ // GitLabExternalIDIndex the index for the external ID
+ GitLabExternalIDIndex = "github-user-external-id-index"
)
// RepositoryInterface is interface for gitlab org data model
@@ -293,7 +295,7 @@ func (repo *Repository) GetGitlabOrganizationByExternalID(ctx context.Context, g
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(repo.gitlabOrgTableName),
- IndexName: aws.String(GitlabOrgLowerNameIndex),
+ IndexName: aws.String(GitLabExternalIDIndex),
}
log.WithFields(f).Debugf("querying for GitLab organization by external group ID: %d...", gitLabGroupID)
@@ -318,18 +320,18 @@ func (repo *Repository) GetGitlabOrganizationByExternalID(ctx context.Context, g
}
// GetGitlabOrganization by organization name
-func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
+func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.GetGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationID,
+ "gitLabOrganizationID": gitLabOrganizationID,
}
- log.WithFields(f).Debug("Querying for GitLab organization by name...")
+ log.WithFields(f).Debugf("Querying for GitLab organization by ID: %s", gitLabOrganizationID)
result, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
Key: map[string]*dynamodb.AttributeValue{
GitLabOrganizationsOrganizationIDColumn: {
- S: aws.String(gitlabOrganizationID),
+ S: aws.String(gitLabOrganizationID),
},
},
TableName: aws.String(repo.gitlabOrgTableName),
@@ -338,7 +340,7 @@ func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitlabOrganiz
return nil, err
}
if len(result.Item) == 0 {
- log.WithFields(f).Debug("Unable to find GitLab organization by name - no results")
+ log.WithFields(f).Debugf("Unable to find GitLab organization by ID: %s - no results", gitLabOrganizationID)
return nil, nil
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index adac34e2b..0d7544019 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -34,13 +34,13 @@ import (
// ServiceInterface contains functions of GitlabOrganizations service
type ServiceInterface interface {
AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
- GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error)
- GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, gitlabOrganizationName string) (*models.GitlabOrganization, error)
+ GetGitlabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
+ GetGitlabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
- GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error)
+ GetGitlabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
- UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
+ UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
}
@@ -125,15 +125,15 @@ func (s *Service) GetGitlabOrganization(ctx context.Context, gitlabOrganizationI
}
// GetGitlabOrganizationByID returns the record associated with the GitLab Organization ID
-func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error) {
+func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationID,
+ "gitLabOrganizationID": gitLabOrganizationID,
}
- log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationID)
- dbModel, err := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id: %s", gitLabOrganizationID)
+ dbModel, err := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
if err != nil {
return nil, err
}
@@ -141,15 +141,15 @@ func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitlabOrganizat
return dbModel, nil
}
-func (s *Service) GetGitlabOrganizationByName(ctx context.Context, gitlabOrganizationName string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationName,
+ "gitlabOrganizationID": gitLabOrganizationName,
}
- log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationName)
- dbModel, err := s.repo.GetGitlabOrganizationByName(ctx, gitlabOrganizationName)
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id: %s", gitLabOrganizationName)
+ dbModel, err := s.repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName)
if err != nil {
return nil, err
}
@@ -232,6 +232,7 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
OrganizationName: org.OrganizationName,
OrganizationURL: org.OrganizationURL,
OrganizationFullPath: org.OrganizationFullPath,
+ OrganizationExternalID: org.OrganizationExternalID,
InstallationURL: buildInstallationURL(org.OrganizationID, orgDetailed.AuthState),
BranchProtectionEnabled: false,
ConnectionStatus: "", // updated below
@@ -283,16 +284,16 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
}
// GetGitlabOrganizationByState returns the GitLab organization by the auth state
-func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrganizationID, authState string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationID,
+ "gitLabOrganizationID": gitLabOrganizationID,
"authState": authState,
}
- log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitlabOrganizationID)
- dbModel, err := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitLabOrganizationID)
+ dbModel, err := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
if err != nil {
return nil, err
}
@@ -305,11 +306,11 @@ func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitlabOrgani
}
// UpdateGitlabOrganizationAuth updates the GitLab organization authentication information
-func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
+func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.UpdateGitlabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "gitlabOrganizationID": gitlabOrganizationID,
+ "gitLabOrganizationID": gitLabOrganizationID,
}
log.WithFields(f).Debugf("updating gitlab org auth")
@@ -318,7 +319,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
return fmt.Errorf("encrypt failed : %v", err)
}
- gitLabOrgModel, err := s.GetGitlabOrganizationByID(ctx, gitlabOrganizationID)
+ gitLabOrgModel, err := s.GetGitlabOrganizationByID(ctx, gitLabOrganizationID)
if err != nil {
return fmt.Errorf("gitlab organization lookup error: %+v", err)
}
@@ -337,13 +338,13 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitlabOrgani
for _, g := range groups {
if g.FullPath == gitLabOrgModel.OrganizationFullPath {
- updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
+ updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
}
log.WithFields(f).Debugf("fetching updated GitLab group/organization record which should now have all the details")
- updatedOrgDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitlabOrganizationID)
+ updatedOrgDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
if getErr != nil {
return getErr
}
@@ -374,12 +375,12 @@ func (s *Service) UpdateGitlabOrganization(ctx context.Context, projectSFID stri
}
// DeleteGitlabOrganization deletes the specified GitLab organization
-func (s *Service) DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error {
+func (s *Service) DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitLabOrgName string) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.DeleteGitlabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
- "gitlabOrgName": gitlabOrgName,
+ "gitLabOrgName": gitLabOrgName,
}
// Lookup the parent
@@ -392,13 +393,13 @@ func (s *Service) DeleteGitlabOrganization(ctx context.Context, projectSFID stri
log.WithFields(f).Debugf("retrieved parent of project sfid : %s -> %s", projectSFID, parentProjectSFID)
// Todo: Enable this when the repositories are implemented
- //err := s.ghRepository.GitHubDisableRepositoriesOfOrganization(ctx, parentProjectSFID, gitlabOrgName)
+ //err := s.ghRepository.GitHubDisableRepositoriesOfOrganization(ctx, parentProjectSFID, gitLabOrgName)
//if err != nil {
// log.WithFields(f).Warnf("problem disabling repositories for github organizations, error: %+v", projErr)
// return err
//}
- return s.repo.DeleteGitlabOrganization(ctx, projectSFID, gitlabOrgName)
+ return s.repo.DeleteGitlabOrganization(ctx, projectSFID, gitLabOrgName)
}
func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {
From 4514fb364e999fc3248be0c2a81c2bdad235fa80 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 15:22:56 -0700
Subject: [PATCH 0438/1276] Resolved GitLab Query Issue (#3174)
Signed-off-by: David Deal
---
cla-backend-go/v2/gitlab_organizations/service.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 0d7544019..398b57ab1 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -337,7 +337,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrgani
}
for _, g := range groups {
- if g.FullPath == gitLabOrgModel.OrganizationFullPath {
+ if g.ID == gitLabOrgModel.ExternalGroupID {
updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
@@ -359,7 +359,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrgani
}
}
- return fmt.Errorf("unable to locate GitLab group name '%s' using search, found: %d", gitLabOrgModel.OrganizationName, len(groups))
+ return fmt.Errorf("unable to locate GitLab group by using external ID: %d, found: %d", gitLabOrgModel.ExternalGroupID, len(groups))
}
// UpdateGitlabOrganization updates the GitLab organization
From 017faf692cbaba04deb004bf91efd5d5eadd61f3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 16:18:33 -0700
Subject: [PATCH 0439/1276] Added GitLab Full Path Support (#3175)
---
cla-backend-go/gitlab_api/client_groups.go | 25 +++
cla-backend-go/serverless.yml | 2 +
.../common/gitlab-organization-create.yaml | 25 +--
cla-backend-go/tests/gitlab_client_test.go | 38 +++++
.../v2/dynamo_events/gitlab_webhooks.go | 6 +-
cla-backend-go/v2/gitlab-activity/service.go | 6 +-
.../v2/gitlab_organizations/handlers.go | 23 +--
.../v2/gitlab_organizations/repository.go | 157 ++++++++++++------
.../v2/gitlab_organizations/service.go | 79 ++++-----
cla-backend-go/v2/gitlab_sign/service.go | 2 +-
.../v2/repositories/gitlab_services.go | 2 +-
cla-backend-go/v2/repositories/service.go | 19 ++-
cla-backend/serverless.yml | 7 +
13 files changed, 266 insertions(+), 125 deletions(-)
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
index c64a5f2f3..ba7a30b60 100644
--- a/cla-backend-go/gitlab_api/client_groups.go
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -121,6 +121,31 @@ func GetGroupByID(ctx context.Context, client *goGitLab.Client, groupID int) (*g
return group, nil
}
+// GetGroupByFullPath gets a gitlab Group by the given full path
+func GetGroupByFullPath(ctx context.Context, client *goGitLab.Client, fullPath string) (*goGitLab.Group, error) {
+ f := logrus.Fields{
+ "functionName": "gitlab_api.client_groups.GetGroupByName",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ groups, err := GetGroupsListAll(ctx, client)
+ //groups, _, err := client.Groups.ListGroups(&goGitLab.ListGroupsOptions{})
+ if err != nil {
+ msg := fmt.Sprintf("problem fetching groups, error: %+v", err)
+ log.WithFields(f).WithError(err).Warn(msg)
+ return nil, errors.New(msg)
+ }
+
+ for _, group := range groups {
+ log.WithFields(f).Debugf("testing %s == %s", fullPath, group.FullPath)
+ if group.FullPath == fullPath {
+ return group, nil
+ }
+ }
+
+ return nil, nil
+}
+
// GetGroupProjectListByGroupID returns a list of GitLab projects under the specified Organization
func GetGroupProjectListByGroupID(ctx context.Context, client *goGitLab.Client, groupID int) ([]*goGitLab.Project, error) {
f := logrus.Fields{
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index efbae4f06..699623260 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -199,6 +199,8 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-org-sfid-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-id-index"
environment:
STAGE: ${self:provider.stage}
diff --git a/cla-backend-go/swagger/common/gitlab-organization-create.yaml b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
index 48384010b..983fe0268 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-create.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
@@ -2,23 +2,26 @@
# SPDX-License-Identifier: MIT
type: object
-required:
- - group_id
properties:
-# organization_name:
-# type: string
-# description: The GitLab Group/Organization name
-# example: "kubernetes"
-# # Pattern aligns with UI and other platform services including Org Service
-# # \w Any word character (alphanumeric & underscore), dashes, periods
-# pattern: '^([\w\-\.]+){2,255}$'
-# minLength: 2
-# maxLength: 255
+ # organization_name:
+ # type: string
+ # description: The GitLab Group/Organization name
+ # example: "kubernetes"
+ # # Pattern aligns with UI and other platform services including Org Service
+ # # \w Any word character (alphanumeric & underscore), dashes, periods
+ # pattern: '^([\w\-\.]+){2,255}$'
+ # minLength: 2
+ # maxLength: 255
group_id:
type: integer
description: The GitLab Group ID
example: 13050017
minimum: 1
+ group_full_path:
+ type: string
+ description: The GitLab Group full path
+ example: 'linuxfoundation/product/easycla'
+ minLength: 3
auto_enabled:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
diff --git a/cla-backend-go/tests/gitlab_client_test.go b/cla-backend-go/tests/gitlab_client_test.go
index 8303f9cbd..46a4924c2 100644
--- a/cla-backend-go/tests/gitlab_client_test.go
+++ b/cla-backend-go/tests/gitlab_client_test.go
@@ -104,6 +104,44 @@ func TestGetGroupByID(t *testing.T) { // no lint
}
}
+func TestGetGroupByFullPath(t *testing.T) { // no lint
+ if enabled { // nolint
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+
+ // Create a new GitLab App client instance
+ gitLabApp := gitlab_api.Init(config.Gitlab.AppClientID, config.Gitlab.AppClientSecret, config.Gitlab.AppPrivateKey)
+
+ // Create a new client
+ gitLabClient, err := gitlab_api.NewGitlabOauthClient(accessInfo, gitLabApp)
+ assert.Nil(t, err, "GitLab OAuth Client Error is Nil")
+ assert.NotNil(t, gitLabClient, "GitLab OAuth Client is Not Nil")
+
+ ctx := utils.NewContext()
+ groupModel, getError := gitlab_api.GetGroupByFullPath(ctx, gitLabClient, "linuxfoundation/product/asitha")
+ assert.Nil(t, getError, "GitLab GetGroup Error should be nil", getError)
+ assert.NotNil(t, groupModel, "Group Model should not be nil")
+ t.Logf("group ID: %d, name: %s, path: %s, full path: %s", groupModel.ID, groupModel.Name, groupModel.Path, groupModel.FullPath)
+ }
+}
+
func TestGetGroupProjectListByGroupID(t *testing.T) { // no lint
if enabled { // nolint
// Need to initialize the system to load the configuration which contains a number of SSM parameters
diff --git a/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
index 49c3160ba..cc1d07696 100644
--- a/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
+++ b/cla-backend-go/v2/dynamo_events/gitlab_webhooks.go
@@ -50,7 +50,7 @@ func (s *service) GitLabRepoAddedWebhookEventHandler(event events.DynamoDBEventR
log.WithFields(f).Debugf("adding webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
- gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, newRepoModel.RepositoryOrganizationName)
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitLabOrganizationByName(ctx, newRepoModel.RepositoryOrganizationName)
if err != nil {
return fmt.Errorf("fetching gitlab org : %s failed : %v", newRepoModel.RepositoryOrganizationName, err)
}
@@ -119,7 +119,7 @@ func (s *service) GitlabRepoModifiedWebhookEventHandler(event events.DynamoDBEve
log.WithFields(f).Debugf("removing webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
}
- gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitLabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
if err != nil {
return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
}
@@ -179,7 +179,7 @@ func (s *service) GitLabRepoRemovedWebhookEventHandler(event events.DynamoDBEven
log.WithFields(f).Debugf("removing webhook for repository : %s:%s with external id : %s", repositoryID, repositoryName, repositoryExternalID)
- gitlabOrg, err := s.gitLabOrgRepo.GetGitlabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
+ gitlabOrg, err := s.gitLabOrgRepo.GetGitLabOrganizationByName(ctx, oldRepoModel.RepositoryOrganizationName)
if err != nil {
return fmt.Errorf("fetching gitlab org : %s failed : %v", oldRepoModel.RepositoryOrganizationName, err)
}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 6d4d61340..6ca2a9f43 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -253,16 +253,16 @@ func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeE
parts := strings.Split(repositoryPath, "/")
organizationName := parts[0]
- gitlabOrg, err := s.gitlabRepository.GetGitlabOrganizationByName(ctx, organizationName)
+ gitlabOrg, err := s.gitlabRepository.GetGitLabOrganizationByName(ctx, organizationName)
if err != nil || gitlabOrg == nil {
// try getting it with project name as well
- gitlabOrg, err = s.gitlabRepository.GetGitlabOrganizationByName(ctx, mergeEvent.Project.Namespace)
+ gitlabOrg, err = s.gitlabRepository.GetGitLabOrganizationByName(ctx, mergeEvent.Project.Namespace)
if err != nil || gitlabOrg == nil {
return nil, fmt.Errorf("gitlab org : %s doesn't exist : %v", organizationName, err)
}
}
- gitlabOrg, err = s.gitlabRepository.GetGitlabOrganization(ctx, gitlabOrg.OrganizationID)
+ gitlabOrg, err = s.gitlabRepository.GetGitLabOrganization(ctx, gitlabOrg.OrganizationID)
if err != nil {
return nil, fmt.Errorf("fetching gitlab org : %s failed : %v", gitlabOrg.OrganizationID, err)
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 66d8e1ff9..837b6ddf0 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -64,7 +64,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.GetGitlabOrganizations(ctx, params.ProjectSFID)
+ result, err := service.GetGitLabOrganizations(ctx, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("Gitlab organization with project SFID not found: %s", params.ProjectSFID)
@@ -94,6 +94,8 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"authUser": authUser.UserName,
"authEmail": authUser.Email,
"projectSFID": params.ProjectSFID,
+ "groupID": params.Body.GroupID,
+ "groupFullPath": params.Body.GroupFullPath,
}
// Load the project
@@ -113,13 +115,12 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Quick check of the parameters
- if params.Body == nil || params.Body.GroupID == nil {
- msg := fmt.Sprintf("missing group ID in body: %+v", params.Body)
+ if params.Body == nil || (params.Body.GroupID == 0 && params.Body.GroupFullPath == "") {
+ msg := fmt.Sprintf("missing group ID or group full path in the body: %+v", params.Body)
log.WithFields(f).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
utils.ErrorResponseBadRequest(reqID, msg))
}
- f["groupID"] = utils.Int64Value(params.Body.GroupID)
if params.Body.AutoEnabled == nil {
msg := fmt.Sprintf("missing autoEnabled name in body: %+v", params.Body)
@@ -138,7 +139,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- result, err := service.AddGitlabOrganization(ctx, params.ProjectSFID, params.Body)
+ result, err := service.AddGitLabOrganization(ctx, params.ProjectSFID, params.Body)
if err != nil {
msg := fmt.Sprintf("unable to add GitLab organization, error: %+v", err)
log.WithFields(f).WithError(err).Warn(msg)
@@ -148,7 +149,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
// Get the current group name for the event
for _, group := range result.List {
- if group.OrganizationExternalID == *params.Body.GroupID {
+ if group.OrganizationExternalID == params.Body.GroupID || group.OrganizationFullPath == params.Body.GroupFullPath {
// Log the event
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
@@ -181,7 +182,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
})
}
- err := service.UpdateGitlabOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
if err != nil {
if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
@@ -231,7 +232,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_organizations.NewDeleteProjectGitlabOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- err = service.DeleteGitlabOrganization(ctx, params.ProjectSFID, params.OrgName)
+ err = service.DeleteGitLabOrganization(ctx, params.ProjectSFID, params.OrgName)
if err != nil {
if strings.Contains(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("project not found with given SFID: %s", params.ProjectSFID)
@@ -288,7 +289,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
gitlabOrganizationID := codeParts[0]
stateVar := codeParts[1]
- gitLabOrg, err := service.GetGitlabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
+ gitLabOrg, err := service.GetGitLabOrganizationByState(ctx, gitlabOrganizationID, stateVar)
if err != nil {
msg := fmt.Sprintf("fetching gitlab model failed : %s : %v", gitlabOrganizationID, err)
log.WithFields(f).WithError(err).Warn(msg)
@@ -304,7 +305,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
log.WithFields(f).Debugf("oauth resp is like : %+v", oauthResp)
- updateErr := service.UpdateGitlabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
+ updateErr := service.UpdateGitLabOrganizationAuth(ctx, gitlabOrganizationID, oauthResp)
if updateErr != nil {
msg := fmt.Sprintf("installation of GitLab Group and Repositories, error: %v", updateErr)
log.WithFields(f).WithError(updateErr).Warn(msg)
@@ -312,7 +313,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Reload the GitLab organization - will have additional details now...
- updatedGitLabOrgDBModel, err := service.GetGitlabOrganizationByID(ctx, gitLabOrg.OrganizationID)
+ updatedGitLabOrgDBModel, err := service.GetGitLabOrganizationByID(ctx, gitLabOrg.OrganizationID)
if err != nil {
msg := fmt.Sprintf("problem loading updated gitlab organization by ID: %s : %v", gitlabOrganizationID, err)
log.WithFields(f).Errorf(msg)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index d6929d98a..a435b6723 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -28,24 +28,28 @@ import (
// indexes
const (
- GitlabOrgSFIDIndex = "gitlab-org-sfid-index"
- GitlabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
- GitlabProjectSFIDOrganizationNameIndex = "gitlab-project-sfid-organization-name-index"
+ // GitlabOrgSFIDIndex the index for the SFID
+ GitlabOrgSFIDIndex = "gitlab-org-sfid-index"
+ // GitlabOrgLowerNameIndex the index for the group/org naem in lower case
+ GitlabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
// GitLabExternalIDIndex the index for the external ID
- GitLabExternalIDIndex = "github-user-external-id-index"
+ GitLabExternalIDIndex = "gitlab-external-group-id-index"
+ // GitLabFullPathIndex the index for the full path
+ GitLabFullPathIndex = "gitlab-full-path-index"
)
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
- AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error)
- GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
- GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
- UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
+ AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error)
+ GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
+ GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
+ UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
// Repository object/struct
@@ -64,24 +68,35 @@ func NewRepository(awsSession *session.Session, stage string) RepositoryInterfac
}
}
-func (repo *Repository) AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error) {
+func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.repository.AddGitlabOrganization",
+ "functionName": "v2.gitlab_organizations.repository.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"parentProjectSFID": parentProjectSFID,
"projectSFID": projectSFID,
"groupID": groupID,
"organizationName": organizationName,
+ "groupFullPath": groupFullPath,
"autoEnabled": autoEnabled,
"autoEnabledClaGroupID": autoEnabledClaGroupID,
"branchProtectionEnabled": branchProtectionEnabled,
"enabled": enabled,
}
- // First, let's check to see if we have an existing gitlab organization with the same name
- existingRecord, getErr := repo.GetGitlabOrganizationByExternalID(ctx, groupID)
- if getErr != nil {
- log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab organization by name %d - ok to create a new record", groupID)
+ var existingRecord *common.GitLabOrganization
+ var getErr error
+ if groupID != 0 {
+ // First, let's check to see if we have an existing gitlab organization with the same name
+ existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
+ if getErr != nil {
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by name %d - ok to create a new record", groupID)
+ }
+ } else if groupFullPath != "" {
+ // First, let's check to see if we have an existing gitlab organization with the same name
+ existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
+ if getErr != nil {
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by full path: %s - ok to create a new record", groupFullPath)
+ }
}
if existingRecord != nil {
@@ -89,14 +104,14 @@ func (repo *Repository) AddGitlabOrganization(ctx context.Context, parentProject
// If everything matches...
if projectSFID == existingRecord.ProjectSFID {
log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
- updateErr := repo.UpdateGitlabOrganizationByExternalID(ctx, projectSFID, groupID, organizationName,
+ updateErr := repo.UpdateGitLabOrganizationByExternalID(ctx, projectSFID, groupID, organizationName, groupFullPath,
autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, enabled)
if updateErr != nil {
return nil, updateErr
}
// Return the updated record
- if gitlabOrg, err := repo.GetGitlabOrganizationByExternalID(ctx, groupID); err != nil {
+ if gitlabOrg, err := repo.GetGitLabOrganizationByExternalID(ctx, groupID); err != nil {
return nil, err
} else {
return common.ToModel(gitlabOrg), nil
@@ -167,10 +182,10 @@ func (repo *Repository) AddGitlabOrganization(ctx context.Context, parentProject
return common.ToModel(gitlabOrg), nil
}
-// GetGitlabOrganizations get GitLab organizations based on the project SFID
-func (repo *Repository) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
+// GetGitLabOrganizations get GitLab organizations based on the project SFID
+func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.repository.GetGitlabOrganizations",
+ "functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -223,10 +238,10 @@ func (repo *Repository) GetGitlabOrganizations(ctx context.Context, projectSFID
return &models2.GitlabOrganizations{List: gitlabOrgList}, nil
}
-// GetGitlabOrganizationByName get GitLab organization by name
-func (repo *Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error) {
+// GetGitLabOrganizationByName get GitLab organization by name
+func (repo *Repository) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
- "functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByName",
+ "functionName": "v1.gitlab_organizations.repository.GetGitLabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabOrganizationName": gitLabOrganizationName,
}
@@ -272,9 +287,9 @@ func (repo *Repository) GetGitlabOrganizationByName(ctx context.Context, gitLabO
return resultOutput[0], nil
}
-func (repo *Repository) GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error) {
+func (repo *Repository) GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error) {
f := logrus.Fields{
- "functionName": "v1.gitlab_organizations.repository.GetGitlabOrganizationByExternalID",
+ "functionName": "v1.gitlab_organizations.repository.GetGitLabOrganizationByExternalID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabGroupID": gitLabGroupID,
}
@@ -319,10 +334,58 @@ func (repo *Repository) GetGitlabOrganizationByExternalID(ctx context.Context, g
return resultOutput[0], nil
}
-// GetGitlabOrganization by organization name
-func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
+// GetGitlabOrganizationByFullPath loads the organization based on the full path value
+func (repo *Repository) GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v1.gitlab_organizations.repository.GetGitLabOrganizationByFullPath",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "groupFullPath": groupFullPath,
+ }
+
+ condition := expression.Key(GitLabOrganizationsOrganizationFullPathColumn).Equal(expression.Value(groupFullPath))
+ builder := expression.NewBuilder().WithKeyCondition(condition)
+ // Use the nice builder to create the expression
+ expr, err := builder.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ IndexName: aws.String(GitLabFullPathIndex),
+ }
+
+ log.WithFields(f).Debugf("querying for GitLab group by full path: %s...", groupFullPath)
+ results, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error retrieving GitLab group by full path: %s", groupFullPath)
+ return nil, err
+ }
+ if len(results.Items) == 0 {
+ log.WithFields(f).Debugf("Unable to find GitLab group by full path: %s - no results", groupFullPath)
+ return nil, nil
+ }
+
+ var resultOutput []*common.GitLabOrganization
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
+ if err != nil {
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ return nil, err
+ }
+
+ return resultOutput[0], nil
+}
+
+// GetGitLabOrganization by organization name
+func (repo *Repository) GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.GetGitlabOrganization",
+ "functionName": "gitlab_organizations.repository.GetGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabOrganizationID": gitLabOrganizationID,
}
@@ -353,10 +416,10 @@ func (repo *Repository) GetGitlabOrganization(ctx context.Context, gitLabOrganiz
return &org, nil
}
-// UpdateGitlabOrganizationAuth updates the specified Gitlab organization oauth info
-func (repo *Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
+// UpdateGitLabOrganizationAuth updates the specified Gitlab organization oauth info
+func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationAuth",
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"organizationID": organizationID,
"organizationFullPath": organizationFullPath,
@@ -365,7 +428,7 @@ func (repo *Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organi
}
_, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitlabOrganization(ctx, organizationID)
+ gitlabOrg, lookupErr := repo.GetGitLabOrganization(ctx, organizationID)
if lookupErr != nil || gitlabOrg == nil {
log.WithFields(f).Warnf("error looking up Gitlab organization by id: %s, error: %+v", organizationID, lookupErr)
return lookupErr
@@ -420,10 +483,10 @@ func (repo *Repository) UpdateGitlabOrganizationAuth(ctx context.Context, organi
return nil
}
-// UpdateGitlabOrganization updates the GitLab group based on the specified values
-func (repo *Repository) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
+// UpdateGitLabOrganization updates the GitLab group based on the specified values
+func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitlabOrganization",
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"organizationName": organizationName,
@@ -434,7 +497,7 @@ func (repo *Repository) UpdateGitlabOrganization(ctx context.Context, projectSFI
}
_, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitlabOrganizationByName(ctx, organizationName)
+ gitlabOrg, lookupErr := repo.GetGitLabOrganizationByName(ctx, organizationName)
if lookupErr != nil {
log.WithFields(f).Warnf("error looking up Gitlab organization by name, error: %+v", lookupErr)
return lookupErr
@@ -493,9 +556,9 @@ func (repo *Repository) UpdateGitlabOrganization(ctx context.Context, projectSFI
}
// UpdateGitlabOrganizationByExternalID updates the GitLab group based on the specified values
-func (repo *Repository) UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
+func (repo *Repository) UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, fullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitlabOrganizationByExternalID",
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationByExternalID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"groupID": groupID,
@@ -507,7 +570,7 @@ func (repo *Repository) UpdateGitlabOrganizationByExternalID(ctx context.Context
}
_, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitlabOrganizationByExternalID(ctx, groupID)
+ gitlabOrg, lookupErr := repo.GetGitLabOrganizationByExternalID(ctx, groupID)
if lookupErr != nil {
log.WithFields(f).Warnf("error looking up GitLab group by ID: %d, error: %+v", groupID, lookupErr)
return lookupErr
@@ -573,17 +636,17 @@ func (repo *Repository) UpdateGitlabOrganizationByExternalID(ctx context.Context
return nil
}
-// DeleteGitlabOrganization deletes the specified GitLab organization
-func (repo *Repository) DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
+// DeleteGitLabOrganization deletes the specified GitLab organization
+func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
f := logrus.Fields{
- "functionName": "v1.gitlab_organizations.repository.DeleteGitlabOrganization",
+ "functionName": "v1.gitlab_organizations.repository.DeleteGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"gitlabOrgName": gitlabOrgName,
}
var gitlabOrganizationID string
- orgs, orgErr := repo.GetGitlabOrganizations(ctx, projectSFID)
+ orgs, orgErr := repo.GetGitLabOrganizations(ctx, projectSFID)
if orgErr != nil {
errMsg := fmt.Sprintf("gitlab organization is not found using projectSFID: %s, error: %+v", projectSFID, orgErr)
log.WithFields(f).Warn(errMsg)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 398b57ab1..3926659fb 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -33,15 +33,15 @@ import (
// ServiceInterface contains functions of GitlabOrganizations service
type ServiceInterface interface {
- AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
- GetGitlabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
- GetGitlabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
- GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
- GetGitlabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
- UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
- UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
- DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
+ AddGitLabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
+ GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
+ GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
+ GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
+ GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
+ UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
+ DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
}
// Service data model
@@ -63,14 +63,15 @@ func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceI
}
// AddGitlabOrganization adds the specified GitLab organization
-func (s *Service) AddGitlabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error) {
+func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.AddGitlabOrganization",
+ "functionName": "v2.gitlab_organizations.service.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"autoEnabled": utils.BoolValue(input.AutoEnabled),
"branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
- "groupID": utils.Int64Value(input.GroupID),
+ "groupID": input.GroupID,
+ "groupFullPath": input.GroupFullPath,
}
psc := v2ProjectService.GetClient()
@@ -100,19 +101,19 @@ func (s *Service) AddGitlabOrganization(ctx context.Context, projectSFID string,
branchProtectionEnabled = utils.BoolValue(input.BranchProtectionEnabled)
}
- resp, err := s.repo.AddGitlabOrganization(ctx, parentProjectSFID, projectSFID, *input.GroupID, "", autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
+ resp, err := s.repo.AddGitLabOrganization(ctx, parentProjectSFID, projectSFID, input.GroupID, "", input.GroupFullPath, autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem adding gitlab organization for project")
return nil, err
}
log.WithFields(f).Debugf("created GitLab organization with ID: %s", resp.OrganizationID)
- return s.GetGitlabOrganizations(ctx, projectSFID)
+ return s.GetGitLabOrganizations(ctx, projectSFID)
}
// GetGitlabOrganization returns the GitLab organization based on the specified GitLab Organization ID
-func (s *Service) GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error) {
- dbModel, err := s.GetGitlabOrganizationByID(ctx, gitlabOrganizationID)
+func (s *Service) GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error) {
+ dbModel, err := s.GetGitLabOrganizationByID(ctx, gitlabOrganizationID)
if err != nil {
return nil, err
}
@@ -125,15 +126,15 @@ func (s *Service) GetGitlabOrganization(ctx context.Context, gitlabOrganizationI
}
// GetGitlabOrganizationByID returns the record associated with the GitLab Organization ID
-func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByID",
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabOrganizationID": gitLabOrganizationID,
}
log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id: %s", gitLabOrganizationID)
- dbModel, err := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
+ dbModel, err := s.repo.GetGitLabOrganization(ctx, gitLabOrganizationID)
if err != nil {
return nil, err
}
@@ -141,15 +142,15 @@ func (s *Service) GetGitlabOrganizationByID(ctx context.Context, gitLabOrganizat
return dbModel, nil
}
-func (s *Service) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.GetGitlabOrganizationByName",
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitlabOrganizationID": gitLabOrganizationName,
}
log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id: %s", gitLabOrganizationName)
- dbModel, err := s.repo.GetGitlabOrganizationByName(ctx, gitLabOrganizationName)
+ dbModel, err := s.repo.GetGitLabOrganizationByName(ctx, gitLabOrganizationName)
if err != nil {
return nil, err
}
@@ -159,16 +160,16 @@ func (s *Service) GetGitlabOrganizationByName(ctx context.Context, gitLabOrganiz
}
// GetGitlabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
-func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
+func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.GetGitlabOrganizations",
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
// Load the GitLab Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
- orgs, err := s.repo.GetGitlabOrganizations(ctx, projectSFID)
+ orgs, err := s.repo.GetGitLabOrganizations(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading gitlab organizations from the project service")
return nil, err
@@ -210,7 +211,7 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
}
}
- orgDetailed, orgErr := s.repo.GetGitlabOrganization(ctx, org.OrganizationID)
+ orgDetailed, orgErr := s.repo.GetGitLabOrganization(ctx, org.OrganizationID)
if orgErr != nil {
log.WithFields(f).Errorf("fetching gitlab org failed : %s : %v", org.OrganizationID, orgErr)
continue
@@ -284,16 +285,16 @@ func (s *Service) GetGitlabOrganizations(ctx context.Context, projectSFID string
}
// GetGitlabOrganizationByState returns the GitLab organization by the auth state
-func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.GetGitlabOrganization",
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabOrganizationID": gitLabOrganizationID,
"authState": authState,
}
log.WithFields(f).Debugf("fetching gitlab organization for gitlab org id : %s", gitLabOrganizationID)
- dbModel, err := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
+ dbModel, err := s.repo.GetGitLabOrganization(ctx, gitLabOrganizationID)
if err != nil {
return nil, err
}
@@ -306,9 +307,9 @@ func (s *Service) GetGitlabOrganizationByState(ctx context.Context, gitLabOrgani
}
// UpdateGitlabOrganizationAuth updates the GitLab organization authentication information
-func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
+func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.UpdateGitlabOrganizationAuth",
+ "functionName": "v2.gitlab_organizations.service.UpdateGitLabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitLabOrganizationID": gitLabOrganizationID,
}
@@ -319,7 +320,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrgani
return fmt.Errorf("encrypt failed : %v", err)
}
- gitLabOrgModel, err := s.GetGitlabOrganizationByID(ctx, gitLabOrganizationID)
+ gitLabOrgModel, err := s.GetGitLabOrganizationByID(ctx, gitLabOrganizationID)
if err != nil {
return fmt.Errorf("gitlab organization lookup error: %+v", err)
}
@@ -338,13 +339,13 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrgani
for _, g := range groups {
if g.ID == gitLabOrgModel.ExternalGroupID {
- updateGitLabOrgErr := s.repo.UpdateGitlabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
+ updateGitLabOrgErr := s.repo.UpdateGitLabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
}
log.WithFields(f).Debugf("fetching updated GitLab group/organization record which should now have all the details")
- updatedOrgDBModel, getErr := s.repo.GetGitlabOrganization(ctx, gitLabOrganizationID)
+ updatedOrgDBModel, getErr := s.repo.GetGitLabOrganization(ctx, gitLabOrganizationID)
if getErr != nil {
return getErr
}
@@ -363,7 +364,7 @@ func (s *Service) UpdateGitlabOrganizationAuth(ctx context.Context, gitLabOrgani
}
// UpdateGitlabOrganization updates the GitLab organization
-func (s *Service) UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
if _, err := s.claGroupRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
@@ -371,13 +372,13 @@ func (s *Service) UpdateGitlabOrganization(ctx context.Context, projectSFID stri
}
}
- return s.repo.UpdateGitlabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
+ return s.repo.UpdateGitLabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
}
// DeleteGitlabOrganization deletes the specified GitLab organization
-func (s *Service) DeleteGitlabOrganization(ctx context.Context, projectSFID string, gitLabOrgName string) error {
+func (s *Service) DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitLabOrgName string) error {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.DeleteGitlabOrganization",
+ "functionName": "v2.gitlab_organizations.service.DeleteGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"gitLabOrgName": gitLabOrgName,
@@ -399,7 +400,7 @@ func (s *Service) DeleteGitlabOrganization(ctx context.Context, projectSFID stri
// return err
//}
- return s.repo.DeleteGitlabOrganization(ctx, projectSFID, gitLabOrgName)
+ return s.repo.DeleteGitLabOrganization(ctx, projectSFID, gitLabOrgName)
}
func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {
diff --git a/cla-backend-go/v2/gitlab_sign/service.go b/cla-backend-go/v2/gitlab_sign/service.go
index 5cd106464..ee4083736 100644
--- a/cla-backend-go/v2/gitlab_sign/service.go
+++ b/cla-backend-go/v2/gitlab_sign/service.go
@@ -54,7 +54,7 @@ func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organ
"mergeRequestID": mergeRequestID,
}
- organization, err := s.gitlabOrgRepo.GetGitlabOrganization(ctx, organizationID)
+ organization, err := s.gitlabOrgRepo.GetGitLabOrganization(ctx, organizationID)
if err != nil {
log.WithFields(f).Debugf("unable to get gitlab organiztion by ID: %s, error: %+v ", organizationID, err)
return nil
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 7229a5fd0..30027ca15 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -32,7 +32,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
"claGroupID": utils.StringValue(input.ClaGroupID),
}
- gitLabOrgModel, orgErr := s.glOrgRepo.GetGitlabOrganizationByName(ctx, utils.StringValue(input.GitlabOrganizationName))
+ gitLabOrgModel, orgErr := s.glOrgRepo.GetGitLabOrganizationByName(ctx, utils.StringValue(input.GitlabOrganizationName))
if orgErr != nil {
msg := fmt.Sprintf("problem loading gitlab organization by name: %s, error: %v", utils.StringValue(input.GitlabOrganizationName), orgErr)
log.WithFields(f).WithError(orgErr).Warn(msg)
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index c01224d08..3cd339608 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -67,15 +67,16 @@ type ServiceInterface interface {
// GithubOrgRepo redefine the interface here to avoid circular dependency issues
type GithubOrgRepo interface {
- AddGitlabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
- GetGitlabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
- GetGitlabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
- GetGitlabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
- UpdateGitlabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitlabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- UpdateGitlabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- DeleteGitlabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
+ AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
+ GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
+ GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
+ UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
// Service is the service model/structure
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index d35d1d310..188ed452b 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -224,6 +224,8 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-users"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-metrics"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs"
+
- Effect: Allow
Action:
- dynamodb:Query
@@ -285,6 +287,11 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-cla-manager-requests/index/cla-manager-requests-project-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/cla-group-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/foundation-sfid-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-org-sfid-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-id-index"
environment:
STAGE: ${self:provider.stage}
From 1ba113bd045dabbf77c95a6ff88717fa94023105 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 16:36:53 -0700
Subject: [PATCH 0440/1276] Updated Docs and Added Full Path to Response
(#3176)
---
.../v2/gitlab_organizations/repository.go | 9 ++++++---
.../v2/gitlab_organizations/service.go | 17 +++++++++--------
2 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index a435b6723..a01feb8f3 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -68,6 +68,7 @@ func NewRepository(awsSession *session.Session, stage string) RepositoryInterfac
}
}
+// AddGitLabOrganization adds the specified values to the GitLab Group/Org table
func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.AddGitLabOrganization",
@@ -89,7 +90,7 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
// First, let's check to see if we have an existing gitlab organization with the same name
existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
if getErr != nil {
- log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by name %d - ok to create a new record", groupID)
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by ID: %d - ok to create a new record", groupID)
}
} else if groupFullPath != "" {
// First, let's check to see if we have an existing gitlab organization with the same name
@@ -100,7 +101,7 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
}
if existingRecord != nil {
- log.WithFields(f).Debugf("An existing GitLab organization with name %d exists in our database", groupID)
+ log.WithFields(f).Debugf("An existing GitLab organization with ID %d exists in our database", groupID)
// If everything matches...
if projectSFID == existingRecord.ProjectSFID {
log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
@@ -142,6 +143,7 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
DateModified: currentTime,
OrganizationName: organizationName,
OrganizationNameLower: strings.ToLower(organizationName),
+ OrganizationFullPath: groupFullPath,
ExternalGroupID: int(groupID),
OrganizationSFID: parentProjectSFID,
ProjectSFID: projectSFID,
@@ -287,6 +289,7 @@ func (repo *Repository) GetGitLabOrganizationByName(ctx context.Context, gitLabO
return resultOutput[0], nil
}
+// GetGitLabOrganizationByExternalID returns the GitLab Group/Org based on the external GitLab Group ID value
func (repo *Repository) GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v1.gitlab_organizations.repository.GetGitLabOrganizationByExternalID",
@@ -334,7 +337,7 @@ func (repo *Repository) GetGitLabOrganizationByExternalID(ctx context.Context, g
return resultOutput[0], nil
}
-// GetGitlabOrganizationByFullPath loads the organization based on the full path value
+// GetGitLabOrganizationByFullPath loads the organization based on the full path value
func (repo *Repository) GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v1.gitlab_organizations.repository.GetGitLabOrganizationByFullPath",
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 3926659fb..4b278a2b1 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -62,7 +62,7 @@ func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceI
}
}
-// AddGitlabOrganization adds the specified GitLab organization
+// AddGitLabOrganization adds the specified GitLab organization
func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.AddGitLabOrganization",
@@ -111,7 +111,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
return s.GetGitLabOrganizations(ctx, projectSFID)
}
-// GetGitlabOrganization returns the GitLab organization based on the specified GitLab Organization ID
+// GetGitLabOrganization returns the GitLab organization based on the specified GitLab Organization ID
func (s *Service) GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error) {
dbModel, err := s.GetGitLabOrganizationByID(ctx, gitlabOrganizationID)
if err != nil {
@@ -125,7 +125,7 @@ func (s *Service) GetGitLabOrganization(ctx context.Context, gitlabOrganizationI
return common.ToModel(dbModel), err
}
-// GetGitlabOrganizationByID returns the record associated with the GitLab Organization ID
+// GetGitLabOrganizationByID returns the record associated with the GitLab Organization ID
func (s *Service) GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByID",
@@ -142,6 +142,7 @@ func (s *Service) GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizat
return dbModel, nil
}
+// GetGitLabOrganizationByName returns the gitlab organization based on the Group/Org name
func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByName",
@@ -159,7 +160,7 @@ func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganiz
}
-// GetGitlabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
+// GetGitLabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizations",
@@ -284,7 +285,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
return out, nil
}
-// GetGitlabOrganizationByState returns the GitLab organization by the auth state
+// GetGitLabOrganizationByState returns the GitLab organization by the auth state
func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganization",
@@ -306,7 +307,7 @@ func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrgani
return common.ToModel(dbModel), nil
}
-// UpdateGitlabOrganizationAuth updates the GitLab organization authentication information
+// UpdateGitLabOrganizationAuth updates the GitLab organization authentication information
func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.UpdateGitLabOrganizationAuth",
@@ -363,7 +364,7 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
return fmt.Errorf("unable to locate GitLab group by using external ID: %d, found: %d", gitLabOrgModel.ExternalGroupID, len(groups))
}
-// UpdateGitlabOrganization updates the GitLab organization
+// UpdateGitLabOrganization updates the GitLab organization
func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
@@ -375,7 +376,7 @@ func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID stri
return s.repo.UpdateGitLabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
}
-// DeleteGitlabOrganization deletes the specified GitLab organization
+// DeleteGitLabOrganization deletes the specified GitLab organization
func (s *Service) DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitLabOrgName string) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.DeleteGitLabOrganization",
From 0b6251378fbc6d51aa2dc569658d90facd384b90 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 17:00:15 -0700
Subject: [PATCH 0441/1276] Added Check for GL Full Path (#3177)
---
cla-backend-go/v2/gitlab_organizations/service.go | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 4b278a2b1..13e25d74b 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -339,7 +339,10 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
for _, g := range groups {
- if g.ID == gitLabOrgModel.ExternalGroupID {
+ // If we have an external group ID or a full path...
+ if (gitLabOrgModel.ExternalGroupID > 0 && g.ID == gitLabOrgModel.ExternalGroupID) ||
+ (gitLabOrgModel.OrganizationFullPath != "" && g.FullPath == gitLabOrgModel.OrganizationFullPath) {
+
updateGitLabOrgErr := s.repo.UpdateGitLabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
@@ -361,7 +364,7 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
}
- return fmt.Errorf("unable to locate GitLab group by using external ID: %d, found: %d", gitLabOrgModel.ExternalGroupID, len(groups))
+ return fmt.Errorf("unable to locate GitLab group by using external ID: %d or full path: %s, found: %d", gitLabOrgModel.ExternalGroupID, gitLabOrgModel.OrganizationFullPath, len(groups))
}
// UpdateGitLabOrganization updates the GitLab organization
From 6fc2aed1576a321f15a2a9a2b2b30ff754e025b6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 19:03:38 -0700
Subject: [PATCH 0442/1276] Updated Lambda Index Permissions (#3178)
---
cla-backend-go/serverless.yml | 2 +-
cla-backend/serverless.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index 699623260..cb5c744ee 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -200,7 +200,7 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
- - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-id-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-group-id-index"
environment:
STAGE: ${self:provider.stage}
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index 188ed452b..74873fe0b 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -291,7 +291,7 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
- - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-id-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-external-group-id-index"
environment:
STAGE: ${self:provider.stage}
From d2341da5df7b7cd9613c473153bfcfbd2520fe3b Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 19 Aug 2021 19:43:08 -0700
Subject: [PATCH 0443/1276] Resolved GitLab Query Bug (#3179)
---
.../v2/gitlab_organizations/handlers.go | 2 +-
.../v2/gitlab_organizations/repository.go | 157 +++++++-----------
.../v2/gitlab_organizations/service.go | 6 +-
cla-backend-go/v2/repositories/service.go | 3 +-
4 files changed, 62 insertions(+), 106 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 837b6ddf0..994f49f51 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -182,7 +182,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
})
}
- err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.OrgName, *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, 0, params.OrgName, "", *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
if err != nil {
if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index a01feb8f3..e00cbaa58 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -47,8 +47,7 @@ type RepositoryInterface interface {
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
@@ -87,12 +86,14 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
var existingRecord *common.GitLabOrganization
var getErr error
if groupID != 0 {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
// First, let's check to see if we have an existing gitlab organization with the same name
existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
if getErr != nil {
log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by ID: %d - ok to create a new record", groupID)
}
} else if groupFullPath != "" {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
// First, let's check to see if we have an existing gitlab organization with the same name
existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
if getErr != nil {
@@ -101,21 +102,30 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
}
if existingRecord != nil {
- log.WithFields(f).Debugf("An existing GitLab organization with ID %d exists in our database", groupID)
+ log.WithFields(f).Debugf("An existing GitLab organization with ID %d or full path: %s exists in our database", groupID, groupFullPath)
// If everything matches...
if projectSFID == existingRecord.ProjectSFID {
- log.WithFields(f).Debug("Existing GitLab organization with same SFID - should be able to update it")
- updateErr := repo.UpdateGitLabOrganizationByExternalID(ctx, projectSFID, groupID, organizationName, groupFullPath,
+ log.WithFields(f).Debug("existing GitLab organization with same SFID - should be able to update it")
+ updateErr := repo.UpdateGitLabOrganization(ctx, projectSFID, groupID, organizationName, groupFullPath,
autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, enabled)
if updateErr != nil {
return nil, updateErr
}
- // Return the updated record
- if gitlabOrg, err := repo.GetGitLabOrganizationByExternalID(ctx, groupID); err != nil {
- return nil, err
- } else {
- return common.ToModel(gitlabOrg), nil
+ if groupID > 0 {
+ // Return the updated record
+ if gitlabOrg, err := repo.GetGitLabOrganizationByExternalID(ctx, groupID); err != nil {
+ return nil, err
+ } else {
+ return common.ToModel(gitlabOrg), nil
+ }
+ } else if groupFullPath != "" {
+ // Return the updated record
+ if gitlabOrg, err := repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath); err != nil {
+ return nil, err
+ } else {
+ return common.ToModel(gitlabOrg), nil
+ }
}
}
@@ -487,11 +497,13 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
}
// UpdateGitLabOrganization updates the GitLab group based on the specified values
-func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
+func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitLabOrganization",
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationByExternalID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
+ "groupID": groupID,
+ "groupFullPath": groupFullPath,
"organizationName": organizationName,
"autoEnabled": autoEnabled,
"autoEnabledClaGroupID": autoEnabledClaGroupID,
@@ -499,17 +511,33 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
"tableName": repo.gitlabOrgTableName,
}
- _, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitLabOrganizationByName(ctx, organizationName)
- if lookupErr != nil {
- log.WithFields(f).Warnf("error looking up Gitlab organization by name, error: %+v", lookupErr)
- return lookupErr
+ var existingRecord *common.GitLabOrganization
+ var getErr error
+ if groupID > 0 {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
+ existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
+ if getErr != nil {
+ msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v", groupID, groupFullPath)
+ log.WithFields(f).WithError(getErr).Warn(msg)
+ return errors.New(msg)
+ }
+ } else if groupFullPath != "" {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
+ existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
+ if getErr != nil {
+ msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v", groupFullPath, getErr)
+ log.WithFields(f).WithError(getErr).Warn(msg)
+ return errors.New(msg)
+ }
}
- if gitlabOrg == nil {
- log.WithFields(f).Warn("error looking up Gitlab organization - no results")
- return errors.New("unable to lookup Gitlab organization by name")
+
+ if existingRecord == nil {
+ msg := fmt.Sprintf("error looking up GitLab group using group ID: %d or full path: %s - no results", groupID, groupFullPath)
+ log.WithFields(f).Warn(msg)
+ return errors.New(msg)
}
+ _, currentTime := utils.CurrentTime()
expressionAttributeNames := map[string]*string{
"#A": aws.String(GitLabOrganizationsAutoEnabledColumn),
"#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
@@ -534,93 +562,22 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
BOOL: aws.Bool(enabled),
},
}
- updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m, #E = :e"
+ updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m, #E = :e "
- input := &dynamodb.UpdateItemInput{
- Key: map[string]*dynamodb.AttributeValue{
- GitLabOrganizationsOrganizationIDColumn: {
- S: aws.String(gitlabOrg.OrganizationID),
- },
- },
- ExpressionAttributeNames: expressionAttributeNames,
- ExpressionAttributeValues: expressionAttributeValues,
- UpdateExpression: &updateExpression,
- TableName: aws.String(repo.gitlabOrgTableName),
- }
+ if organizationName != "" {
+ expressionAttributeNames["#N"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
+ expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(organizationName)}
+ updateExpression = fmt.Sprintf("%s, #N = :n ", updateExpression)
- log.WithFields(f).Debugf("updating gitlab organization record: %+v", input)
- _, updateErr := repo.dynamoDBClient.UpdateItem(input)
- if updateErr != nil {
- log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v", updateErr)
- return updateErr
+ expressionAttributeNames["#NL"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
+ expressionAttributeValues[":nl"] = &dynamodb.AttributeValue{S: aws.String(strings.ToLower(organizationName))}
+ updateExpression = fmt.Sprintf("%s, #NL = :nl ", updateExpression)
}
- return nil
-}
-
-// UpdateGitlabOrganizationByExternalID updates the GitLab group based on the specified values
-func (repo *Repository) UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, fullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
- f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationByExternalID",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "groupID": groupID,
- "organizationName": organizationName,
- "autoEnabled": autoEnabled,
- "autoEnabledClaGroupID": autoEnabledClaGroupID,
- "branchProtectionEnabled": branchProtectionEnabled,
- "tableName": repo.gitlabOrgTableName,
- }
-
- _, currentTime := utils.CurrentTime()
- gitlabOrg, lookupErr := repo.GetGitLabOrganizationByExternalID(ctx, groupID)
- if lookupErr != nil {
- log.WithFields(f).Warnf("error looking up GitLab group by ID: %d, error: %+v", groupID, lookupErr)
- return lookupErr
- }
- if gitlabOrg == nil {
- log.WithFields(f).Warn("error looking up GitLab group - no results")
- return errors.New("unable to lookup GitLab group by ID")
- }
-
- expressionAttributeNames := map[string]*string{
- "#A": aws.String(GitLabOrganizationsAutoEnabledColumn),
- "#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
- "#B": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
- "#N": aws.String(GitLabOrganizationsOrganizationNameColumn),
- "#NL": aws.String(GitLabOrganizationsOrganizationNameLowerColumn),
- "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
- "#E": aws.String(GitLabOrganizationsEnabledColumn),
- }
- expressionAttributeValues := map[string]*dynamodb.AttributeValue{
- ":a": {
- BOOL: aws.Bool(autoEnabled),
- },
- ":c": {
- S: aws.String(autoEnabledClaGroupID),
- },
- ":b": {
- BOOL: aws.Bool(branchProtectionEnabled),
- },
- ":n": {
- S: aws.String(organizationName),
- },
- ":nl": {
- S: aws.String(strings.ToLower(organizationName)),
- },
- ":m": {
- S: aws.String(currentTime),
- },
- ":e": {
- BOOL: aws.Bool(enabled),
- },
- }
- updateExpression := "SET #A = :a, #C = :c, #B = :b, #N = :n, #NL = :nl, #M = :m, #E = :e "
-
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
GitLabOrganizationsOrganizationIDColumn: {
- S: aws.String(gitlabOrg.OrganizationID),
+ S: aws.String(existingRecord.OrganizationID),
},
},
ExpressionAttributeNames: expressionAttributeNames,
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 13e25d74b..22da09b06 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -39,7 +39,7 @@ type ServiceInterface interface {
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
}
@@ -368,7 +368,7 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
// UpdateGitLabOrganization updates the GitLab organization
-func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
// check if valid cla group id is passed
if autoEnabledClaGroupID != "" {
if _, err := s.claGroupRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
@@ -376,7 +376,7 @@ func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID stri
}
}
- return s.repo.UpdateGitLabOrganization(ctx, projectSFID, organizationName, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
+ return s.repo.UpdateGitLabOrganization(ctx, projectSFID, groupID, organizationName, groupFullPath, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
}
// DeleteGitLabOrganization deletes the specified GitLab organization
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 3cd339608..118218149 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -74,8 +74,7 @@ type GithubOrgRepo interface {
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, organizationName string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- UpdateGitLabOrganizationByExternalID(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
From fdc9aef7af639e5feb9b012577a4487fd82c29b6 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 08:28:28 -0700
Subject: [PATCH 0444/1276] Resolved GitLab Lookup Error With No OrgName Value
(#3180)
---
.../common/gitlab-repositories-add.yaml | 13 +++++++---
.../v2/repositories/gitlab_services.go | 26 ++++++++++++-------
cla-backend-go/v2/repositories/handlers.go | 6 +++--
3 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/cla-backend-go/swagger/common/gitlab-repositories-add.yaml b/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
index fa1e32043..33da7ac31 100644
--- a/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
+++ b/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
@@ -2,9 +2,7 @@
# SPDX-License-Identifier: MIT
type: object
-required:
- - gitlab_organization_name
- - cla_group_id
+description: 'GitLab repositories add model'
properties:
repository_gitlab_ids:
type: array
@@ -17,6 +15,15 @@ properties:
type: string
description: The organization name associated with this repository
example: 'The Linux Foundation/product/EasyCLA'
+ organization_external_id:
+ type: integer
+ description: The Gitlab Group/Organization external ID used by GitLab
+ example: 13050017
+ minimum: 1
+ organization_full_path:
+ type: string
+ description: The Gitlab Group/Organization full path
+ example: "linuxfoundation/product/easycla"
cla_group_id:
description: CLA Group ID
$ref: './common/properties/internal-id.yaml'
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 30027ca15..00450c7d8 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -28,13 +28,15 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
"functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
- "organizationName": utils.StringValue(input.GitlabOrganizationName),
- "claGroupID": utils.StringValue(input.ClaGroupID),
+ "organizationName": input.GitlabOrganizationName,
+ "claGroupID": input.ClaGroupID,
+ "groupFullPath": input.OrganizationFullPath,
+ "groupID": input.OrganizationExternalID,
}
- gitLabOrgModel, orgErr := s.glOrgRepo.GetGitLabOrganizationByName(ctx, utils.StringValue(input.GitlabOrganizationName))
+ gitLabOrgModel, orgErr := s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GitlabOrganizationName)
if orgErr != nil {
- msg := fmt.Sprintf("problem loading gitlab organization by name: %s, error: %v", utils.StringValue(input.GitlabOrganizationName), orgErr)
+ msg := fmt.Sprintf("problem loading gitlab organization by name: %s, error: %v", input.GitlabOrganizationName, orgErr)
log.WithFields(f).WithError(orgErr).Warn(msg)
return nil, errors.New(msg)
}
@@ -61,8 +63,8 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositoryName: project.Name,
RepositoryFullPath: project.PathWithNamespace,
RepositoryURL: project.WebURL,
- RepositoryOrganizationName: utils.StringValue(input.GitlabOrganizationName), // gitlab group/organization
- RepositoryCLAGroupID: utils.StringValue(input.ClaGroupID),
+ RepositoryOrganizationName: input.GitlabOrganizationName,
+ RepositoryCLAGroupID: input.ClaGroupID,
RepositoryType: utils.GitLabLower, // should always be gitlab
Enabled: false, // we don't enable by default
}
@@ -77,7 +79,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryAdded,
ProjectSFID: projectSFID,
- CLAGroupID: utils.StringValue(input.ClaGroupID),
+ CLAGroupID: input.ClaGroupID,
LfUsername: utils.GetUserNameFromContext(ctx),
EventData: &events.RepositoryAddedEventData{
RepositoryName: project.PathWithNamespace, // give the full path/name
@@ -85,7 +87,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
})
}
- return s.GitLabGetRepositoriesByOrganizationName(ctx, utils.StringValue(input.GitlabOrganizationName))
+ return s.GitLabGetRepositoriesByProjectSFID(ctx, projectSFID)
}
// GitLabAddRepositoriesByApp adds the GitLab repositories based on the application credentials
@@ -95,6 +97,8 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": gitLabOrgModel.ProjectSFID,
"organizationName": gitLabOrgModel.OrganizationName,
+ "groupFullPath": gitLabOrgModel.OrganizationFullPath,
+ "groupID": gitLabOrgModel.ExternalGroupID,
}
// Get the client
@@ -125,8 +129,10 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
// Build input to the add function
input := &v2Models.GitlabRepositoriesAdd{
- ClaGroupID: utils.StringRef(projectCLAGroupModel.ClaGroupID),
- GitlabOrganizationName: utils.StringRef(gitLabOrgModel.OrganizationName),
+ ClaGroupID: projectCLAGroupModel.ClaGroupID,
+ GitlabOrganizationName: gitLabOrgModel.OrganizationName,
+ OrganizationExternalID: int64(gitLabOrgModel.ExternalGroupID),
+ OrganizationFullPath: gitLabOrgModel.OrganizationFullPath,
RepositoryGitlabIds: listProjectIDs,
}
_, addRepoErr := s.GitLabAddRepositories(ctx, gitLabOrgModel.ProjectSFID, input)
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index b919a9c37..8f57c8c80 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -417,8 +417,10 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"authUser": authUser.UserName,
"authEmail": authUser.Email,
"projectSFID": params.ProjectSFID,
- "organizationName": utils.StringValue(params.GitlabRepositoriesAdd.GitlabOrganizationName),
- "claGroupID": utils.StringValue(params.GitlabRepositoriesAdd.ClaGroupID),
+ "organizationName": params.GitlabRepositoriesAdd.GitlabOrganizationName,
+ "claGroupID": params.GitlabRepositoriesAdd.ClaGroupID,
+ "groupFullPath": params.GitlabRepositoriesAdd.OrganizationFullPath,
+ "groupID": params.GitlabRepositoriesAdd.OrganizationExternalID,
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
From f585b3bfc5061f7b83d3108d3b7c665a14341b98 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 10:20:54 -0700
Subject: [PATCH 0445/1276] GitLab Debug/Cleanup (#3182)
Signed-off-by: David Deal
---
.../v2/gitlab_organizations/handlers.go | 29 +++++++++++++
.../v2/gitlab_organizations/repository.go | 5 ++-
.../v2/gitlab_organizations/service.go | 42 ++++++++++---------
3 files changed, 54 insertions(+), 22 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 994f49f51..846f51fe1 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -7,6 +7,8 @@ import (
"errors"
"fmt"
"net/http"
+ "net/url"
+ "regexp"
"strings"
"github.com/go-openapi/runtime"
@@ -122,6 +124,33 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequest(reqID, msg))
}
+ // Clean up/filter the Group Full Path, if needed
+ if params.Body.GroupFullPath != "" {
+ r, regexErr := regexp.Compile(`^http(s)?://`)
+ if regexErr != nil {
+ msg := fmt.Sprintf("invalid regex for group full path, error: %+v", regexErr)
+ log.WithFields(f).WithError(regexErr).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationInternalServerError().WithPayload(
+ utils.ErrorResponseInternalServerErrorWithError(reqID, msg, regexErr))
+ }
+ if r.MatchString(params.Body.GroupFullPath) {
+ groupWithUrl, urlParseErr := url.Parse(params.Body.GroupFullPath)
+ if urlParseErr != nil {
+ msg := fmt.Sprintf("invalid group full path provided, error: %+v", urlParseErr)
+ log.WithFields(f).WithError(urlParseErr).Warn(msg)
+ return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
+ utils.ErrorResponseBadRequestWithError(reqID, msg, urlParseErr))
+ }
+ // Update the group full path value - just include the path and not the https://... part
+ params.Body.GroupFullPath = groupWithUrl.Path
+ }
+
+ // Remove leading slash
+ if strings.HasPrefix(params.Body.GroupFullPath, "/") {
+ params.Body.GroupFullPath = params.Body.GroupFullPath[1:]
+ }
+ }
+
if params.Body.AutoEnabled == nil {
msg := fmt.Sprintf("missing autoEnabled name in body: %+v", params.Body)
log.WithFields(f).Warn(msg)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index e00cbaa58..4c9872aef 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -129,8 +129,9 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
}
}
- log.WithFields(f).Debug("Existing GitLab organization with different project SFID - won't be able to update it - will return conflict")
- return nil, fmt.Errorf("record already exists")
+ msg := fmt.Sprintf("record already exists - existing GitLab group with a different project SFID - won't be able to update it")
+ log.WithFields(f).Debug(msg)
+ return nil, errors.New(msg)
}
// No existing records - create one
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 22da09b06..654bcde82 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -168,14 +168,6 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
"projectSFID": projectSFID,
}
- // Load the GitLab Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
- log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
- orgs, err := s.repo.GetGitLabOrganizations(ctx, projectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("problem loading gitlab organizations from the project service")
- return nil, err
- }
-
psc := v2ProjectService.GetClient()
log.WithFields(f).Debug("loading project details from the project service...")
projectServiceRecord, err := psc.GetProject(projectSFID)
@@ -193,16 +185,25 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
f["parentProjectSFID"] = parentProjectSFID
log.WithFields(f).Debug("located parentProjectID...")
+ // Load the GitLab Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
+ log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
+ orgList, err := s.repo.GetGitLabOrganizations(ctx, projectSFID)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("problem loading gitlab organizations from the project service")
+ return nil, err
+ }
+ log.WithFields(f).Debugf("loaded %d Gitlab organizations for projectSFID: %s", len(orgList.List), projectSFID)
+
// Our response model
out := &models.GitlabProjectOrganizations{
List: make([]*models.GitlabProjectOrganization, 0),
}
- orgmap := make(map[string]*models.GitlabProjectOrganization)
- for _, org := range orgs.List {
+ orgMap := make(map[string]*models.GitlabProjectOrganization)
+ for _, org := range orgList.List {
autoEnabledCLAGroupName := ""
if org.AutoEnabledClaGroupID != "" {
- log.WithFields(f).Debugf("Loading CLA Group by ID: %s to obtain the name for GitLab auth enabled CLA Group response", org.AutoEnabledClaGroupID)
+ log.WithFields(f).Debugf("loading CLA Group by ID: %s to obtain the name for GitLab auth enabled CLA Group response", org.AutoEnabledClaGroupID)
claGroupMode, claGroupLookupErr := s.claGroupRepository.GetCLAGroup(ctx, org.AutoEnabledClaGroupID)
if claGroupLookupErr != nil {
log.WithFields(f).WithError(claGroupLookupErr).Warnf("Unable to lookup CLA Group by ID: %s", org.AutoEnabledClaGroupID)
@@ -212,18 +213,19 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
}
}
+ log.WithFields(f).Debugf("loading GitLab organization by organization ID: %s", org.OrganizationID)
orgDetailed, orgErr := s.repo.GetGitLabOrganization(ctx, org.OrganizationID)
if orgErr != nil {
log.WithFields(f).Errorf("fetching gitlab org failed : %s : %v", org.OrganizationID, orgErr)
continue
}
- reposFromOrg, repoErr := s.v2GitRepoService.GitLabGetRepositoriesByOrganizationName(ctx, org.OrganizationName)
+ repoList, repoErr := s.v2GitRepoService.GitLabGetRepositoriesByProjectSFID(ctx, projectSFID)
if repoErr != nil {
- if errors.Is(repoErr, &utils.GitLabRepositoryNotFound{}) {
- log.WithFields(f).Debugf("no repositories onboarded for GitLab Org: %s", org.OrganizationName)
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ log.WithFields(f).WithError(repoErr).Debugf("no GitLab repositories onboarded for project : %s", projectSFID)
} else {
- log.WithFields(f).Debugf("unexpected error while fetching gitlab org repositories for GitLab Org: %s : %v", org.OrganizationName, repoErr)
+ log.WithFields(f).WithError(repoErr).Debugf("unexpected error while fetching GitLab group repositories for project: %s, error: %v", projectSFID, repoErr)
}
}
@@ -236,9 +238,9 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
OrganizationFullPath: org.OrganizationFullPath,
OrganizationExternalID: org.OrganizationExternalID,
InstallationURL: buildInstallationURL(org.OrganizationID, orgDetailed.AuthState),
- BranchProtectionEnabled: false,
- ConnectionStatus: "", // updated below
- Repositories: []*models.GitlabProjectRepository{},
+ BranchProtectionEnabled: false, // TODO review this - why not modeled?
+ ConnectionStatus: "", // updated below
+ Repositories: []*models.GitlabProjectRepository{}, // updated below
}
if orgDetailed.AuthInfo == "" {
@@ -254,7 +256,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
log.WithFields(f).Errorf("using gitlab client for gitlab org : %s failed : %v", org.OrganizationID, clientErr)
rorg.ConnectionStatus = utils.ConnectionFailure
} else {
- rorg.Repositories = s.updateRepositoryStatus(glClient, toGitLabProjectResponse(reposFromOrg))
+ rorg.Repositories = s.updateRepositoryStatus(glClient, toGitLabProjectResponse(repoList))
user, _, userErr := glClient.Users.CurrentUser()
if userErr != nil {
@@ -268,7 +270,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
}
}
- orgmap[org.OrganizationName] = rorg
+ orgMap[org.OrganizationName] = rorg
out.List = append(out.List, rorg)
}
From 88ccf5e358c7dc2cd3b1d5cffb59ebb91394527c Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 11:22:32 -0700
Subject: [PATCH 0446/1276] Resolved GitLab Query Issue + Added Go Routine
(#3183)
---
.../v2/repositories/gitlab_services.go | 147 +++++++++++++-----
1 file changed, 108 insertions(+), 39 deletions(-)
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 00450c7d8..f5d5a7c77 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -9,6 +9,8 @@ import (
"fmt"
"strconv"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/common"
+
"github.com/communitybridge/easycla/cla-backend-go/events"
v2GitLabOrg "github.com/communitybridge/easycla/cla-backend-go/v2/common"
@@ -34,57 +36,119 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
"groupID": input.OrganizationExternalID,
}
- gitLabOrgModel, orgErr := s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GitlabOrganizationName)
- if orgErr != nil {
- msg := fmt.Sprintf("problem loading gitlab organization by name: %s, error: %v", input.GitlabOrganizationName, orgErr)
- log.WithFields(f).WithError(orgErr).Warn(msg)
+ var gitLabOrgModel *common.GitLabOrganization
+ var getOrgErr error
+ if input.GitlabOrganizationName != "" {
+ gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GitlabOrganizationName)
+ if getOrgErr != nil {
+ msg := fmt.Sprintf("problem loading GitLab organization by name: %s, error: %v", input.GitlabOrganizationName, getOrgErr)
+ log.WithFields(f).WithError(getOrgErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ } else if input.OrganizationFullPath != "" {
+ gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
+ if getOrgErr != nil {
+ msg := fmt.Sprintf("problem loading GitLab organization by full path: %s, error: %v", input.OrganizationFullPath, getOrgErr)
+ log.WithFields(f).WithError(getOrgErr).Warn(msg)
+ return nil, errors.New(msg)
+ }
+ }
+ if gitLabOrgModel == nil {
+ msg := fmt.Sprintf("problem loading GitLab organization by name '%s' or full path '%s'", input.GitlabOrganizationName, input.OrganizationFullPath)
+ log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
+ log.WithFields(f).Debugf("successfully loading GitLab group/organization")
// Get the client
gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
if err != nil {
- return nil, fmt.Errorf("initializing gitlab client : %v", err)
+ return nil, fmt.Errorf("initializing GitLab client : %v", err)
}
- for _, gitLabProjectID := range input.RepositoryGitlabIds {
- project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, int(gitLabProjectID)) // ok to down-cast as the IDs are not 64 bit
- if getProjectErr != nil {
- return nil, fmt.Errorf("unable to load project by ID: %d, error: %v", int(gitLabProjectID), getProjectErr)
- }
+ type GitLabAddRepositoryResponse struct {
+ RepositoryName string
+ RepositoryFullPath string
+ Error error
+ }
+ addRepoRespChan := make(chan *GitLabAddRepositoryResponse, len(input.RepositoryGitlabIds))
- // Convert int to string
- repositoryExternalIDString := strconv.Itoa(project.ID)
-
- inputDBModel := &repoModels.RepositoryDBModel{
- RepositorySfdcID: projectSFID,
- ProjectSFID: projectSFID,
- RepositoryExternalID: repositoryExternalIDString,
- RepositoryName: project.Name,
- RepositoryFullPath: project.PathWithNamespace,
- RepositoryURL: project.WebURL,
- RepositoryOrganizationName: input.GitlabOrganizationName,
- RepositoryCLAGroupID: input.ClaGroupID,
- RepositoryType: utils.GitLabLower, // should always be gitlab
- Enabled: false, // we don't enable by default
- }
+ // Add each repo - could be a lot of repos, so we run this in a go routine
+ for _, gitLabProjectID := range input.RepositoryGitlabIds {
+ go func(gitLabProjectID int) {
+ project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, gitLabProjectID)
+ if getProjectErr != nil {
+ newErr := fmt.Errorf("unable to load GitLab project using ID: %d, error: %v", gitLabProjectID, getProjectErr)
+ log.WithFields(f).WithError(newErr)
+ addRepoRespChan <- &GitLabAddRepositoryResponse{
+ Error: newErr,
+ }
+ return
+ }
+
+ // Convert int to string
+ repositoryExternalIDString := strconv.Itoa(project.ID)
+
+ inputDBModel := &repoModels.RepositoryDBModel{
+ RepositorySfdcID: projectSFID,
+ ProjectSFID: projectSFID,
+ RepositoryExternalID: repositoryExternalIDString,
+ RepositoryName: project.Name,
+ RepositoryFullPath: project.PathWithNamespace,
+ RepositoryURL: project.WebURL,
+ RepositoryOrganizationName: input.GitlabOrganizationName,
+ RepositoryCLAGroupID: input.ClaGroupID,
+ RepositoryType: utils.GitLabLower, // should always be gitlab
+ Enabled: false, // we don't enable by default
+ }
+
+ repoModel, addErr := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, inputDBModel)
+ if addErr != nil || repoModel == nil {
+ log.WithFields(f).WithError(addErr).Warnf("problem adding GitLab repository with name: %s, error: %+v", project.PathWithNamespace, addErr)
+ addRepoRespChan <- &GitLabAddRepositoryResponse{
+ Error: addErr,
+ }
+ return
+ }
+
+ // Log the event
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryAdded,
+ ProjectSFID: projectSFID,
+ CLAGroupID: input.ClaGroupID,
+ LfUsername: utils.GetUserNameFromContext(ctx),
+ EventData: &events.RepositoryAddedEventData{
+ RepositoryName: project.PathWithNamespace, // give the full path/name
+ },
+ })
+ addRepoRespChan <- &GitLabAddRepositoryResponse{
+ RepositoryName: repoModel.RepositoryName,
+ RepositoryFullPath: repoModel.RepositoryFullPath,
+ Error: nil,
+ }
+ }(int(gitLabProjectID)) // ok to down cast
+ }
- _, addErr := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, inputDBModel)
- if addErr != nil {
- log.WithFields(f).WithError(addErr).Warnf("problem adding GitLab repository with name: %s, error: %+v", project.PathWithNamespace, addErr)
- return nil, addErr
+ // Wait for the go routines to finish and load up the results
+ log.WithFields(f).Debug("waiting for add repos to finish...")
+ var lastErr error
+ for range input.RepositoryGitlabIds {
+ select {
+ case response := <-addRepoRespChan:
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warn(response.Error.Error())
+ lastErr = response.Error
+ } else {
+ log.WithFields(f).Debugf("added repo: %s with full path: %s", response.RepositoryName, response.RepositoryFullPath)
+ }
+ case <-ctx.Done():
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for CLA Groups to load timeouted")
+ lastErr = fmt.Errorf("cla group laoding failed : %v", ctx.Err())
}
+ }
- // Log the event
- s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryAdded,
- ProjectSFID: projectSFID,
- CLAGroupID: input.ClaGroupID,
- LfUsername: utils.GetUserNameFromContext(ctx),
- EventData: &events.RepositoryAddedEventData{
- RepositoryName: project.PathWithNamespace, // give the full path/name
- },
- })
+ if lastErr != nil {
+ return nil, lastErr
}
return s.GitLabGetRepositoriesByProjectSFID(ctx, projectSFID)
@@ -135,16 +199,21 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
OrganizationFullPath: gitLabOrgModel.OrganizationFullPath,
RepositoryGitlabIds: listProjectIDs,
}
+ log.WithFields(f).Debugf("adding %d GitLab repositories", len(listProjectIDs))
_, addRepoErr := s.GitLabAddRepositories(ctx, gitLabOrgModel.ProjectSFID, input)
if addRepoErr != nil {
+ log.WithFields(f).WithError(addRepoErr).Warnf("problem adding %d GitLab repositories", len(listProjectIDs))
return nil, addRepoErr
}
// Return the list of repos to caller
+ log.WithFields(f).Debugf("fetching complete repository list by project SFID: %s", gitLabOrgModel.ProjectSFID)
dbModels, getRepoErr := s.gitV2Repository.GitHubGetRepositoriesByProjectSFID(ctx, gitLabOrgModel.ProjectSFID)
if getRepoErr != nil {
+ log.WithFields(f).WithError(getRepoErr).Warnf("problem fetching repositories by project SFID: %s", gitLabOrgModel.ProjectSFID)
return nil, getRepoErr
}
+
return dbModelsToGitLabRepositories(dbModels)
}
From d75ceb1b83df776ccbdd6390551f3cf561de5f4c Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 12:42:30 -0700
Subject: [PATCH 0447/1276] Added Org Name in Callback Workflow (#3184)
Signed-off-by: David Deal
---
.../v2/gitlab_organizations/repository.go | 78 +++++++++++--------
.../v2/gitlab_organizations/service.go | 2 +-
.../v2/repositories/gitlab_services.go | 6 +-
cla-backend-go/v2/repositories/service.go | 4 +-
4 files changed, 52 insertions(+), 38 deletions(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 4c9872aef..7a077cf25 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -20,7 +20,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
- models2 "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
@@ -40,13 +40,13 @@ const (
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
- AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error)
- GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error)
+ AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, groupName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
+ GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
- UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
+ UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
@@ -68,7 +68,7 @@ func NewRepository(awsSession *session.Session, stage string) RepositoryInterfac
}
// AddGitLabOrganization adds the specified values to the GitLab Group/Org table
-func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*models2.GitlabOrganization, error) {
+func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -138,13 +138,13 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
_, currentTime := utils.CurrentTime()
organizationID, err := uuid.NewV4()
if err != nil {
- log.WithFields(f).WithError(err).Warnf("Unable to generate a UUID for gitlab org, error: %v", err)
+ log.WithFields(f).WithError(err).Warnf("Unable to generate a UUID for gitlab org, error: %v2Models", err)
return nil, err
}
authStateNonce, err := uuid.NewV4()
if err != nil {
- log.WithFields(f).WithError(err).Warnf("Unable to generate a auth nonce UUID for gitlab org, error: %v", err)
+ log.WithFields(f).WithError(err).Warnf("Unable to generate a auth nonce UUID for gitlab org, error: %v2Models", err)
return nil, err
}
@@ -196,7 +196,7 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
}
// GetGitLabOrganizations get GitLab organizations based on the project SFID
-func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models2.GitlabOrganizations, error) {
+func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizations",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -212,7 +212,7 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
- log.WithFields(f).Warnf("problem building query expression, error: %+v", err)
+ log.WithFields(f).Warnf("problem building query expression, error: %+v2Models", err)
return nil, err
}
@@ -235,8 +235,8 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
if len(results.Items) == 0 {
log.WithFields(f).Debug("no results from query")
- return &models2.GitlabOrganizations{
- List: []*models2.GitlabOrganization{},
+ return &v2Models.GitlabOrganizations{
+ List: []*v2Models.GitlabOrganization{},
}, nil
}
@@ -248,7 +248,7 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
log.WithFields(f).Debug("building response model...")
gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
- return &models2.GitlabOrganizations{List: gitlabOrgList}, nil
+ return &v2Models.GitlabOrganizations{List: gitlabOrgList}, nil
}
// GetGitLabOrganizationByName get GitLab organization by name
@@ -293,7 +293,7 @@ func (repo *Repository) GetGitLabOrganizationByName(ctx context.Context, gitLabO
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
return nil, err
}
@@ -341,7 +341,7 @@ func (repo *Repository) GetGitLabOrganizationByExternalID(ctx context.Context, g
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
return nil, err
}
@@ -389,7 +389,7 @@ func (repo *Repository) GetGitLabOrganizationByFullPath(ctx context.Context, gro
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v", err)
+ log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
return nil, err
}
@@ -424,27 +424,28 @@ func (repo *Repository) GetGitLabOrganization(ctx context.Context, gitLabOrganiz
var org common.GitLabOrganization
err = dynamodbattribute.UnmarshalMap(result.Item, &org)
if err != nil {
- log.WithFields(f).Warnf("error unmarshalling organization table data, error: %v", err)
+ log.WithFields(f).Warnf("error unmarshalling organization table data, error: %v2Models", err)
return nil, err
}
return &org, nil
}
// UpdateGitLabOrganizationAuth updates the specified Gitlab organization oauth info
-func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error {
+func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationAuth",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "organizationID": organizationID,
- "organizationFullPath": organizationFullPath,
- "organizationURL": organizationURL,
- "tableName": repo.gitlabOrgTableName,
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationAuth",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "organizationID": organizationID,
+ "groupName": groupName,
+ "groupFullPath": groupFullPath,
+ "organizationURL": organizationURL,
+ "tableName": repo.gitlabOrgTableName,
}
_, currentTime := utils.CurrentTime()
gitlabOrg, lookupErr := repo.GetGitLabOrganization(ctx, organizationID)
if lookupErr != nil || gitlabOrg == nil {
- log.WithFields(f).Warnf("error looking up Gitlab organization by id: %s, error: %+v", organizationID, lookupErr)
+ log.WithFields(f).Warnf("error looking up Gitlab organization by id: %s, error: %+v2Models", organizationID, lookupErr)
return lookupErr
}
@@ -463,7 +464,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
S: aws.String(organizationURL),
},
":fp": {
- S: aws.String(organizationFullPath),
+ S: aws.String(groupFullPath),
},
":m": {
S: aws.String(currentTime),
@@ -472,9 +473,18 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
N: aws.String(strconv.Itoa(gitLabGroupID)),
},
}
-
updateExpression := "SET #A = :a, #U = :u, #FP = :fp, #M = :m, #P = :p"
+ if groupName != "" {
+ expressionAttributeNames["#N"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
+ expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(groupName)}
+ updateExpression = fmt.Sprintf("%s, #N = :n ", updateExpression)
+
+ expressionAttributeNames["#NL"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
+ expressionAttributeValues[":nl"] = &dynamodb.AttributeValue{S: aws.String(strings.ToLower(groupName))}
+ updateExpression = fmt.Sprintf("%s, #NL = :nl ", updateExpression)
+ }
+
input := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
GitLabOrganizationsOrganizationIDColumn: {
@@ -490,7 +500,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
log.WithFields(f).Debug("updating gitlab organization record...")
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
- log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v", updateErr)
+ log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v2Models", updateErr)
return updateErr
}
@@ -518,7 +528,7 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v", groupID, groupFullPath)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v2Models", groupID, groupFullPath)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
@@ -526,7 +536,7 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v", groupFullPath, getErr)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v2Models", groupFullPath, getErr)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
@@ -587,10 +597,10 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
TableName: aws.String(repo.gitlabOrgTableName),
}
- log.WithFields(f).Debugf("updating GitLab organization record: %+v", input)
+ log.WithFields(f).Debugf("updating GitLab organization record: %+v2Models", input)
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
- log.WithFields(f).Warnf("unable to update GitLab organization record, error: %+v", updateErr)
+ log.WithFields(f).Warnf("unable to update GitLab organization record, error: %+v2Models", updateErr)
return updateErr
}
@@ -609,7 +619,7 @@ func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFI
var gitlabOrganizationID string
orgs, orgErr := repo.GetGitLabOrganizations(ctx, projectSFID)
if orgErr != nil {
- errMsg := fmt.Sprintf("gitlab organization is not found using projectSFID: %s, error: %+v", projectSFID, orgErr)
+ errMsg := fmt.Sprintf("gitlab organization is not found using projectSFID: %s, error: %+v2Models", projectSFID, orgErr)
log.WithFields(f).Warn(errMsg)
return errors.New(errMsg)
}
@@ -653,7 +663,7 @@ func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFI
},
)
if err != nil {
- errMsg := fmt.Sprintf("error deleting gitlab organization: %s - %+v", gitlabOrgName, err)
+ errMsg := fmt.Sprintf("error deleting gitlab organization: %s - %+v2Models", gitlabOrgName, err)
log.WithFields(f).Warnf(errMsg)
return errors.New(errMsg)
}
@@ -661,7 +671,7 @@ func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFI
return nil
}
-func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*common.GitLabOrganization) []*models2.GitlabOrganization {
+func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*common.GitLabOrganization) []*v2Models.GitlabOrganization {
f := logrus.Fields{
"functionName": "buildGitlabOrganizationListModels",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 654bcde82..b23e824f2 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -345,7 +345,7 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
if (gitLabOrgModel.ExternalGroupID > 0 && g.ID == gitLabOrgModel.ExternalGroupID) ||
(gitLabOrgModel.OrganizationFullPath != "" && g.FullPath == gitLabOrgModel.OrganizationFullPath) {
- updateGitLabOrgErr := s.repo.UpdateGitLabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.FullPath, g.WebURL)
+ updateGitLabOrgErr := s.repo.UpdateGitLabOrganizationAuth(ctx, gitLabOrganizationID, g.ID, authInfoEncrypted, g.Name, g.FullPath, g.WebURL)
if updateGitLabOrgErr != nil {
return updateGitLabOrgErr
}
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index f5d5a7c77..e5713c6e8 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -39,6 +39,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
var gitLabOrgModel *common.GitLabOrganization
var getOrgErr error
if input.GitlabOrganizationName != "" {
+ log.WithFields(f).Debugf("fetching GitLab organization by name: %s", input.GitlabOrganizationName)
gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GitlabOrganizationName)
if getOrgErr != nil {
msg := fmt.Sprintf("problem loading GitLab organization by name: %s, error: %v", input.GitlabOrganizationName, getOrgErr)
@@ -46,6 +47,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
return nil, errors.New(msg)
}
} else if input.OrganizationFullPath != "" {
+ log.WithFields(f).Debugf("fetching GitLab organization by full path: %s", input.OrganizationFullPath)
gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
if getOrgErr != nil {
msg := fmt.Sprintf("problem loading GitLab organization by full path: %s, error: %v", input.OrganizationFullPath, getOrgErr)
@@ -58,7 +60,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
- log.WithFields(f).Debugf("successfully loading GitLab group/organization")
+ log.WithFields(f).Debugf("successfully loaded GitLab group/organization")
// Get the client
gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
@@ -76,6 +78,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
// Add each repo - could be a lot of repos, so we run this in a go routine
for _, gitLabProjectID := range input.RepositoryGitlabIds {
go func(gitLabProjectID int) {
+ log.WithFields(f).Debugf("loading GitLab project from GitLab using projectID: %d...", gitLabProjectID)
project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, gitLabProjectID)
if getProjectErr != nil {
newErr := fmt.Errorf("unable to load GitLab project using ID: %d, error: %v", gitLabProjectID, getProjectErr)
@@ -85,6 +88,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
}
return
}
+ log.WithFields(f).Debugf("loaded GitLab project from GitLab using projectID: %d", gitLabProjectID)
// Convert int to string
repositoryExternalIDString := strconv.Itoa(project.ID)
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 118218149..877ee9829 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -67,13 +67,13 @@ type ServiceInterface interface {
// GithubOrgRepo redefine the interface here to avoid circular dependency issues
type GithubOrgRepo interface {
- AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
+ AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, groupName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
- UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, organizationFullPath, organizationURL string) error
+ UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
}
From 2ff4f665470021953ba4e155048ad43c597041be Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 13:08:50 -0700
Subject: [PATCH 0448/1276] Resolved Duplicate Key Issue in GL Repo (#3185)
Signed-off-by: David Deal
---
cla-backend-go/v2/gitlab_organizations/repository.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 7a077cf25..54d206003 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -480,7 +480,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(groupName)}
updateExpression = fmt.Sprintf("%s, #N = :n ", updateExpression)
- expressionAttributeNames["#NL"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
+ expressionAttributeNames["#NL"] = aws.String(GitLabOrganizationsOrganizationNameLowerColumn)
expressionAttributeValues[":nl"] = &dynamodb.AttributeValue{S: aws.String(strings.ToLower(groupName))}
updateExpression = fmt.Sprintf("%s, #NL = :nl ", updateExpression)
}
From 585abee79ab5a7738fbb99d67e23928cdb90d250 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 20 Aug 2021 17:07:01 -0700
Subject: [PATCH 0449/1276] Updated GitLab Repo Enrol Unenroll (#3186)
---
cla-backend-go/events/event_data.go | 31 +-
cla-backend-go/go.mod | 25 --
cla-backend-go/go.sum | 300 ------------------
cla-backend-go/repositories/constants.go | 6 +
cla-backend-go/swagger/cla.v2.yaml | 30 +-
...d.yaml => gitlab-repositories-enable.yaml} | 16 +-
cla-backend-go/utils/errors.go | 26 +-
.../v2/repositories/gitlab_services.go | 70 +++-
cla-backend-go/v2/repositories/handlers.go | 99 +++---
cla-backend-go/v2/repositories/repository.go | 97 ++++--
cla-backend-go/v2/repositories/service.go | 8 +-
11 files changed, 272 insertions(+), 436 deletions(-)
rename cla-backend-go/swagger/common/{gitlab-repositories-add.yaml => gitlab-repositories-enable.yaml} (94%)
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 130fc63fa..2175748a0 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -44,7 +44,8 @@ type RepositoryAddedEventData struct {
// RepositoryDisabledEventData event data model
type RepositoryDisabledEventData struct {
- RepositoryName string
+ RepositoryName string
+ RepositoryExternalID int64
}
// RepositoryRenamedEventData event data model
@@ -482,7 +483,20 @@ func (ed *RepositoryAddedEventData) GetEventDetailsString(args *LogEventArgs) (s
// GetEventDetailsString returns the details string for this event
func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was deleted for the project %s", ed.RepositoryName, args.ProjectName)
+ data := "The GitHub repository " // nolint
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if ed.RepositoryName != "" {
+ data = data + fmt.Sprintf(" with repository name %s", ed.RepositoryName)
+ }
+ if ed.RepositoryExternalID > 0 {
+ data = data + fmt.Sprintf(" with repository external ID %d", ed.RepositoryExternalID)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectSFID)
+ }
+ data = data + " was disabled"
if args.UserName != "" {
data = data + fmt.Sprintf(" by the user %s", args.UserName)
}
@@ -1452,13 +1466,20 @@ func (ed *RepositoryAddedEventData) GetEventSummaryString(args *LogEventArgs) (s
// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("The GitHub repository %s was deleted", ed.RepositoryName)
+ data := "The GitHub repository " // nolint
if args.CLAGroupName != "" {
data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
}
- if args.ProjectName != "" {
- data = data + fmt.Sprintf(" for the project %s", args.ProjectName)
+ if ed.RepositoryName != "" {
+ data = data + fmt.Sprintf(" with repository name %s", ed.RepositoryName)
+ }
+ if ed.RepositoryExternalID > 0 {
+ data = data + fmt.Sprintf(" with repository external ID %d", ed.RepositoryExternalID)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectSFID)
}
+ data = data + " was disabled"
if args.CompanyName != "" {
data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
}
diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod
index 1c4bfd89f..c737d1c57 100644
--- a/cla-backend-go/go.mod
+++ b/cla-backend-go/go.mod
@@ -16,16 +16,10 @@ require (
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/bradleyfalzon/ghinstallation v1.1.1
- github.com/coreos/bbolt v1.3.2 // indirect
- github.com/coreos/etcd v3.3.13+incompatible // indirect
- github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
- github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
- github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fnproject/fdk-go v0.0.2
github.com/gin-gonic/gin v1.7.2
- github.com/go-delve/delve v1.7.0 // indirect
github.com/go-openapi/errors v0.19.6
github.com/go-openapi/loads v0.19.5
github.com/go-openapi/runtime v0.19.19
@@ -40,50 +34,32 @@ require (
github.com/google/go-github/v37 v37.0.0
github.com/google/uuid v1.1.4
github.com/gorilla/sessions v1.2.1 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
- github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/imroc/req v0.3.0
github.com/jessevdk/go-flags v1.4.0
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0
- github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2
github.com/kr/pretty v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
- github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.1
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/mozillazg/request v0.8.0 // indirect
- github.com/myitcv/gobin v0.0.14 // indirect
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc
- github.com/peterh/liner v1.2.1 // indirect
- github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0 // indirect
- github.com/prometheus/client_golang v0.9.3 // indirect
- github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/rs/cors v1.7.0
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
github.com/sirupsen/logrus v1.8.1
- github.com/soheilhy/cmux v0.1.4 // indirect
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8
- github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
github.com/ugorji/go v1.2.6 // indirect
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
github.com/xanzy/go-gitlab v0.50.1
- github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
- go.etcd.io/bbolt v1.3.2 // indirect
- go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a // indirect
go.uber.org/ratelimit v0.1.0
- golang.org/x/arch v0.0.0-20210727222714-28578f966459 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
@@ -92,5 +68,4 @@ require (
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
google.golang.org/protobuf v1.27.1 // indirect
- gopkg.in/resty.v1 v1.12.0 // indirect
)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 690e61600..7a0dc864a 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -17,38 +17,28 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM=
github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUamMO8SWS1m4ZKJGGeh9lT985U=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
@@ -56,29 +46,17 @@ github.com/LF-Engineering/lfx-kit v0.1.25 h1:Bb3Snc72ppBmbS5CMoLBGFg1Tt7ZhZktZLJ
github.com/LF-Engineering/lfx-kit v0.1.25/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
github.com/LF-Engineering/lfx-models v0.6.44 h1:a4/6+Hc05caUCzd9eQnZIioZUhWxtgpfgVRuf/M2SRY=
github.com/LF-Engineering/lfx-models v0.6.44/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
-github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
-github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -92,71 +70,35 @@ github.com/aws/aws-sdk-go v1.36.27 h1:wc3xLJJHog2SwiqlLnrLUuct/n+dBjB45QhuZw2psV
github.com/aws/aws-sdk-go v1.36.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
-github.com/bketelsen/crypt v0.0.4 h1:w/jqZtC9YD4DS/Vp9GhWfWcCpuAL58oTnLoI8vE9YHU=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.1.0/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
-github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
-github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
-github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
@@ -165,18 +107,13 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
github.com/fnproject/fdk-go v0.0.2/go.mod h1:9m+nEyku9SqJAVJQsfZOZBQzFkCs+jvmbZJhvgDX4ts=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -185,22 +122,11 @@ github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e/go.mod h1:7cKuhb5qV2
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a h1:l4yNPeA/3kNJwE0uDBVXtFX8hfiHrlqkXBLPOrchWzk=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
-github.com/go-delve/delve v1.7.0 h1:MaWAD3LtvjE/LL98urSHPjaMT+OubpQ2sqF3R2Uj1rc=
-github.com/go-delve/delve v1.7.0/go.mod h1:2DpgGoHOW7r7MXyykmT7axp9IEEIc8EV/swa5m8rkbo=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -280,58 +206,38 @@ github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
-github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
-github.com/gobuffalo/flect v0.1.3 h1:3GQ53z7E3o00C/yy7Ko8VXqQXoJGLkrTQCLTF1EjoXU=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
-github.com/gobuffalo/genny v0.1.1 h1:iQ0D6SpNXIxu52WESsD+KoQ7af2e3nCfnSBoSF/hKe0=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
-github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211 h1:mSVZ4vj4khv+oThUfS+SQU3UuFIZ5Zo6UNcvK8E8Mz8=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
-github.com/gobuffalo/gogen v0.1.1 h1:dLg+zb+uOyd/mKeQUYIbwbNmfRsr9hd/WtYWepmayhI=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
-github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
-github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
-github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
-github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZCj6A=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
-github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
-github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/dep v0.5.4 h1:WfV5qbGwsBNUDhk+pfI6emWm7SdDFsnSWkqCMNG3BRs=
github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -361,10 +267,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -379,20 +283,15 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-dap v0.5.0 h1:RMHAVn5xeunBakYk65ggHXttk6qjZVdbmi+xhAoL2wY=
-github.com/google/go-dap v0.5.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3bOQ/tM=
github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -404,11 +303,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -417,70 +313,41 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979 h1:UsXWMy9j+GSCN/I1/Oyc4wGaeW2CDYqeqAkEvWPu+cs=
github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
-github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ=
@@ -488,7 +355,6 @@ github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94=
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
@@ -498,7 +364,6 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
-github.com/jmank88/nuts v0.4.0 h1:3rHp+7YcvtkTPohGBA++MwneB9OlX/rpORvleiRivMQ=
github.com/jmank88/nuts v0.4.0/go.mod h1:TKOSbm0p73pdAzgQ7lcZheG2cinZiXqy60KM5ooL3j8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -506,17 +371,12 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
-github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
-github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180128142709-bca911dae073/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -524,49 +384,31 @@ github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f h1:a3Vd00a20dTKLpyS2h
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f/go.mod h1:+7K7MqWi5xWI+s1LyB2g0Di71jZo27y+XOlmhNtV1Y0=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2 h1:McU3wXjBrKfJcOt2Pali5qEir9NLrqOh4EECzdWHknM=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2/go.mod h1:3mJ64RiWU2x9U6IigvcoVLra6LZQTOwMuHpk02OtOJc=
-github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
-github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
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=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
-github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -575,40 +417,22 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
-github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
-github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
-github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
-github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -621,141 +445,75 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9rNM=
github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/myitcv/gobin v0.0.14 h1:YkTUz0IeRspEJlly/+AXRBMA3GN7ArRVbsLJ1uYFwRk=
-github.com/myitcv/gobin v0.0.14/go.mod h1:GvHEiYCWroKI2KrMT+xQkHC3FC551wigVWeR4Sgg5P4=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI=
-github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd h1:b2wg8HW/u55DT7Y/vamdEn/jdvtsGkxzl+0+iHa5YmE=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.3.0 h1:yPHEatyQC4jN3vdfvqJXG7O9vfC6LhaAV1NEdYpP+h0=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc h1:JI2yIEkVFpe4eYIM/fTNtlIayTiGj4m+iku5JLx8uOY=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc/go.mod h1:3wwz3xi60q88WM0kKZeOJvdQ4YgW4Og7whEiodseWs8=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-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=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
-github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wkb8NefNCTRDEodfo6MtfH9BaO8ncMA=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429/go.mod h1:fK0DIsn9VGLYVur3nQ54Yz4LSLLCyDil0gzq5Y8Yzls=
-github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62Wd/ZOea4XYqE+y1sf2pdm+VItsc0c=
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -770,8 +528,6 @@ github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8 h1:xp/21gmSP
github.com/tencentyun/scf-go-lib v0.0.0-20200116145541-9a6ea1bf75b8/go.mod h1:K3DbqPpP2WE/9MWokWWzgFZcbgtMb9Wd5CYk9AAbEN8=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v0.0.0-20180129160544-d2b24cf3d3b4/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
@@ -779,37 +535,23 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
-github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
-github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862 h1:eg5xqGZGatsyRpVnFJkdeUWSFk46lDgkXLvOryv5ySg=
github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA=
github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ=
github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
-github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
-github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@@ -822,25 +564,13 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.starlark.net v0.0.0-20200821142938-949cc6f4b097/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
-go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a h1:wDtSCWGrX9tusypq2Qq9xzaA3Tf/+4D2KaWO+HQvGZE=
-go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
-golang.org/x/arch v0.0.0-20210727222714-28578f966459 h1:ECTRghTMeoUryGydSc+nr1o4M2i73DwlP4LFEDJb3II=
-golang.org/x/arch v0.0.0-20210727222714-28578f966459/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -865,7 +595,6 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -884,10 +613,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -897,15 +624,12 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -976,8 +700,6 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1012,7 +734,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1031,7 +752,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1047,7 +767,6 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1076,7 +795,6 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1108,7 +826,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1136,7 +853,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1187,7 +903,6 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1208,7 +923,6 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1224,22 +938,14 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1259,13 +965,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
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=
-rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/cla-backend-go/repositories/constants.go b/cla-backend-go/repositories/constants.go
index 40ab63317..69e937a88 100644
--- a/cla-backend-go/repositories/constants.go
+++ b/cla-backend-go/repositories/constants.go
@@ -3,12 +3,18 @@
package repositories
+// RepositoryIDColumn constant
+const RepositoryIDColumn = "repository_id"
+
// RepositoryNameColumn constant
const RepositoryNameColumn = "repository_name"
// RepositoryTypeColumn constant
const RepositoryTypeColumn = "repository_type"
+// RepositoryExternalIDColumn constant
+const RepositoryExternalIDColumn = "repository_external_id"
+
// RepositoryProjectIDColumn constant
const RepositoryProjectIDColumn = "project_sfid"
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 05ee68c21..24bd81f6d 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1763,10 +1763,10 @@ paths:
- gitlab-organizations
/project/{projectSFID}/gitlab/repositories:
- post:
- summary: Add a GitLab repository to the project
- description: Endpoint to add a GitLab repository for the project
- operationId: addProjectGitLabRepository
+ put:
+ summary: Enables GitLab repositories for the CLA Group
+ description: Endpoint to enable one or more GitLab repositories for the CLA Group
+ operationId: enableGitLabRepository
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-acl"
@@ -1777,9 +1777,9 @@ paths:
type: string
required: true
- in: body
- name: gitlab-repositories-add
+ name: gitlab-repositories-enable
schema:
- $ref: '#/definitions/gitlab-repositories-add'
+ $ref: '#/definitions/gitlab-repositories-enable'
required: true
responses:
'200':
@@ -1798,8 +1798,6 @@ paths:
$ref: '#/responses/forbidden'
'404':
$ref: '#/responses/not-found'
- '409':
- $ref: '#/responses/conflict'
'500':
$ref: '#/responses/internal-server-error'
tags:
@@ -1839,11 +1837,11 @@ paths:
tags:
- gitlab-repositories
- /project/{projectSFID}/gitlab/repositories/{repositoryID}:
+ /project/{projectSFID}/gitlab/repositories/{repositoryExternalID}:
delete:
- summary: Remove the GitLab repository from the project
- description: Endpoint to remove a GitLab repository from a project
- operationId: deleteProjectGitLabRepository
+ summary: Unenrolls the GitLab repository from the CLA Group
+ description: Endpoint to unenroll a GitLab repository from a CLA Group
+ operationId: unenrollGitLabRepository
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-acl"
@@ -1853,9 +1851,9 @@ paths:
in: path
type: string
required: true
- - name: repositoryID
+ - name: repositoryExternalID
in: path
- type: string
+ type: integer
required: true
responses:
'204':
@@ -4504,8 +4502,8 @@ definitions:
gitlab-repositories-list:
$ref: './common/gitlab-repositories-list.yaml'
- gitlab-repositories-add:
- $ref: './common/gitlab-repositories-add.yaml'
+ gitlab-repositories-enable:
+ $ref: './common/gitlab-repositories-enable.yaml'
# ---------------------------------------------------------------------------
# CLA Group Definitions
diff --git a/cla-backend-go/swagger/common/gitlab-repositories-add.yaml b/cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
similarity index 94%
rename from cla-backend-go/swagger/common/gitlab-repositories-add.yaml
rename to cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
index 33da7ac31..e28d91c14 100644
--- a/cla-backend-go/swagger/common/gitlab-repositories-add.yaml
+++ b/cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
@@ -2,15 +2,8 @@
# SPDX-License-Identifier: MIT
type: object
-description: 'GitLab repositories add model'
+description: 'GitLab repositories enable model'
properties:
- repository_gitlab_ids:
- type: array
- items:
- description: the repository external identifier, such as the GitLab ID of the repository
- type: integer
- minimum: 1
- example: 7
gitlab_organization_name:
type: string
description: The organization name associated with this repository
@@ -27,3 +20,10 @@ properties:
cla_group_id:
description: CLA Group ID
$ref: './common/properties/internal-id.yaml'
+ repository_gitlab_ids:
+ type: array
+ items:
+ description: the repository external identifier, such as the GitLab ID of the repository
+ type: integer
+ minimum: 1
+ example: 7
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index 3c80e7418..67193de67 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -354,12 +354,13 @@ func (e *GitHubRepositoryExists) Unwrap() error {
// GitLabRepositoryNotFound is an error model for a GitLab repository not found
type GitLabRepositoryNotFound struct {
- Message string
- OrganizationName string
- RepositoryName string
- ProjectSFID string
- CLAGroupID string
- Err error
+ Message string
+ OrganizationName string
+ RepositoryName string
+ RepositoryExternalID int64
+ ProjectSFID string
+ CLAGroupID string
+ Err error
}
// Error is an error string function for the GitHubRepositoryNotFound model
@@ -374,6 +375,9 @@ func (e *GitLabRepositoryNotFound) Error() string {
if e.RepositoryName != "" {
msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
}
+ if e.RepositoryExternalID > 0 {
+ msg = fmt.Sprintf("%s - repository external ID: %d ", msg, e.RepositoryExternalID)
+ }
if e.ProjectSFID != "" {
msg = fmt.Sprintf("%s - project SFID: %s ", msg, e.ProjectSFID)
}
@@ -394,9 +398,10 @@ func (e *GitLabRepositoryNotFound) Unwrap() error {
// GitLabDuplicateRepositoriesFound is an error model for a GitLab duplicate repositories found
type GitLabDuplicateRepositoriesFound struct {
- Message string
- RepositoryName string
- Err error
+ Message string
+ RepositoryName string
+ RepositoryExternalID int64
+ Err error
}
// Error is an error string function for the GitLabDuplicateRepositoriesFound model
@@ -408,6 +413,9 @@ func (e *GitLabDuplicateRepositoriesFound) Error() string {
if e.RepositoryName != "" {
msg = fmt.Sprintf("%s - repository: %s ", msg, e.RepositoryName)
}
+ if e.RepositoryExternalID > 0 {
+ msg = fmt.Sprintf("%s - repository external ID: %d ", msg, e.RepositoryExternalID)
+ }
if e.Err != nil {
msg = fmt.Sprintf("%s - error: %+v ", msg, e.Err.Error())
}
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index e5713c6e8..77bc099b0 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -25,7 +25,7 @@ import (
)
// GitLabAddRepositories service function
-func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesAdd) (*v2Models.GitlabRepositoriesList, error) {
+func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesEnable) (*v2Models.GitlabRepositoriesList, error) {
f := logrus.Fields{
"functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -146,8 +146,8 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
log.WithFields(f).Debugf("added repo: %s with full path: %s", response.RepositoryName, response.RepositoryFullPath)
}
case <-ctx.Done():
- log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for CLA Groups to load timeouted")
- lastErr = fmt.Errorf("cla group laoding failed : %v", ctx.Err())
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for add repositories timed out")
+ lastErr = fmt.Errorf("add repositories failed with timeout, error: %v", ctx.Err())
}
}
@@ -196,7 +196,7 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
}
// Build input to the add function
- input := &v2Models.GitlabRepositoriesAdd{
+ input := &v2Models.GitlabRepositoriesEnable{
ClaGroupID: projectCLAGroupModel.ClaGroupID,
GitlabOrganizationName: gitLabOrgModel.OrganizationName,
OrganizationExternalID: int64(gitLabOrgModel.ExternalGroupID),
@@ -241,6 +241,16 @@ func (s *Service) GitLabGetRepositoryByName(ctx context.Context, repositoryName
return dbModelToGitLabRepository(dbModel)
}
+// GitLabGetRepositoryByExternalID service function
+func (s *Service) GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*v2Models.GitlabRepository, error) {
+ dbModel, err := s.gitV2Repository.GitLabGetRepositoryByExternalID(ctx, repositoryExternalID)
+ if err != nil {
+ return nil, err
+ }
+
+ return dbModelToGitLabRepository(dbModel)
+}
+
// GitLabGetRepositoriesByProjectSFID service function
func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error) {
dbModel, err := s.gitV2Repository.GitHubGetRepositoriesByProjectSFID(ctx, projectSFID)
@@ -298,14 +308,58 @@ func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, o
}, nil
}
+// GitLabEnableRepositories assigns repos to
+func (s *Service) GitLabEnableRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64) error {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.gitlab_services.GitLabEnableRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ type GitLabUpdateRepositoryResponse struct {
+ RepositoryID int64
+ Error error
+ }
+ updateRepoChan := make(chan *GitLabUpdateRepositoryResponse, len(repositoryIDList))
+
+ for _, repoID := range repositoryIDList {
+ go func(claGroupID string, repoID int64) {
+ updateErr := s.GitLabEnableRepository(ctx, claGroupID, repoID)
+ updateRepoChan <- &GitLabUpdateRepositoryResponse{
+ RepositoryID: repoID,
+ Error: updateErr,
+ }
+ }(claGroupID, repoID)
+ }
+
+ // Wait for the go routines to finish and load up the results
+ log.WithFields(f).Debug("waiting for update repos to finish...")
+ var lastErr error
+ for range repositoryIDList {
+ select {
+ case response := <-updateRepoChan:
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warn(response.Error.Error())
+ lastErr = response.Error
+ } else {
+ log.WithFields(f).Debugf("updated repo with ID: %d", response.RepositoryID)
+ }
+ case <-ctx.Done():
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for update GitLab repos timed out")
+ lastErr = fmt.Errorf("waiting for update GitLab repositories timed out, error: %v", ctx.Err())
+ }
+ }
+
+ return lastErr
+}
+
// GitLabEnableRepository service function
-func (s *Service) GitLabEnableRepository(ctx context.Context, repositoryID string) error {
- return s.gitV2Repository.GitLabEnableRepositoryByID(ctx, repositoryID)
+func (s *Service) GitLabEnableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
+ return s.gitV2Repository.GitLabEnableRepositoryByID(ctx, claGroupID, repositoryExternalID)
}
// GitLabDisableRepository service function
-func (s *Service) GitLabDisableRepository(ctx context.Context, repositoryID string) error {
- return s.gitV2Repository.GitLabDisableRepositoryByID(ctx, repositoryID)
+func (s *Service) GitLabDisableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
+ return s.gitV2Repository.GitLabDisableRepositoryByID(ctx, claGroupID, repositoryExternalID)
}
// GitLabDisableCLAGroupRepositories service function
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 8f57c8c80..afa3223d9 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -8,6 +8,9 @@ import (
"fmt"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_organizations"
+ project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_repositories"
"github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
@@ -406,102 +409,112 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_repositories.NewGetProjectGitLabRepositoriesOK().WithPayload(response)
})
- api.GitlabRepositoriesAddProjectGitLabRepositoryHandler = gitlab_repositories.AddProjectGitLabRepositoryHandlerFunc(
- func(params gitlab_repositories.AddProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
+ api.GitlabRepositoriesEnableGitLabRepositoryHandler = gitlab_repositories.EnableGitLabRepositoryHandlerFunc(
+ func(params gitlab_repositories.EnableGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
- "functionName": "v2.repositories.handlers.GitlabRepositoriesAddProjectGitLabRepositoryHandler",
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesEnableGitLabRepositoryHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"authUser": authUser.UserName,
"authEmail": authUser.Email,
"projectSFID": params.ProjectSFID,
- "organizationName": params.GitlabRepositoriesAdd.GitlabOrganizationName,
- "claGroupID": params.GitlabRepositoriesAdd.ClaGroupID,
- "groupFullPath": params.GitlabRepositoriesAdd.OrganizationFullPath,
- "groupID": params.GitlabRepositoriesAdd.OrganizationExternalID,
+ "organizationName": params.GitlabRepositoriesEnable.GitlabOrganizationName,
+ "claGroupID": params.GitlabRepositoriesEnable.ClaGroupID,
+ "groupFullPath": params.GitlabRepositoriesEnable.OrganizationFullPath,
+ "groupID": params.GitlabRepositoriesEnable.OrganizationExternalID,
+ }
+
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Add GitLab Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Add GitLab Repositories for Project '%s' with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
- return gitlab_repositories.NewAddProjectGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ return gitlab_repositories.NewEnableGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- if len(params.GitlabRepositoriesAdd.RepositoryGitlabIds) == 0 {
+ if len(params.GitlabRepositoriesEnable.RepositoryGitlabIds) == 0 {
msg := "missing repository GitLab ID values"
- return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- log.WithFields(f).Debugf("Adding GitLab repository for project: %s", params.ProjectSFID)
- result, err := service.GitLabAddRepositories(ctx, params.ProjectSFID, params.GitlabRepositoriesAdd)
- if err != nil {
- if _, ok := err.(*utils.GitLabRepositoryExists); ok {
- msg := fmt.Sprintf("unable to add repository - repository already exists for projectSFID: %s, err: %+v", params.ProjectSFID, err)
- log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewAddProjectGitLabRepositoryConflict().WithXRequestID(reqID).WithPayload(utils.ErrorResponseConflictWithError(reqID, msg, err))
- }
+ log.WithFields(f).Debugf("assigning GitLab repository for project: %s and CLA Group: %s", params.ProjectSFID, params.GitlabRepositoriesEnable.ClaGroupID)
+ enableErr := service.GitLabEnableRepositories(ctx, params.GitlabRepositoriesEnable.ClaGroupID, params.GitlabRepositoriesEnable.RepositoryGitlabIds)
+ if enableErr != nil {
msg := fmt.Sprintf("problem adding GitLab repositories for projectSFID: %s", params.ProjectSFID)
- log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewAddProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ log.WithFields(f).WithError(enableErr).Warn(msg)
+ return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, enableErr))
+ }
+
+ repoList, getErr := service.GitLabGetRepositoriesByProjectSFID(ctx, params.ProjectSFID)
+ if getErr != nil {
+ msg := fmt.Sprintf("problem fetching GitLab repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(getErr).Warn(msg)
+ return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, getErr))
}
- return gitlab_repositories.NewAddProjectGitLabRepositoryOK().WithPayload(result)
+ return gitlab_repositories.NewEnableGitLabRepositoryOK().WithPayload(repoList)
})
- api.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler = gitlab_repositories.DeleteProjectGitLabRepositoryHandlerFunc(
- func(params gitlab_repositories.DeleteProjectGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
+ api.GitlabRepositoriesUnenrollGitLabRepositoryHandler = gitlab_repositories.UnenrollGitLabRepositoryHandlerFunc(
+ func(params gitlab_repositories.UnenrollGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
- "functionName": "v2.repositories.handlers.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
- "projectSFID": params.ProjectSFID,
- "repositoryID": params.RepositoryID,
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "repositoryExternalID": params.RepositoryExternalID,
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Delete Gitlab Repositories with Project scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Unenroll Gitlab Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
log.WithFields(f).Debug(msg)
- return gitlab_repositories.NewDeleteProjectGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ return gitlab_repositories.NewUnenrollGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- ghRepo, err := service.GitLabGetRepository(ctx, params.RepositoryID)
+ ghRepo, err := service.GitLabGetRepositoryByExternalID(ctx, params.RepositoryExternalID)
if err != nil {
if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
- msg := fmt.Sprintf("repository not found for projectSFID: %s", params.ProjectSFID)
+ msg := fmt.Sprintf("repository not found for repository external ID: %d", params.RepositoryExternalID)
log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewDeleteProjectGitLabRepositoryNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ return gitlab_repositories.NewUnenrollGitLabRepositoryNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
}
- msg := fmt.Sprintf("problem looking up repository for projectSFID: %s", params.ProjectSFID)
+ msg := fmt.Sprintf("problem looking up repository using the repostiory external ID: %d", params.RepositoryExternalID)
log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewDeleteProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ return gitlab_repositories.NewUnenrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- err = service.GitLabDisableRepository(ctx, params.RepositoryID)
+ err = service.GitLabDisableRepository(ctx, "", params.RepositoryExternalID)
if err != nil {
msg := fmt.Sprintf("problem disabling repository for projectSFID: %s, error: %+v", params.ProjectSFID, err)
log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewDeleteProjectGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ return gitlab_repositories.NewUnenrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.RepositoryDisabled,
ProjectSFID: params.ProjectSFID,
- CLAGroupID: ghRepo.RepositoryClaGroupID,
LfUsername: authUser.UserName,
EventData: &events.RepositoryDisabledEventData{
- RepositoryName: ghRepo.RepositoryName,
+ RepositoryName: ghRepo.RepositoryName,
+ RepositoryExternalID: ghRepo.RepositoryExternalID,
},
})
- return gitlab_repositories.NewDeleteProjectGitLabRepositoryNoContent().WithXRequestID(reqID)
+ return gitlab_repositories.NewUnenrollGitLabRepositoryNoContent().WithXRequestID(reqID)
})
}
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index c246b9c7d..c5afe0ca5 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -6,6 +6,7 @@ package repositories
import (
"context"
"fmt"
+ "strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
@@ -22,16 +23,18 @@ import (
// RepositoryInterface interface defines the functions for the GitLab repository data model
type RepositoryInterface interface {
- GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error)
- GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByCLAGroupEnabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByCLAGroupDisabled(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) ([]*repoModels.RepositoryDBModel, error)
GitHubGetRepositoriesByOrganizationName(ctx context.Context, orgName string) ([]*repoModels.RepositoryDBModel, error)
+
+ GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error)
+ GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error)
+ GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*repoModels.RepositoryDBModel, error)
GitLabAddRepository(ctx context.Context, projectSFID string, input *repoModels.RepositoryDBModel) (*repoModels.RepositoryDBModel, error)
- GitLabEnableRepositoryByID(ctx context.Context, repositoryID string) error
- GitLabDisableRepositoryByID(ctx context.Context, repositoryID string) error
+ GitLabEnableRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64) error
+ GitLabDisableRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64) error
GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
}
@@ -118,6 +121,32 @@ func (r *Repository) GitLabGetRepositoryByName(ctx context.Context, repositoryNa
return record, nil
}
+// GitLabGetRepositoryByExternalID returns the database model for the specified repository by external ID
+func (r *Repository) GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*repoModels.RepositoryDBModel, error) {
+ str := strconv.FormatInt(repositoryExternalID, 10)
+ condition := expression.Key(repoModels.RepositoryExternalIDColumn).Equal(expression.Value(str))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
+ record, err := r.getRepositoryWithConditionFilter(ctx, condition, filter, repoModels.RepositoryExternalIDIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ RepositoryExternalID: repositoryExternalID,
+ }
+ }
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabDuplicateRepositoriesFound); ok {
+ return nil, &utils.GitLabDuplicateRepositoriesFound{
+ RepositoryExternalID: repositoryExternalID,
+ }
+ }
+ // Some other error
+ return nil, err
+ }
+
+ return record, nil
+}
+
// GitHubGetRepositoriesByCLAGroup returns the database models for the specified CLA Group ID
func (r *Repository) GitHubGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string) ([]*repoModels.RepositoryDBModel, error) {
condition := expression.Key(repoModels.RepositoryCLAGroupIDColumn).Equal(expression.Value(claGroupID))
@@ -284,13 +313,13 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
}
// GitLabEnableRepositoryByID enables the specified repository
-func (r *Repository) GitLabEnableRepositoryByID(ctx context.Context, repositoryID string) error {
- return r.setRepositoryEnabledValue(ctx, repositoryID, true)
+func (r *Repository) GitLabEnableRepositoryByID(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
+ return r.setRepositoryEnabledValue(ctx, claGroupID, repositoryExternalID, true)
}
// GitLabDisableRepositoryByID disables the specified repository
-func (r *Repository) GitLabDisableRepositoryByID(ctx context.Context, repositoryID string) error {
- return r.setRepositoryEnabledValue(ctx, repositoryID, false)
+func (r *Repository) GitLabDisableRepositoryByID(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
+ return r.setRepositoryEnabledValue(ctx, claGroupID, repositoryExternalID, false)
}
// GitLabEnableCLAGroupRepositories enables the specified CLA Group repositories
@@ -301,7 +330,12 @@ func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGr
}
for _, repo := range repositories {
- enableErr := r.GitLabEnableRepositoryByID(ctx, repo.RepositoryID)
+ int64I, parseErr := strconv.ParseInt(repo.RepositoryExternalID, 10, 64)
+ if parseErr != nil {
+ return parseErr
+ }
+
+ enableErr := r.GitLabEnableRepositoryByID(ctx, claGroupID, int64I)
if enableErr != nil {
return enableErr
}
@@ -318,7 +352,12 @@ func (r *Repository) GitLabDisableCLAGroupRepositories(ctx context.Context, claG
}
for _, repo := range repositories {
- enableErr := r.GitLabDisableRepositoryByID(ctx, repo.RepositoryID)
+ int64I, parseErr := strconv.ParseInt(repo.RepositoryExternalID, 10, 64)
+ if parseErr != nil {
+ return parseErr
+ }
+
+ enableErr := r.GitLabDisableRepositoryByID(ctx, claGroupID, int64I)
if enableErr != nil {
return enableErr
}
@@ -427,14 +466,22 @@ func (r *Repository) getRepositoriesWithConditionFilter(ctx context.Context, con
}
// setRepositoryEnabledValue sets the specified repository to the specified enabled value
-func (r *Repository) setRepositoryEnabledValue(ctx context.Context, repositoryID string, enabledValue bool) error {
+func (r *Repository) setRepositoryEnabledValue(ctx context.Context, claGroupID string, repositoryExternalID int64, enabledValue bool) error {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repository.setRepositoryEnabledValue",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "repositoryExternalID": repositoryExternalID,
+ "enabledValue": enabledValue,
+ }
+
// Load the existing model - need to fetch the old values, if available
- existingModel, getErr := r.GitLabGetRepository(ctx, repositoryID)
+ existingModel, getErr := r.GitLabGetRepositoryByExternalID(ctx, repositoryExternalID)
if getErr != nil {
return getErr
}
if existingModel == nil {
- return fmt.Errorf("unable to locate existing repository entry by ID: %s", repositoryID)
+ return fmt.Errorf("unable to locate existing repository entry by external ID: %d", repositoryExternalID)
}
// If we have an old note - grab it/save it
@@ -448,10 +495,7 @@ func (r *Repository) setRepositoryEnabledValue(ctx context.Context, repositoryID
}
_, now := utils.CurrentTime()
- _, err := r.dynamoDBClient.UpdateItem(&dynamodb.UpdateItemInput{
- Key: map[string]*dynamodb.AttributeValue{
- "repository_id": {S: aws.String(repositoryID)},
- },
+ updateInput := &dynamodb.UpdateItemInput{
ExpressionAttributeNames: map[string]*string{
"#enabled": aws.String(repoModels.RepositoryEnabledColumn),
"#note": aws.String(repoModels.RepositoryNoteColumn),
@@ -468,9 +512,24 @@ func (r *Repository) setRepositoryEnabledValue(ctx context.Context, repositoryID
S: aws.String(now),
},
},
- UpdateExpression: aws.String("SET #enabled = :enabledValue, #note = :noteValue, #dateModified = :dateModifiedValue"),
+ Key: map[string]*dynamodb.AttributeValue{
+ repoModels.RepositoryIDColumn: {S: aws.String(existingModel.RepositoryID)},
+ },
TableName: aws.String(r.repositoryTableName),
- })
+ UpdateExpression: aws.String("SET #enabled = :enabledValue, #note = :noteValue, #dateModified = :dateModifiedValue"),
+ }
+
+ if claGroupID != "" {
+ updateInput.ExpressionAttributeNames["#claGroupID"] = aws.String(repoModels.RepositoryCLAGroupIDColumn)
+ updateInput.ExpressionAttributeValues[":claGroupIDValue"] = &dynamodb.AttributeValue{S: aws.String(claGroupID)}
+ updateExpression := fmt.Sprintf("%s, #claGroupID = :claGroupIDValue ", *updateInput.UpdateExpression)
+ updateInput.UpdateExpression = aws.String(updateExpression)
+ }
+
+ _, err := r.dynamoDBClient.UpdateItem(updateInput)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem with update, error: %+v", err.Error())
+ }
return err
}
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 877ee9829..117ee5277 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -55,13 +55,15 @@ type ServiceInterface interface {
GitLabGetRepository(ctx context.Context, repositoryID string) (*v2Models.GitlabRepository, error)
GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*v2Models.GitlabRepository, error)
+ GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*v2Models.GitlabRepository, error)
GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error)
- GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesAdd) (*v2Models.GitlabRepositoriesList, error)
+ GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesEnable) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
- GitLabEnableRepository(ctx context.Context, repositoryID string) error
- GitLabDisableRepository(ctx context.Context, repositoryID string) error
+ GitLabEnableRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64) error
+ GitLabEnableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error
+ GitLabDisableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error
GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
}
From a11e60cf0e012bac795aa0b2d56c025d40e7c2e5 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Mon, 23 Aug 2021 18:23:44 +0300
Subject: [PATCH 0450/1276] fix gitlab sign url to v4 (#3187)
---
cla-backend-go/config/config.go | 1 +
cla-backend-go/config/ssm.go | 3 +++
cla-backend-go/v2/gitlab-activity/service.go | 4 ++--
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 42e55caec..2852db09b 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -68,6 +68,7 @@ type Config struct {
CorporateConsoleV2URL string `json:"corporateConsoleV2URL"`
CLAContributorv2Base string `json:"cla-contributor-v2-base"`
+ ClaAPIV4Base string `json:"cla_api_v4_base"`
// SNSEventTopic the topic ARN for events
SNSEventTopicARN string `json:"snsEventTopicARN"`
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index c28129a13..2f1706cb5 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -103,6 +103,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-enable-services-for-parent-%s", stage),
fmt.Sprintf("cla-signature-query-default-%s", stage),
fmt.Sprintf("cla-platform-api-gw-%s", stage),
+ fmt.Sprintf("cla-api-v4-base-%s", stage),
}
// For each key to lookup
@@ -175,6 +176,8 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.Gitlab.WebHookURI = resp.value
case fmt.Sprintf("cla-contributor-v2-base-%s", stage):
config.CLAContributorv2Base = resp.value
+ case fmt.Sprintf("cla-api-v4-base-%s", stage):
+ config.ClaAPIV4Base = resp.value
case fmt.Sprintf("cla-corporate-base-%s", stage):
config.CorporateConsoleURL = resp.value
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 6ca2a9f43..d61dae58e 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -235,8 +235,8 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
}
func GetFullSignURL(gitlabOrganizationID string, gitlabRepositoryID string, mrID string) string {
- return fmt.Sprintf("%s/v2/repository-provider/%s/sign/%s/%s/%s/#/",
- config.GetConfig().APIGatewayURL,
+ return fmt.Sprintf("%s/v4/repository-provider/%s/sign/%s/%s/%s/#/",
+ config.GetConfig().ClaAPIV4Base,
utils.GitLabLower,
gitlabOrganizationID,
gitlabRepositoryID,
From 78a617f549ed23c3e45f659415a3eaa471506401 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 23 Aug 2021 10:30:17 -0700
Subject: [PATCH 0451/1276] Updated GitHub/GitLab Permissions Checks (#3188)
---
cla-backend-go/swagger/cla.v2.yaml | 2 +
.../v2/repositories/gitlab_services.go | 4 +-
cla-backend-go/v2/repositories/handlers.go | 83 +++++++++++++++----
3 files changed, 72 insertions(+), 17 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 24bd81f6d..99f08c281 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1442,6 +1442,8 @@ paths:
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
+ '404':
+ $ref: '#/responses/not-found'
'409':
$ref: '#/responses/conflict'
'500':
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 77bc099b0..303ececdf 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -97,8 +97,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositorySfdcID: projectSFID,
ProjectSFID: projectSFID,
RepositoryExternalID: repositoryExternalIDString,
- RepositoryName: project.Name,
- RepositoryFullPath: project.PathWithNamespace,
+ RepositoryName: project.PathWithNamespace, // Name column is actually the full path for both GitHub and GitLab
RepositoryURL: project.WebURL,
RepositoryOrganizationName: input.GitlabOrganizationName,
RepositoryCLAGroupID: input.ClaGroupID,
@@ -381,7 +380,6 @@ func dbModelToGitLabRepository(dbModel *repoModels.RepositoryDBModel) (*v2Models
RepositoryClaGroupID: dbModel.RepositoryCLAGroupID, // CLA Group ID
RepositoryExternalID: gitLabExternalID, // GitLab unique gitV1Repository ID
RepositoryName: dbModel.RepositoryName, // Short repository name
- RepositoryFullPath: dbModel.RepositoryFullPath, // Full repository path
RepositoryOrganizationName: dbModel.RepositoryOrganizationName, // Group/Organization name
RepositoryURL: dbModel.RepositoryURL, // full url
RepositoryType: dbModel.RepositoryType, // gitlab
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index afa3223d9..9c66a9e2f 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -8,7 +8,6 @@ import (
"fmt"
"strings"
- "github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_organizations"
project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_repositories"
@@ -47,9 +46,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"projectSFID": params.ProjectSFID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return github_repositories.NewGetProjectGithubRepositoriesNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Get GitHub V3Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Get GitHub repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewGetProjectGithubRepositoriesForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -99,9 +106,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"repositoryGitHubIDs": strings.Join(params.GithubRepositoryInput.RepositoryGithubIds, ","),
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return github_repositories.NewAddProjectGithubRepositoryNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Add GitHub V3Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to add GitHub repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewAddProjectGithubRepositoryForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -178,9 +193,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"repositoryID": params.RepositoryID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return github_repositories.NewDeleteProjectGithubRepositoryNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Delete GitHub V3Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Get GitHub repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewDeleteProjectGithubRepositoryForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -236,9 +259,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"repositoryID": params.RepositoryID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return github_repositories.NewGetProjectGithubRepositoryBranchProtectionNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Query Protected Branch GitHub V3Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Query Protected Branch GitHub Repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewGetProjectGithubRepositoryBranchProtectionForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -297,9 +328,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"repositoryID": params.RepositoryID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return github_repositories.NewUpdateProjectGithubRepositoryBranchProtectionNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Update Protected Branch GitHub V3Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Update Protected Branch GitHub Repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return github_repositories.NewUpdateProjectGithubRepositoryBranchProtectionForbidden().WithPayload(
utils.ErrorResponseForbidden(reqID, msg))
@@ -378,9 +417,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"projectSFID": params.ProjectSFID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_repositories.NewGetProjectGitLabRepositoriesNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Get GitLab Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
+ msg := fmt.Sprintf("user %s does not have access to Get GitLab Repositories for Project %s with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
return gitlab_repositories.NewGetProjectGitLabRepositoriesForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
@@ -430,7 +477,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
psc := project_service.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
- return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ return gitlab_repositories.NewEnableGitLabRepositoryNotFound().WithPayload(
utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
@@ -478,6 +525,14 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"repositoryExternalID": params.RepositoryExternalID,
}
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_repositories.NewUnenrollGitLabRepositoryNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Unenroll Gitlab Repositories with Project scope of %s",
authUser.UserName, params.ProjectSFID)
From 2a145c1a11dd746b182be4dcf2a649abac265332 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 23 Aug 2021 15:16:30 -0700
Subject: [PATCH 0452/1276] Updated GitLab Organizations Swagger (#3189)
---
.../common/gitlab-organization-create.yaml | 4 +-
.../common/gitlab-organization-update.yaml | 8 ++--
.../v2/gitlab_organizations/handlers.go | 20 ++++----
.../v2/gitlab_organizations/service.go | 4 +-
cla-backend-go/v2/signatures/handlers.go | 46 +++++++------------
cla-backend-go/v2/signatures/service.go | 5 +-
6 files changed, 38 insertions(+), 49 deletions(-)
diff --git a/cla-backend-go/swagger/common/gitlab-organization-create.yaml b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
index 983fe0268..1bb91ac86 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-create.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
@@ -17,9 +17,9 @@ properties:
description: The GitLab Group ID
example: 13050017
minimum: 1
- group_full_path:
+ organization_full_path:
type: string
- description: The GitLab Group full path
+ description: The GitLab Group/Organization full path
example: 'linuxfoundation/product/easycla'
minLength: 3
auto_enabled:
diff --git a/cla-backend-go/swagger/common/gitlab-organization-update.yaml b/cla-backend-go/swagger/common/gitlab-organization-update.yaml
index e9875eca5..dec67909d 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-update.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-update.yaml
@@ -3,15 +3,15 @@
type: object
required:
- - autoEnabled
+ - auto_enabled
properties:
- autoEnabled:
+ auto_enabled:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Group/Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
- autoEnabledClaGroupID:
+ auto_enabled_cla_group_id:
type: string
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the GitLab Group/Organization. If autoEnabled is on this field needs to be set as well.
- branchProtectionEnabled:
+ branch_protection_enabled:
type: boolean
description: Flag to indicate if this Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
x-omitempty: true
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 846f51fe1..9fd0214d7 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -97,7 +97,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
"authEmail": authUser.Email,
"projectSFID": params.ProjectSFID,
"groupID": params.Body.GroupID,
- "groupFullPath": params.Body.GroupFullPath,
+ "groupFullPath": params.Body.OrganizationFullPath,
}
// Load the project
@@ -117,7 +117,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Quick check of the parameters
- if params.Body == nil || (params.Body.GroupID == 0 && params.Body.GroupFullPath == "") {
+ if params.Body == nil || (params.Body.GroupID == 0 && params.Body.OrganizationFullPath == "") {
msg := fmt.Sprintf("missing group ID or group full path in the body: %+v", params.Body)
log.WithFields(f).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
@@ -125,16 +125,16 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Clean up/filter the Group Full Path, if needed
- if params.Body.GroupFullPath != "" {
+ if params.Body.OrganizationFullPath != "" {
r, regexErr := regexp.Compile(`^http(s)?://`)
if regexErr != nil {
- msg := fmt.Sprintf("invalid regex for group full path, error: %+v", regexErr)
+ msg := fmt.Sprintf("invalid regex for group/organization full path, error: %+v", regexErr)
log.WithFields(f).WithError(regexErr).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationInternalServerError().WithPayload(
utils.ErrorResponseInternalServerErrorWithError(reqID, msg, regexErr))
}
- if r.MatchString(params.Body.GroupFullPath) {
- groupWithUrl, urlParseErr := url.Parse(params.Body.GroupFullPath)
+ if r.MatchString(params.Body.OrganizationFullPath) {
+ groupWithUrl, urlParseErr := url.Parse(params.Body.OrganizationFullPath)
if urlParseErr != nil {
msg := fmt.Sprintf("invalid group full path provided, error: %+v", urlParseErr)
log.WithFields(f).WithError(urlParseErr).Warn(msg)
@@ -142,12 +142,12 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequestWithError(reqID, msg, urlParseErr))
}
// Update the group full path value - just include the path and not the https://... part
- params.Body.GroupFullPath = groupWithUrl.Path
+ params.Body.OrganizationFullPath = groupWithUrl.Path
}
// Remove leading slash
- if strings.HasPrefix(params.Body.GroupFullPath, "/") {
- params.Body.GroupFullPath = params.Body.GroupFullPath[1:]
+ if strings.HasPrefix(params.Body.OrganizationFullPath, "/") {
+ params.Body.OrganizationFullPath = params.Body.OrganizationFullPath[1:]
}
}
@@ -178,7 +178,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
// Get the current group name for the event
for _, group := range result.List {
- if group.OrganizationExternalID == params.Body.GroupID || group.OrganizationFullPath == params.Body.GroupFullPath {
+ if group.OrganizationExternalID == params.Body.GroupID || group.OrganizationFullPath == params.Body.OrganizationFullPath {
// Log the event
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
LfUsername: authUser.UserName,
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index b23e824f2..fc48002f7 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -71,7 +71,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
"autoEnabled": utils.BoolValue(input.AutoEnabled),
"branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
"groupID": input.GroupID,
- "groupFullPath": input.GroupFullPath,
+ "groupFullPath": input.OrganizationFullPath,
}
psc := v2ProjectService.GetClient()
@@ -101,7 +101,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
branchProtectionEnabled = utils.BoolValue(input.BranchProtectionEnabled)
}
- resp, err := s.repo.AddGitLabOrganization(ctx, parentProjectSFID, projectSFID, input.GroupID, "", input.GroupFullPath, autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
+ resp, err := s.repo.AddGitLabOrganization(ctx, parentProjectSFID, projectSFID, input.GroupID, "", input.OrganizationFullPath, autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem adding gitlab organization for project")
return nil, err
diff --git a/cla-backend-go/v2/signatures/handlers.go b/cla-backend-go/v2/signatures/handlers.go
index 0abcc1592..7ae5c2788 100644
--- a/cla-backend-go/v2/signatures/handlers.go
+++ b/cla-backend-go/v2/signatures/handlers.go
@@ -889,6 +889,19 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
"companyID": params.CompanyID,
}
+ // Lookup the CLA Group by ID - make sure it's valid
+ claGroupModel, err := projectRepo.GetCLAGroupByID(ctx, params.ClaGroupID, project.DontLoadRepoDetails)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
+ if err == project.ErrProjectDoesNotExist {
+ return signatures.NewListClaGroupCorporateContributorsNotFound().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseNotFoundWithError(reqID, problemLoadingCLAGroupByID, err))
+ }
+
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(
+ utils.ErrorResponseBadRequest(reqID, problemLoadingCLAGroupByID))
+ }
+
// Make sure the user has provided the companyID
if params.CompanyID == nil {
msg := "missing companyID as input"
@@ -901,44 +914,19 @@ func Configure(api *operations.EasyclaAPI, claGroupService project.Service, proj
if err != nil {
msg := fmt.Sprintf("User lookup for company by ID: %s failed : %v", *params.CompanyID, err)
log.Warn(msg)
- if _, ok := err.(*utils.CompanyNotFound); ok {
- return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 404 Not Found - error getting company - " + msg,
- Code: "404",
- })
- }
- return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(&models.ErrorResponse{
- Message: "EasyCLA - 400 Bad Request - error getting company - " + msg,
- Code: "400",
- })
- }
-
- // Lookup the CLA Group by ID - make sure it's valid
- claGroupModel, err := projectRepo.GetCLAGroupByID(ctx, params.ClaGroupID, project.DontLoadRepoDetails)
- if err != nil {
- log.WithFields(f).WithError(err).Warn(problemLoadingCLAGroupByID)
- if err == project.ErrProjectDoesNotExist {
- return signatures.NewListClaGroupCorporateContributorsNotFound().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseNotFoundWithError(reqID, problemLoadingCLAGroupByID, err))
- }
-
- return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(
- utils.ErrorResponseBadRequest(reqID, problemLoadingCLAGroupByID))
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
// Make sure CCLA is enabled for this CLA Group
if !claGroupModel.ProjectCCLAEnabled {
- msg := "cla group does not support corporate contribution"
+ msg := fmt.Sprintf("CLA Group with ID '%s' does not support corporate contribution", params.ClaGroupID)
log.WithFields(f).Warn(msg)
- // Return 200 as the retool UI can't handle 400's
- return signatures.NewListClaGroupCorporateContributorsOK().WithXRequestID(reqID).WithPayload(&models.CorporateContributorList{
- List: []*models.CorporateContributor{}, // empty list
- })
+ return signatures.NewListClaGroupCorporateContributorsBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, errors.New(msg)))
}
// Lookup the Project to CLA Group mapping table entries - this will have the correct details
projectCLAGroupEntries, projectCLAGroupErr := projectClaGroupsRepo.GetProjectsIdsForClaGroup(ctx, params.ClaGroupID)
- // Should have at least one entry if we're setup correctly - it will have the foundation (parent project/project group) and project details set
+ // Should have at least one entry if we're set up correctly - it will have the foundation (parent project/project group) and project details set
if projectCLAGroupErr != nil || len(projectCLAGroupEntries) == 0 {
msg := fmt.Sprintf("unable to load project CLA Group mappings for CLA Group: %s - has this project been migrated to v2?", params.ClaGroupID)
log.WithFields(f).Warn(msg)
diff --git a/cla-backend-go/v2/signatures/service.go b/cla-backend-go/v2/signatures/service.go
index 45aa39516..947a1811d 100644
--- a/cla-backend-go/v2/signatures/service.go
+++ b/cla-backend-go/v2/signatures/service.go
@@ -157,7 +157,7 @@ func (s *Service) GetProjectIclaSignaturesCsv(ctx context.Context, claGroupID st
// GetProjectCclaSignaturesCsv returns the ICLA signatures as a CSV file for the specified CLA Group and search term filters
func (s *Service) GetProjectCclaSignaturesCsv(ctx context.Context, claGroupID string) ([]byte, error) {
f := logrus.Fields{
- "functionName": "GetProjectCclaSignaturesCsv",
+ "functionName": "v2.signatures.service.GetProjectCclaSignaturesCsv",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"claGroupID": claGroupID,
}
@@ -289,7 +289,7 @@ func (s *Service) IsZipPresentOnS3(zipFilePath string) (bool, error) {
// GetClaGroupCorporateContributors returns the list of corporate contributors for the specified CLA Group and company
func (s *Service) GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID string, searchTerm *string) (*models.CorporateContributorList, error) {
f := logrus.Fields{
- "functionName": "GetClaGroupCorporateContributors",
+ "functionName": "v2.signatures.service.GetClaGroupCorporateContributors",
"claGroupID": claGroupID,
"companyID": companyID,
}
@@ -302,6 +302,7 @@ func (s *Service) GetClaGroupCorporateContributors(ctx context.Context, claGroup
if err != nil {
return nil, err
}
+ log.WithFields(f).Debugf("discovered %d CLA corporate contributors...", len(result.List))
log.WithFields(f).Debug("converting to v2 response model...")
var resp models.CorporateContributorList
From fa20eda8c14af7ab03874b0aee279ba4f9f4363d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Mon, 23 Aug 2021 17:05:54 -0700
Subject: [PATCH 0453/1276] Added GitLab Permissions Error Details/Response
(#3190)
---
cla-backend-go/gitlab_api/client_groups.go | 9 +++---
cla-backend-go/tests/gitlab_client_test.go | 3 +-
.../v2/gitlab_organizations/service.go | 28 +++++++++++++++++--
3 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
index ba7a30b60..38720fcb0 100644
--- a/cla-backend-go/gitlab_api/client_groups.go
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -23,7 +23,7 @@ type UserGroup struct {
}
// GetGroupsListAll returns a complete list of GitLab groups for which the client as authorization/visibility
-func GetGroupsListAll(ctx context.Context, client *goGitLab.Client) ([]*goGitLab.Group, error) {
+func GetGroupsListAll(ctx context.Context, client *goGitLab.Client, minAccessLevel goGitLab.AccessLevelValue) ([]*goGitLab.Group, error) {
f := logrus.Fields{
"functionName": "gitlab_api.client_groups.GetGroupsListAll",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -36,8 +36,9 @@ func GetGroupsListAll(ctx context.Context, client *goGitLab.Client) ([]*goGitLab
Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
PerPage: 100, // max is 100
},
- AllAvailable: utils.Bool(true), // Show all the groups you have access to (defaults to false for authenticated users, true for administrators); Attributes owned and min_access_level have precedence
- MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
+ AllAvailable: utils.Bool(true), // Show all the groups you have access to (defaults to false for authenticated users, true for administrators); Attributes owned and min_access_level have precedence
+ MinAccessLevel: goGitLab.AccessLevel(minAccessLevel), // Limit by current user minimal access level.
+ //MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
}
var groupList []*goGitLab.Group
@@ -128,7 +129,7 @@ func GetGroupByFullPath(ctx context.Context, client *goGitLab.Client, fullPath s
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
- groups, err := GetGroupsListAll(ctx, client)
+ groups, err := GetGroupsListAll(ctx, client, goGitLab.MaintainerPermissions)
//groups, _, err := client.Groups.ListGroups(&goGitLab.ListGroupsOptions{})
if err != nil {
msg := fmt.Sprintf("problem fetching groups, error: %+v", err)
diff --git a/cla-backend-go/tests/gitlab_client_test.go b/cla-backend-go/tests/gitlab_client_test.go
index 46a4924c2..487d77931 100644
--- a/cla-backend-go/tests/gitlab_client_test.go
+++ b/cla-backend-go/tests/gitlab_client_test.go
@@ -221,6 +221,7 @@ func TestGitLabListGroups(t *testing.T) { // no lint
PerPage: 100,
},
}
+
groups, resp, searchErr := gitLabClient.Groups.ListGroups(opts)
assert.Nil(t, searchErr, "GitLab List Groups Error is Nil")
if searchErr != nil {
@@ -232,7 +233,7 @@ func TestGitLabListGroups(t *testing.T) { // no lint
assert.Fail(t, fmt.Sprintf("unable to list GitLab groups, status code: %d, body: %s", resp.StatusCode, respBody))
}
for _, g := range groups {
- t.Logf("name: %s, id: %d, web url: %s, path: %s, full path: %s", g.Name, g.ID, g.WebURL, g.Path, g.FullPath)
+ t.Logf("name: %s, id: %d, path: %s, full path: %s, web url: %s", g.Name, g.ID, g.Path, g.FullPath, g.WebURL)
}
}
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index fc48002f7..aafa3506b 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -335,12 +335,12 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
// Query the groups list
- groups, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient)
+ groupsWithMaintainerPerms, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient, goGitLab.MaintainerPermissions)
if groupListErr != nil {
return groupListErr
}
- for _, g := range groups {
+ for _, g := range groupsWithMaintainerPerms {
// If we have an external group ID or a full path...
if (gitLabOrgModel.ExternalGroupID > 0 && g.ID == gitLabOrgModel.ExternalGroupID) ||
(gitLabOrgModel.OrganizationFullPath != "" && g.FullPath == gitLabOrgModel.OrganizationFullPath) {
@@ -366,7 +366,29 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
}
- return fmt.Errorf("unable to locate GitLab group by using external ID: %d or full path: %s, found: %d", gitLabOrgModel.ExternalGroupID, gitLabOrgModel.OrganizationFullPath, len(groups))
+ // Query the groups list to see if the user has minimal access permissions only
+ groupsWithMinimalAccessPerms, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient, goGitLab.MinimalAccessPermissions)
+ if groupListErr != nil {
+ return groupListErr
+ }
+
+ // Loop through the responses - check to see if we have a match, if so, return a specific error message
+ for _, g := range groupsWithMinimalAccessPerms {
+ // If we have an external group ID or a full path...
+ if (gitLabOrgModel.ExternalGroupID > 0 && g.ID == gitLabOrgModel.ExternalGroupID) ||
+ (gitLabOrgModel.OrganizationFullPath != "" && g.FullPath == gitLabOrgModel.OrganizationFullPath) {
+ msg := ""
+ if gitLabOrgModel.ExternalGroupID > 0 {
+ msg = fmt.Sprintf("external ID: %d", g.ID)
+ } else if gitLabOrgModel.OrganizationFullPath != "" {
+ msg = fmt.Sprintf("full path: '%s'", g.FullPath)
+ }
+ return fmt.Errorf("found the GitLab group '%s' by using the %s filter - however the authenticated user does not have maintainer or above permissions for this GitLab group", g.FullPath, msg)
+ }
+ }
+
+ return fmt.Errorf("unable to locate GitLab group by using external ID: %d or full path: %s, found %d groups where user has maintainer or above permissions",
+ gitLabOrgModel.ExternalGroupID, gitLabOrgModel.OrganizationFullPath, len(groupsWithMaintainerPerms))
}
// UpdateGitLabOrganization updates the GitLab organization
From 296bed28da9c3fe4f73ac5c34506073bbba25c32 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Mon, 23 Aug 2021 19:40:14 -0700
Subject: [PATCH 0454/1276] Bug/Gitlab Redirect contributor console (#3191)
- Updated user_gitlab_attribute call with integer in the repository call
- Updated signature meta data set(v4) and get (v2)
- Updated Store value type in stores table
Signed-off-by: Harold Wanyama
Co-authored-by: Harold Wanyama
---
cla-backend-go/cmd/server.go | 4 +-
cla-backend-go/users/repository.go | 2 +-
cla-backend-go/v2/gitlab_sign/handlers.go | 9 ++-
cla-backend-go/v2/gitlab_sign/service.go | 80 +++++++++++++++--------
cla-backend-go/v2/store/repository.go | 15 +++--
cla-backend/cla/models/dynamo_models.py | 6 +-
cla-backend/cla/utils.py | 4 ++
7 files changed, 83 insertions(+), 37 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index 2d38fed75..e1f88712e 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -239,7 +239,7 @@ func server(localMode bool) http.Handler {
// initialize github
github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
// initialize gitlab
- _ = gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
+ gitlabApp := gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
// Our backend repository handlers
userRepo := user.NewDynamoRepository(awsSession, stage)
@@ -309,7 +309,7 @@ func server(localMode bool) http.Handler {
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, gitV1Repository, v1ProjectClaGroupRepo)
gitlabOrganizationsService := gitlab_organizations.NewService(gitlabOrganizationRepo, v2RepositoriesService, v1ProjectClaGroupRepo)
gitlabActivityService := gitlab_activity.NewService(gitlabOrganizationRepo, gitV1Repository, gitV2Repository, usersRepo, signaturesRepo, v1ProjectClaGroupRepo, v1CompanyRepo, signaturesRepo)
- gitlabSignService := gitlab_sign.NewService(v2RepositoriesService, gitlabOrganizationRepo, usersService, storeRepository)
+ gitlabSignService := gitlab_sign.NewService(v2RepositoriesService, gitlabOrganizationRepo, usersService, storeRepository, gitlabApp)
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, gitV1Repository, v1ProjectClaGroupRepo, githubOrganizationsService)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, gitV1Repository, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(gitV1Repository, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 350e98781..484878d02 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -107,7 +107,7 @@ func (repo repository) CreateUser(user *models.User) (*models.User, error) {
if user.GitlabID != "" {
attributes["user_gitlab_id"] = &dynamodb.AttributeValue{
- S: aws.String(user.GitlabID),
+ N: aws.String(user.GitlabID),
}
}
diff --git a/cla-backend-go/v2/gitlab_sign/handlers.go b/cla-backend-go/v2/gitlab_sign/handlers.go
index acd2ebeed..317fc8c58 100644
--- a/cla-backend-go/v2/gitlab_sign/handlers.go
+++ b/cla-backend-go/v2/gitlab_sign/handlers.go
@@ -6,11 +6,13 @@ package gitlab_sign
import (
"context"
"fmt"
+ "net/http"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_sign"
"github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/sirupsen/logrus"
@@ -33,7 +35,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
log.WithFields(f).Debugf("Initiating Gitlab sign request for : %+v ", srp)
- err := service.GitlabSignRequest(ctx, srp.HTTPRequest, srp.OrganizationID, srp.GitlabRepositoryID, srp.MergeRequestID, contributorConsoleV2Base, eventService)
+ consoleURL, err := service.GitlabSignRequest(ctx, srp.HTTPRequest, srp.OrganizationID, srp.GitlabRepositoryID, srp.MergeRequestID, contributorConsoleV2Base, eventService)
if err != nil {
msg := fmt.Sprintf("problem initiating sign request for :%+v", srp)
@@ -42,6 +44,9 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- return gitlab_sign.NewSignRequestOK()
+ return middleware.ResponderFunc(func(rw http.ResponseWriter, pr runtime.Producer) {
+ http.Redirect(rw, srp.HTTPRequest, *consoleURL, http.StatusSeeOther)
+ })
+
})
}
diff --git a/cla-backend-go/v2/gitlab_sign/service.go b/cla-backend-go/v2/gitlab_sign/service.go
index ee4083736..61d79e34b 100644
--- a/cla-backend-go/v2/gitlab_sign/service.go
+++ b/cla-backend-go/v2/gitlab_sign/service.go
@@ -6,10 +6,14 @@ package gitlab_sign
import (
"context"
"encoding/json"
+
+ // "encoding/json"
+ "errors"
"fmt"
"net/http"
"net/url"
"strconv"
+
"time"
"github.com/sirupsen/logrus"
@@ -29,24 +33,25 @@ type service struct {
repoService repositories.ServiceInterface
gitlabOrgRepo gitlab_organizations.RepositoryInterface
userService users.Service
- gitlabApp gitlab_api.App
+ gitlabApp *gitlab_api.App
storeRepo store.Repository
}
type Service interface {
- GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) error
+ GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) (*string, error)
}
-func NewService(gitlabRepositoryService repositories.ServiceInterface, gitlabOrgRepository gitlab_organizations.RepositoryInterface, userService users.Service, storeRepo store.Repository) Service {
+func NewService(gitlabRepositoryService repositories.ServiceInterface, gitlabOrgRepository gitlab_organizations.RepositoryInterface, userService users.Service, storeRepo store.Repository, gitlabApp *gitlab_api.App) Service {
return &service{
repoService: gitlabRepositoryService,
gitlabOrgRepo: gitlabOrgRepository,
userService: userService,
+ gitlabApp: gitlabApp,
storeRepo: storeRepo,
}
}
-func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) error {
+func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organizationID, repositoryID, mergeRequestID, contributorConsoleV2Base string, eventService events.Service) (*string, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_sign.service.GitlabSignRequest",
"organizationID": organizationID,
@@ -57,46 +62,46 @@ func (s service) GitlabSignRequest(ctx context.Context, req *http.Request, organ
organization, err := s.gitlabOrgRepo.GetGitLabOrganization(ctx, organizationID)
if err != nil {
log.WithFields(f).Debugf("unable to get gitlab organiztion by ID: %s, error: %+v ", organizationID, err)
- return nil
+ return nil, err
}
if organization.AuthInfo == "" {
msg := fmt.Sprintf("organization: %s has no auth details", organizationID)
log.WithFields(f).Debug(msg)
- return nil
+ return nil, errors.New(msg)
}
- gitlabClient, err := gitlab_api.NewGitlabOauthClient(organization.AuthInfo, &s.gitlabApp)
+ gitlabClient, err := gitlab_api.NewGitlabOauthClient(organization.AuthInfo, s.gitlabApp)
if err != nil {
log.WithFields(f).Debugf("initializaing gitlab client for gitlab org: %s failed: %v", organizationID, err)
- return nil
+ return nil, err
}
mergeRequestIDInt, err := strconv.Atoi(mergeRequestID)
if err != nil {
log.WithFields(f).Debugf("unable to convert organization string value : %s to Int", organizationID)
- return err
+ return nil, err
}
log.WithFields(f).Debug("Determining return URL from the inbound request ...")
mergeRequest, _, err := gitlabClient.MergeRequests.GetMergeRequest(repositoryID, mergeRequestIDInt, &gitlab.GetMergeRequestsOptions{})
if err != nil || mergeRequest == nil {
log.WithFields(f).Debugf("unable to fetch MR Web URL: mergeRequestID: %s ", mergeRequestID)
- return err
+ return nil, err
}
originURL := mergeRequest.WebURL
log.WithFields(f).Debugf("Return URL from the inbound request is : %s ", originURL)
- err = s.redirectToConsole(ctx, req, gitlabClient, repositoryID, mergeRequestID, originURL, contributorConsoleV2Base, eventService)
+ consoleURL, err := s.redirectToConsole(ctx, req, gitlabClient, repositoryID, mergeRequestID, originURL, contributorConsoleV2Base, eventService)
if err != nil {
log.WithFields(f).Debug("unable to redirect to contributor console")
- return err
+ return nil, err
}
- return nil
+ return consoleURL, nil
}
-func (s service) redirectToConsole(ctx context.Context, req *http.Request, gitlabClient *gitlab.Client, repositoryID, mergeRequestID, originURL, contributorBaseURL string, eventService events.Service) error {
+func (s service) redirectToConsole(ctx context.Context, req *http.Request, gitlabClient *gitlab.Client, repositoryID, mergeRequestID, originURL, contributorBaseURL string, eventService events.Service) (*string, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_sign.service.redirectToConsole",
"repositoryID": repositoryID,
@@ -108,28 +113,51 @@ func (s service) redirectToConsole(ctx context.Context, req *http.Request, gitla
if err != nil {
msg := fmt.Sprintf("unable to get or create user : %+v ", err)
log.WithFields(f).Warn(msg)
- return err
+ return nil, err
}
- gitlabRepo, err := s.repoService.GitHubGetRepository(ctx, repositoryID)
+ repoIDInt, err := strconv.Atoi(repositoryID)
+ if err != nil {
+ msg := fmt.Sprintf("unable to convert GitlabRepoID: %s to int", repositoryID)
+ log.WithFields(f).Warn(msg)
+ return nil, err
+ }
+
+ log.WithFields(f).Debugf("getting gitlab repository for: %d", repoIDInt)
+ gitlabRepo, err := s.repoService.GitLabGetRepositoryByExternalID(ctx, int64(repoIDInt))
if err != nil {
msg := fmt.Sprintf("unable to find repository by ID: %s , error: %+v ", repositoryID, err)
log.WithFields(f).Warn(msg)
- return err
+ return nil, err
}
+ type StoreValue struct {
+ UserID string `json:"user_id"`
+ ProjectID string `json:"project_id"`
+ RepositoryID string `json:"repository_id"`
+ MergeRequestID string `json:"merge_request_id"`
+ ReturnURL string `json:"return_url"`
+ }
+
+ log.WithFields(f).Debugf("setting active signature metadata: claUser: %+v, repository: %+v", claUser, gitlabRepo)
// set active signature metadata to track the user signing process
key := fmt.Sprintf("active_signature:%s", claUser.UserID)
- var value map[string]string
- value["user_id"] = claUser.UserID
- value["project_id"] = gitlabRepo.RepositoryClaGroupID
- value["repository_id"] = repositoryID
- value["merge_request_id"] = mergeRequestID
+ storeValue := StoreValue{
+ UserID: claUser.UserID,
+ ProjectID: gitlabRepo.RepositoryClaGroupID,
+ RepositoryID: repositoryID,
+ MergeRequestID: mergeRequestID,
+ ReturnURL: originURL,
+ }
+ json_data, err := json.Marshal(storeValue)
+ if err != nil {
+ log.Fatal(err)
+ }
expire := time.Now().AddDate(0, 0, 1).Unix()
- jsonVal, _ := json.Marshal(value)
+ // jsonVal, _ := json.Marshal(value)
- err = s.storeRepo.SetActiveSignatureMetaData(ctx, key, expire, jsonVal)
+ err = s.storeRepo.SetActiveSignatureMetaData(ctx, key, expire, string(json_data))
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to save signature metadata")
}
@@ -141,10 +169,10 @@ func (s service) redirectToConsole(ctx context.Context, req *http.Request, gitla
if err != nil {
msg := fmt.Sprintf("unable to redirect to : %s , error: %+v ", consoleURL, err)
log.WithFields(f).Warn(msg)
- return err
+ return nil, err
}
- return nil
+ return &consoleURL, nil
}
func (s service) getOrCreateUser(ctx context.Context, gitlabClient *gitlab.Client, eventsService events.Service) (*models.User, error) {
diff --git a/cla-backend-go/v2/store/repository.go b/cla-backend-go/v2/store/repository.go
index 64785faad..4d93dbf4b 100644
--- a/cla-backend-go/v2/store/repository.go
+++ b/cla-backend-go/v2/store/repository.go
@@ -19,14 +19,14 @@ import (
//DBStore represents DB Model for the store table
type DBStore struct {
- Key string
- Value []byte
- Expire int64
+ Key string `dynamodbav:"key"`
+ Value string `dynamodbav:"value"`
+ Expire int64 `dynamodbav:"expire"`
}
// Repository interface
type Repository interface {
- SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value []byte) error
+ SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value string) error
}
type repo struct {
@@ -45,7 +45,7 @@ func NewRepository(awsSession *session.Session, stage string) Repository {
}
// SetActiveSignatureMetaData sets active signature meta data
-func (r repo) SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value []byte) error {
+func (r repo) SetActiveSignatureMetaData(ctx context.Context, key string, expire int64, value string) error {
f := logrus.Fields{
"functionName": "v2.store.repository.SetActiveSignatureMetaData",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -60,12 +60,17 @@ func (r repo) SetActiveSignatureMetaData(ctx context.Context, key string, expire
Expire: expire,
}
+ log.WithFields(f).Debugf("key: %s ", store.Key)
+ log.WithFields(f).Debugf("value: %+s ", store.Value)
+
v, err := dynamodbattribute.MarshalMap(store)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem marshalling store record")
return err
}
+ log.WithFields(f).Debugf("Marshalled values: %+v", v)
+
_, err = r.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
Item: v,
TableName: &r.storeTableName,
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 2b83cdaad..73fdc9404 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -3561,9 +3561,13 @@ def set(self, key, value):
model.save()
def get(self, key):
+ import json
model = StoreModel()
try:
- return model.get(key).value
+ val = model.get(key).value
+ if isinstance(val, dict):
+ val = json.dumps(val)
+ return val
except StoreModel.DoesNotExist:
raise cla.models.DoesNotExist("Key not found")
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 6d2a60666..917c9f873 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1201,6 +1201,10 @@ def get_active_signature_return_url(user_id, metadata=None):
if metadata is None:
cla.log.warning('Could not find active signature for user {}, return URL request failed'.format(user_id))
return None
+
+ # Factor in Gitlab flow process
+ if "merge_request_id" in metadata.keys():
+ return metadata['return_url']
# Get Github ID from metadata
github_repository_id = metadata['repository_id']
From fc8ad854d6adff0b1b173238e4eed4b35ebd92f6 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Tue, 24 Aug 2021 19:06:31 +0300
Subject: [PATCH 0455/1276] use full repo path when lookup repos on mr
callbacks (#3192)
---
cla-backend-go/config/config.go | 3 ++
cla-backend-go/config/ssm.go | 3 ++
cla-backend-go/gitlab_api/mr.go | 23 ++--------
cla-backend-go/v2/gitlab-activity/service.go | 43 +++++++++++++++----
.../v2/gitlab-activity/service_test.go | 15 +++++--
5 files changed, 55 insertions(+), 32 deletions(-)
diff --git a/cla-backend-go/config/config.go b/cla-backend-go/config/config.go
index 2852db09b..752ee6188 100644
--- a/cla-backend-go/config/config.go
+++ b/cla-backend-go/config/config.go
@@ -82,6 +82,9 @@ type Config struct {
// CLAV1ApiURL is api url of v1. it is used in v2 sign service
ClaV1ApiURL string `json:"cla_v1_api_url"`
+ // CLALandingPage
+ CLALandingPage string `json:"cla_landing_page"`
+
// AcsAPIKey is api key of the acs
AcsAPIKey string `json:"acs_api_key"`
diff --git a/cla-backend-go/config/ssm.go b/cla-backend-go/config/ssm.go
index 2f1706cb5..dde94aa34 100644
--- a/cla-backend-go/config/ssm.go
+++ b/cla-backend-go/config/ssm.go
@@ -104,6 +104,7 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
fmt.Sprintf("cla-signature-query-default-%s", stage),
fmt.Sprintf("cla-platform-api-gw-%s", stage),
fmt.Sprintf("cla-api-v4-base-%s", stage),
+ fmt.Sprintf("cla-landing-page-%s", stage),
}
// For each key to lookup
@@ -178,6 +179,8 @@ func loadSSMConfig(awsSession *session.Session, stage string) Config { //nolint
config.CLAContributorv2Base = resp.value
case fmt.Sprintf("cla-api-v4-base-%s", stage):
config.ClaAPIV4Base = resp.value
+ case fmt.Sprintf("cla-landing-page-%s", stage):
+ config.CLALandingPage = resp.value
case fmt.Sprintf("cla-corporate-base-%s", stage):
config.CorporateConsoleURL = resp.value
diff --git a/cla-backend-go/gitlab_api/mr.go b/cla-backend-go/gitlab_api/mr.go
index 7a454b893..33145c1ef 100644
--- a/cla-backend-go/gitlab_api/mr.go
+++ b/cla-backend-go/gitlab_api/mr.go
@@ -92,22 +92,7 @@ func SetCommitStatus(client *gitlab.Client, projectID int, commitSha string, sta
}
// 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, targetURL string) error {
- covered := fmt.Sprintf(`
- `, targetURL)
- failed := fmt.Sprintf(`
- `, targetURL)
-
- var body string
- if state == gitlab.Failed {
- body = failed
- } else {
- body = covered
- }
-
- if message != "" {
- body += "
" + message
- }
+func SetMrComment(client *gitlab.Client, projectID int, mergeID int, message string) error {
notes, _, err := client.Notes.ListMergeRequestNotes(projectID, mergeID, &gitlab.ListMergeRequestNotesOptions{})
if err != nil {
@@ -117,7 +102,7 @@ func SetMrComment(client *gitlab.Client, projectID int, mergeID int, state gitla
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") {
+ if strings.Contains(n.Body, "cla-signed.svg") || strings.Contains(n.Body, "cla-not-signed.svg") || strings.Contains(n.Body, "cla-missing-id.svg") || strings.Contains(n.Body, "cla-confirmation-needed.svg") {
previousNote = n
break
}
@@ -127,7 +112,7 @@ func SetMrComment(client *gitlab.Client, projectID int, mergeID int, state gitla
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,
+ Body: &message,
})
if err != nil {
return fmt.Errorf("creating comment for project id : %d and merge id : %d : failed %v", projectID, mergeID, err)
@@ -135,7 +120,7 @@ func SetMrComment(client *gitlab.Client, projectID int, mergeID int, state gitla
} 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,
+ Body: &message,
})
if err != nil {
return fmt.Errorf("updtae comment for project id : %d and merge id : %d : failed %v", projectID, mergeID, err)
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index d61dae58e..1d209d7ea 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -80,7 +80,6 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
projectName := mergeEvent.Project.Name
projectID := mergeEvent.Project.ID
mergeID := mergeEvent.ObjectAttributes.IID
- repositoryName := mergeEvent.Repository.Name
repositoryPath := mergeEvent.Project.PathWithNamespace
lastCommitSha := mergeEvent.ObjectAttributes.LastCommit.ID
@@ -89,8 +88,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitlabProjectName": projectName,
"gitlabProjectID": projectID,
- "repositoryName": repositoryName,
- "repositoryPath": repositoryPath,
+ "repositoryName": repositoryPath,
"mergeID": mergeID,
}
@@ -113,7 +111,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
}
// try to find the repository via the external id
- gitlabRepo, err := s.getGitlabRepoByName(ctx, repositoryName)
+ gitlabRepo, err := s.getGitlabRepoByName(ctx, repositoryPath)
if err != nil {
return fmt.Errorf("finding internal repository for gitlab org name failed : %v", err)
}
@@ -162,7 +160,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
return fmt.Errorf("setting commit status failed : %v", err)
}
- if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Failed, mrCommentContent, signURL); err != nil {
+ if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, mrCommentContent); err != nil {
return fmt.Errorf("setting comment failed : %v", err)
}
@@ -173,13 +171,34 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
return fmt.Errorf("setting commit status failed : %v", err)
}
- if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, gitlab.Success, mrCommentContent, signURL); err != nil {
+ if err := gitlab_api.SetMrComment(gitlabClient, projectID, mergeID, mrCommentContent); err != nil {
return fmt.Errorf("setting comment failed : %v", err)
}
return err
}
func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*gitlab.User, signURL string) string {
+ landingPage := config.GetConfig().CLALandingPage
+ landingPage += "/#/?version=2"
+
+ var badgeHyperlink string
+ if len(missingUsers) > 0{
+ badgeHyperlink = signURL
+ }else{
+ badgeHyperlink = landingPage
+ }
+
+ coveredBadge := fmt.Sprintf(`
+ `, badgeHyperlink)
+ failedBadge := fmt.Sprintf(`
+ `, badgeHyperlink)
+ missingUserIDBadge := fmt.Sprintf(`
+ `, badgeHyperlink)
+ confirmationNeededBadge := fmt.Sprintf(`
+ `, badgeHyperlink)
+
+ var body string
+
var result string
failed := ":x:"
success := ":white_check_mark:"
@@ -191,6 +210,7 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
result += fmt.Sprintf("
%s %s
", success, authorInfo)
}
result += ""
+ body = coveredBadge
}
gitlabSupportURL := "https://about.gitlab.com/support"
@@ -206,6 +226,7 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
please submit a support request ticket.
`, failed, authorInfo, gitlabSupportURL, easyCLASupportURL)
result += msg
+ body = missingUserIDBadge
} else if errors.Is(missingUser.err, missingCompanyAffiliation) {
msg := fmt.Sprintf(`
%s is authorized, but they must confirm their affiliation with their company.
Start the authorization process
@@ -216,7 +237,7 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
please submit a support request ticket.
`, authorInfo, signURL, easyCLASupportURL)
result += msg
-
+ body = confirmationNeededBadge
} else {
msg := fmt.Sprintf(`
%s -
%s's commit is not authorized under a signed CLA.
@@ -225,13 +246,17 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
please submit a support request ticket.
`, signURL, failed, authorInfo, signURL, easyCLASupportURL)
result += msg
-
+ body = failedBadge
}
}
result += ""
}
- return result
+ if result != ""{
+ body += "
" + result
+ }
+
+ return body
}
func GetFullSignURL(gitlabOrganizationID string, gitlabRepositoryID string, mrID string) string {
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
index 7b651d5a1..1dd8a458b 100644
--- a/cla-backend-go/v2/gitlab-activity/service_test.go
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -113,10 +113,11 @@ func TestPrepareMrCommentContent(t *testing.T) {
missingApprovalContains := "%s's commit is not authorized under a signed CLA"
testCases := []struct {
- name string
- signed []*gitlab.User
- missing []*gatedGitlabUser
- expectedMsgs []string
+ name string
+ signed []*gitlab.User
+ missing []*gatedGitlabUser
+ expectedMsgs []string
+ expectedBadge string
}{
{
name: "all signed",
@@ -125,6 +126,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
{ID: 2, Username: "oracle"},
},
expectedMsgs: []string{signedContains, signedContains},
+ expectedBadge: "cla-signed.svg",
},
{
name: "missing id",
@@ -135,6 +137,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
{err: missingID, User: &gitlab.User{ID: 3, Username: "missing"}},
},
expectedMsgs: []string{signedContains, missingUserContains},
+ expectedBadge: "cla-missing-id.svg",
},
{
name: "missing affiliation",
@@ -145,6 +148,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
{err: missingCompanyAffiliation, User: &gitlab.User{ID: 4, Username: "affiliationUser"}},
},
expectedMsgs: []string{signedContains, missingAffiliationContains},
+ expectedBadge: "cla-confirmation-needed.svg",
},
{
name: "missing approval",
@@ -155,6 +159,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
{err: missingCompanyApproval, User: &gitlab.User{ID: 5, Username: "approvalUser"}},
},
expectedMsgs: []string{signedContains, missingApprovalContains},
+ expectedBadge: "cla-not-signed.svg",
},
}
@@ -183,6 +188,8 @@ func TestPrepareMrCommentContent(t *testing.T) {
expected := fmt.Sprintf(tc.expectedMsgs[i], getAuthorInfo(allUsers[i]))
assert.Contains(tt, p, expected)
}
+
+ assert.Contains(tt, result, tc.expectedBadge)
})
}
}
From 81915f7582978dd9c041fde5bafa1bf26ac8d9c1 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 24 Aug 2021 11:15:08 -0700
Subject: [PATCH 0456/1276] Cleanup GitLab Error Messages (#3193)
Signed-off-by: David Deal
---
cla-backend-go/gitlab_api/client_groups.go | 1 -
.../v2/gitlab_organizations/service.go | 28 +++++--------------
2 files changed, 7 insertions(+), 22 deletions(-)
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
index 38720fcb0..196b95531 100644
--- a/cla-backend-go/gitlab_api/client_groups.go
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -38,7 +38,6 @@ func GetGroupsListAll(ctx context.Context, client *goGitLab.Client, minAccessLev
},
AllAvailable: utils.Bool(true), // Show all the groups you have access to (defaults to false for authenticated users, true for administrators); Attributes owned and min_access_level have precedence
MinAccessLevel: goGitLab.AccessLevel(minAccessLevel), // Limit by current user minimal access level.
- //MinAccessLevel: goGitLab.AccessLevel(goGitLab.MaintainerPermissions), // Limit by current user minimal access level.
}
var groupList []*goGitLab.Group
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index aafa3506b..5117ac230 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -366,29 +366,15 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
}
- // Query the groups list to see if the user has minimal access permissions only
- groupsWithMinimalAccessPerms, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient, goGitLab.MinimalAccessPermissions)
- if groupListErr != nil {
- return groupListErr
- }
-
- // Loop through the responses - check to see if we have a match, if so, return a specific error message
- for _, g := range groupsWithMinimalAccessPerms {
- // If we have an external group ID or a full path...
- if (gitLabOrgModel.ExternalGroupID > 0 && g.ID == gitLabOrgModel.ExternalGroupID) ||
- (gitLabOrgModel.OrganizationFullPath != "" && g.FullPath == gitLabOrgModel.OrganizationFullPath) {
- msg := ""
- if gitLabOrgModel.ExternalGroupID > 0 {
- msg = fmt.Sprintf("external ID: %d", g.ID)
- } else if gitLabOrgModel.OrganizationFullPath != "" {
- msg = fmt.Sprintf("full path: '%s'", g.FullPath)
- }
- return fmt.Errorf("found the GitLab group '%s' by using the %s filter - however the authenticated user does not have maintainer or above permissions for this GitLab group", g.FullPath, msg)
- }
+ msg := ""
+ if gitLabOrgModel.ExternalGroupID > 0 {
+ msg = fmt.Sprintf("external ID: %d", gitLabOrgModel.ExternalGroupID)
+ } else if gitLabOrgModel.OrganizationFullPath != "" {
+ msg = fmt.Sprintf("full path: '%s'", gitLabOrgModel.OrganizationFullPath)
}
- return fmt.Errorf("unable to locate GitLab group by using external ID: %d or full path: %s, found %d groups where user has maintainer or above permissions",
- gitLabOrgModel.ExternalGroupID, gitLabOrgModel.OrganizationFullPath, len(groupsWithMaintainerPerms))
+ return fmt.Errorf("unable to locate the provided GitLab group by %s using the provided permissions - discovered %d groups where user has maintainer or above permissions.",
+ msg, len(groupsWithMaintainerPerms))
}
// UpdateGitLabOrganization updates the GitLab organization
From cdce916104ad2a54983105676a6ea804a72f90d3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 24 Aug 2021 16:58:11 -0700
Subject: [PATCH 0457/1276] API Updates to GitLab Group/Org and Repos (#3194)
---
cla-backend-go/events/event_data.go | 40 ++++--
.../github_organizations/handlers.go | 4 +-
cla-backend-go/repositories/constants.go | 3 +
cla-backend-go/swagger/cla.v2.yaml | 74 +++-------
.../common/gitlab-organization-create.yaml | 4 +-
.../common/gitlab-organization-update.yaml | 7 +-
.../common/gitlab-repositories-enable.yaml | 29 ----
.../common/gitlab-repositories-enroll.yaml | 25 ++++
cla-backend-go/tests/utils_list_utils_test.go | 38 ++++++
cla-backend-go/utils/autoenable.go | 4 +-
cla-backend-go/utils/context.go | 4 +-
cla-backend-go/utils/list_utils.go | 18 +++
cla-backend-go/v2/common/models.go | 1 +
.../v2/github_organizations/handlers.go | 4 +-
cla-backend-go/v2/gitlab-activity/service.go | 6 +-
.../v2/gitlab-activity/service_test.go | 8 +-
.../v2/gitlab_organizations/handlers.go | 64 ++++-----
.../v2/gitlab_organizations/repository.go | 94 ++++++++-----
.../v2/gitlab_organizations/service.go | 120 ++++++++++++++---
.../v2/repositories/gitlab_services.go | 88 ++++++------
cla-backend-go/v2/repositories/handlers.go | 122 +++++------------
cla-backend-go/v2/repositories/models.go | 13 ++
cla-backend-go/v2/repositories/repository.go | 127 ++++++++++++++----
cla-backend-go/v2/repositories/service.go | 12 +-
24 files changed, 541 insertions(+), 368 deletions(-)
delete mode 100644 cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
create mode 100644 cla-backend-go/swagger/common/gitlab-repositories-enroll.yaml
create mode 100644 cla-backend-go/tests/utils_list_utils_test.go
create mode 100644 cla-backend-go/utils/list_utils.go
create mode 100644 cla-backend-go/v2/repositories/models.go
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 2175748a0..653cc6644 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -208,6 +208,7 @@ type GitlabOrganizationDeletedEventData struct {
// GitlabOrganizationUpdatedEventData data model
type GitlabOrganizationUpdatedEventData struct {
GitlabOrganizationName string
+ GitLabGroupID int64
AutoEnabled bool
AutoEnabledClaGroupID string
}
@@ -732,13 +733,25 @@ func (ed *GitlabOrganizationDeletedEventData) GetEventDetailsString(args *LogEve
// GetEventDetailsString returns the details string for this event
func (ed *GitlabOrganizationUpdatedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("GitHub Organization:%s was updated with auto-enabled: %t",
- ed.GitlabOrganizationName, ed.AutoEnabled)
+ data := "GitHub Organization" // nolint
+ if ed.GitlabOrganizationName != "" {
+ data = fmt.Sprintf("%s with name: %s", data, ed.GitlabOrganizationName)
+ }
+ if ed.GitLabGroupID > 0 {
+ data = fmt.Sprintf("%s with group ID: %d", data, ed.GitLabGroupID)
+ }
+ data = fmt.Sprintf("%s was updated with auto-enabled: %t", data, ed.AutoEnabled)
if ed.AutoEnabledClaGroupID != "" {
- data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ data = fmt.Sprintf("%s with auto-enabled-cla-group: %s", data, ed.AutoEnabledClaGroupID)
+ }
+ if args.ProjectName != "" {
+ data = fmt.Sprintf("%s for the project %s", data, args.ProjectName)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" with project SFID %s", args.ProjectName)
}
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = fmt.Sprintf("%s by the user %s", data, args.UserName)
}
data = data + "."
return data, true
@@ -1783,19 +1796,22 @@ func (ed *GitlabOrganizationDeletedEventData) GetEventSummaryString(args *LogEve
// GetEventSummaryString returns the summary string for this event
func (ed *GitlabOrganizationUpdatedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
- data := fmt.Sprintf("Gitlab Organization: %s was updated with auto-enabled: %t",
- ed.GitlabOrganizationName, ed.AutoEnabled)
- if ed.AutoEnabledClaGroupID != "" {
- data = data + fmt.Sprintf(" with auto-enabled-cla-group: %s", ed.AutoEnabledClaGroupID)
+ data := "GitHub Organization" // nolint
+ if ed.GitlabOrganizationName != "" {
+ data = fmt.Sprintf("%s with name: %s", data, ed.GitlabOrganizationName)
}
- if args.CLAGroupName != "" {
- data = data + fmt.Sprintf(" for CLA Group %s", args.CLAGroupName)
+ if ed.GitLabGroupID > 0 {
+ data = fmt.Sprintf("%s with group ID: %d", data, ed.GitLabGroupID)
+ }
+ data = fmt.Sprintf("%s was updated with auto-enabled: %t", data, ed.AutoEnabled)
+ if ed.AutoEnabledClaGroupID != "" {
+ data = fmt.Sprintf("%s with auto-enabled-cla-group: %s", data, ed.AutoEnabledClaGroupID)
}
if args.ProjectName != "" {
- data = data + fmt.Sprintf(" for project %s", args.ProjectName)
+ data = fmt.Sprintf("%s for the project %s", data, args.ProjectName)
}
if args.UserName != "" {
- data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ data = fmt.Sprintf("%s by the user %s", data, args.UserName)
}
data = data + "."
return data, true
diff --git a/cla-backend-go/github_organizations/handlers.go b/cla-backend-go/github_organizations/handlers.go
index c09ed5ffb..e40db30fe 100644
--- a/cla-backend-go/github_organizations/handlers.go
+++ b/cla-backend-go/github_organizations/handlers.go
@@ -53,7 +53,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
})
}
- if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ if !utils.ValidateAutoEnabledClaGroupID(*params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
return github_organizations.NewAddProjectGithubOrganizationBadRequest().WithPayload(&models.ErrorResponse{
Code: "400",
Message: "EasyCLA - 400 Bad Request - AutoEnabledClaGroupID can't be empty when AutoEnabled",
@@ -148,7 +148,7 @@ func Configure(api *operations.ClaAPI, service ServiceInterface, eventService ev
})
}
- if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ if !utils.ValidateAutoEnabledClaGroupID(*params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
Code: "400",
Message: "EasyCLA - 400 Bad Request - AutoEnabledClaGroupID can't be empty when AutoEnabled",
diff --git a/cla-backend-go/repositories/constants.go b/cla-backend-go/repositories/constants.go
index 69e937a88..c673e5661 100644
--- a/cla-backend-go/repositories/constants.go
+++ b/cla-backend-go/repositories/constants.go
@@ -42,6 +42,9 @@ const RepositoryDisabled = "disabled"
// RepositoryProjectIndex constant
const RepositoryProjectIndex = "project-repository-index"
+// RepositoryTypeIndex constant
+const RepositoryTypeIndex = "repository-type-index"
+
// RepositoryExternalIDIndex constant
const RepositoryExternalIDIndex = "external-repository-index"
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 99f08c281..8809d7dde 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1688,11 +1688,11 @@ paths:
tags:
- gitlab-organizations
- /project/{projectSFID}/gitlab/organizations/{orgName}/config:
+ /project/{projectSFID}/gitlab/group/{gitLabGroupID}/config:
put:
- summary: Update Gitlab Organization Configuration
- description: Endpoint to adjust the Gitlab Organization Configuration, such as toggling the auto-enable flag
- operationId: updateProjectGitlabOrganizationConfig
+ summary: Update Gitlab Group/Organization Configuration
+ description: Endpoint to adjust the Gitlab Group/Organization Configuration by GitLab Group ID
+ operationId: updateProjectGitlabGroupConfig
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-acl"
@@ -1702,9 +1702,9 @@ paths:
in: path
type: string
required: true
- - name: orgName
+ - name: gitLabGroupID
in: path
- type: string
+ type: integer
required: true
- in: body
name: body
@@ -1729,10 +1729,12 @@ paths:
tags:
- gitlab-organizations
+ # /project/{projectSFID}/gitlab/organization?organization_full_path=linuxfoundation/product/test:
+ /project/{projectSFID}/gitlab/organization:
delete:
- summary: Delete Gitlab organization in the project
- description: Endpoint to delete the Gitlab organization for the project
- operationId: deleteProjectGitlabOrganization
+ summary: Delete Gitlab Group/Organization Configuration
+ description: Endpoint to delete the Gitlab Group/Organization by Group ID
+ operationId: deleteProjectGitlabGroupConfig
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-acl"
@@ -1742,8 +1744,8 @@ paths:
in: path
type: string
required: true
- - name: orgName
- in: path
+ - name: organization_full_path
+ in: query
type: string
required: true
responses:
@@ -1766,9 +1768,9 @@ paths:
/project/{projectSFID}/gitlab/repositories:
put:
- summary: Enables GitLab repositories for the CLA Group
- description: Endpoint to enable one or more GitLab repositories for the CLA Group
- operationId: enableGitLabRepository
+ summary: Enrolls/Unenrolls GitLab repositories for the CLA Group
+ description: Endpoint to enroll or unenroll GitLab repositories for the CLA Group
+ operationId: enrollGitLabRepository
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-acl"
@@ -1779,9 +1781,9 @@ paths:
type: string
required: true
- in: body
- name: gitlab-repositories-enable
+ name: gitlab-repositories-enroll
schema:
- $ref: '#/definitions/gitlab-repositories-enable'
+ $ref: '#/definitions/gitlab-repositories-enroll'
required: true
responses:
'200':
@@ -1839,42 +1841,6 @@ paths:
tags:
- gitlab-repositories
- /project/{projectSFID}/gitlab/repositories/{repositoryExternalID}:
- delete:
- summary: Unenrolls the GitLab repository from the CLA Group
- description: Endpoint to unenroll a GitLab repository from a CLA Group
- operationId: unenrollGitLabRepository
- parameters:
- - $ref: "#/parameters/x-request-id"
- - $ref: "#/parameters/x-acl"
- - $ref: "#/parameters/x-username"
- - $ref: "#/parameters/x-email"
- - name: projectSFID
- in: path
- type: string
- required: true
- - name: repositoryExternalID
- in: path
- type: integer
- required: true
- responses:
- '204':
- description: 'Resource Deleted'
- headers:
- x-request-id:
- type: string
- description: The unique request ID value - assigned/set by the API Gateway based on the session
- '400':
- $ref: '#/responses/invalid-request'
- '401':
- $ref: '#/responses/unauthorized'
- '403':
- $ref: '#/responses/forbidden'
- '404':
- $ref: '#/responses/not-found'
- tags:
- - gitlab-repositories
-
/cla-group/{claGroupID}/icla/signatures:
get:
summary: List individual signatures for CLA Group
@@ -4504,8 +4470,8 @@ definitions:
gitlab-repositories-list:
$ref: './common/gitlab-repositories-list.yaml'
- gitlab-repositories-enable:
- $ref: './common/gitlab-repositories-enable.yaml'
+ gitlab-repositories-enroll:
+ $ref: './common/gitlab-repositories-enroll.yaml'
# ---------------------------------------------------------------------------
# CLA Group Definitions
diff --git a/cla-backend-go/swagger/common/gitlab-organization-create.yaml b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
index 1bb91ac86..fdadba9a0 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-create.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-create.yaml
@@ -27,8 +27,8 @@ properties:
description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
default: false
auto_enabled_cla_group_id:
- type: string
- description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
+ $ref: './common/properties/internal-id.yaml'
+ description: Specifies which CLA Group ID to be used when the auto enabled flag in enabled for the GitLab Group/Organization. When the auto enabled flag is set to true, this field needs to be set to a valid CLA Group ID value.
branch_protection_enabled:
type: boolean
description: Flag to indicate if this GitLab Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
diff --git a/cla-backend-go/swagger/common/gitlab-organization-update.yaml b/cla-backend-go/swagger/common/gitlab-organization-update.yaml
index dec67909d..fcad44a80 100644
--- a/cla-backend-go/swagger/common/gitlab-organization-update.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization-update.yaml
@@ -2,15 +2,14 @@
# SPDX-License-Identifier: MIT
type: object
-required:
- - auto_enabled
+description: GitLab Organization Update model
properties:
auto_enabled:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Group/Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
auto_enabled_cla_group_id:
- type: string
- description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the GitLab Group/Organization. If autoEnabled is on this field needs to be set as well.
+ $ref: './common/properties/internal-id.yaml'
+ description: Specifies which CLA Group ID to be used when the auto enabled flag in enabled for the GitLab Group/Organization. When the auto enabled flag is set to true, this field needs to be set to a valid CLA Group ID value.
branch_protection_enabled:
type: boolean
description: Flag to indicate if this Group/Organization is configured to automatically setup branch protection on CLA enabled repositories.
diff --git a/cla-backend-go/swagger/common/gitlab-repositories-enable.yaml b/cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
deleted file mode 100644
index e28d91c14..000000000
--- a/cla-backend-go/swagger/common/gitlab-repositories-enable.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright The Linux Foundation and each contributor to CommunityBridge.
-# SPDX-License-Identifier: MIT
-
-type: object
-description: 'GitLab repositories enable model'
-properties:
- gitlab_organization_name:
- type: string
- description: The organization name associated with this repository
- example: 'The Linux Foundation/product/EasyCLA'
- organization_external_id:
- type: integer
- description: The Gitlab Group/Organization external ID used by GitLab
- example: 13050017
- minimum: 1
- organization_full_path:
- type: string
- description: The Gitlab Group/Organization full path
- example: "linuxfoundation/product/easycla"
- cla_group_id:
- description: CLA Group ID
- $ref: './common/properties/internal-id.yaml'
- repository_gitlab_ids:
- type: array
- items:
- description: the repository external identifier, such as the GitLab ID of the repository
- type: integer
- minimum: 1
- example: 7
diff --git a/cla-backend-go/swagger/common/gitlab-repositories-enroll.yaml b/cla-backend-go/swagger/common/gitlab-repositories-enroll.yaml
new file mode 100644
index 000000000..6c491a657
--- /dev/null
+++ b/cla-backend-go/swagger/common/gitlab-repositories-enroll.yaml
@@ -0,0 +1,25 @@
+# Copyright The Linux Foundation and each contributor to CommunityBridge.
+# SPDX-License-Identifier: MIT
+
+type: object
+description: 'GitLab repositories enable model'
+properties:
+ cla_group_id:
+ description: CLA Group ID
+ $ref: './common/properties/internal-id.yaml'
+ enroll:
+ type: array
+ description: a list of GitLab repositories to enroll
+ items:
+ description: the GitLab repository external identifier, such as the GitLab ID of the repository
+ type: integer
+ minimum: 1
+ example: 7
+ unenroll:
+ type: array
+ description: a list of GitLab repositories to unenroll
+ items:
+ description: the GitLab repository external identifier, such as the GitLab ID of the repository
+ type: integer
+ minimum: 1
+ example: 7
diff --git a/cla-backend-go/tests/utils_list_utils_test.go b/cla-backend-go/tests/utils_list_utils_test.go
new file mode 100644
index 000000000..e6d5f1e08
--- /dev/null
+++ b/cla-backend-go/tests/utils_list_utils_test.go
@@ -0,0 +1,38 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFindInt64Duplicates(t *testing.T) {
+ type TestCase struct {
+ A []int64
+ B []int64
+ Expected []int64
+ }
+ testInputs := []TestCase{
+ {nil, nil, []int64{}},
+ {nil, []int64{}, []int64{}},
+ {[]int64{}, nil, []int64{}},
+ {nil, nil, []int64{}},
+ {[]int64{}, []int64{}, []int64{}},
+ {[]int64{1}, []int64{}, []int64{}},
+ {[]int64{}, []int64{1}, []int64{}},
+ {[]int64{1, 2}, []int64{}, []int64{}},
+ {[]int64{}, []int64{1, 2}, []int64{}},
+ {[]int64{1}, []int64{1}, []int64{1}},
+ {[]int64{1, 2}, []int64{1}, []int64{1}},
+ {[]int64{1, 2}, []int64{1, 3, 4}, []int64{1}},
+ {[]int64{1, 2, 3, 4, 5}, []int64{1, 5, 3, 4}, []int64{1, 3, 5, 4}},
+ }
+
+ for _, testInput := range testInputs {
+ assert.ElementsMatch(t, testInput.Expected, utils.FindInt64Duplicates(testInput.A, testInput.B))
+ }
+}
diff --git a/cla-backend-go/utils/autoenable.go b/cla-backend-go/utils/autoenable.go
index a3283bfee..478019ba7 100644
--- a/cla-backend-go/utils/autoenable.go
+++ b/cla-backend-go/utils/autoenable.go
@@ -4,8 +4,8 @@
package utils
// ValidateAutoEnabledClaGroupID checks for validation if autoEnabled flag is on autoEnabledClaGroupID is enabled as well
-func ValidateAutoEnabledClaGroupID(autoEnabled *bool, autoEnabledClaGroupID string) bool {
- if autoEnabled == nil || !*autoEnabled {
+func ValidateAutoEnabledClaGroupID(autoEnabled bool, autoEnabledClaGroupID string) bool {
+ if !autoEnabled {
return true
}
diff --git a/cla-backend-go/utils/context.go b/cla-backend-go/utils/context.go
index e1be4a955..49e612a72 100644
--- a/cla-backend-go/utils/context.go
+++ b/cla-backend-go/utils/context.go
@@ -39,12 +39,12 @@ func NewContextWithUser(authUser *auth.User) context.Context {
return context.Background()
}
- return context.WithValue(context.WithValue(context.Background(), XREQUESTID, requestID), "authUser", authUser) // nolint
+ return context.WithValue(context.WithValue(context.Background(), XREQUESTID, requestID), CtxAuthUser, authUser) // nolint
}
// ContextWithRequestAndUser returns a new context with the specified request ID and user
func ContextWithRequestAndUser(ctx context.Context, reqID string, authUser *auth.User) context.Context {
- return context.WithValue(context.WithValue(ctx, XREQUESTID, reqID), "authUser", authUser) // nolint
+ return context.WithValue(context.WithValue(ctx, XREQUESTID, reqID), CtxAuthUser, authUser) // nolint
}
// ContextWithUser returns a new context with the specified user
diff --git a/cla-backend-go/utils/list_utils.go b/cla-backend-go/utils/list_utils.go
new file mode 100644
index 000000000..999a98a11
--- /dev/null
+++ b/cla-backend-go/utils/list_utils.go
@@ -0,0 +1,18 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+// FindInt64Duplicates returns true if the two lists include any duplicates, false otherwise. Returns the duplicates
+func FindInt64Duplicates(a, b []int64) []int64 {
+ var duplicates []int64
+ for i := 0; i < len(a); i++ {
+ for j := 0; j < len(b); j++ {
+ if a[i] == b[j] {
+ duplicates = append(duplicates, a[i])
+ }
+ }
+ }
+
+ return duplicates
+}
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index 6fbd36291..5b94929c3 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -25,6 +25,7 @@ type GitLabOrganization struct {
AutoEnabledClaGroupID string `json:"auto_enabled_cla_group_id,omitempty"`
AuthInfo string `json:"auth_info"`
AuthState string `json:"auth_state"`
+ Note string `json:"note,omitempty"`
Version string `json:"version,omitempty"`
}
diff --git a/cla-backend-go/v2/github_organizations/handlers.go b/cla-backend-go/v2/github_organizations/handlers.go
index a08053bd7..1d53a10eb 100644
--- a/cla-backend-go/v2/github_organizations/handlers.go
+++ b/cla-backend-go/v2/github_organizations/handlers.go
@@ -112,7 +112,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ if !utils.ValidateAutoEnabledClaGroupID(*params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
msg := "AutoEnabledClaGroupID can't be empty when AutoEnabled"
log.WithFields(f).WithError(err).Warn(msg)
return github_organizations.NewAddProjectGithubOrganizationBadRequest().WithPayload(
@@ -213,7 +213,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ if !utils.ValidateAutoEnabledClaGroupID(*params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
msg := fmt.Sprintf("AutoEnabledClaGroupID can't be empty when AutoEnabled flag is set to true - issue in request body for project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
log.WithFields(f).Debug(msg)
return github_organizations.NewUpdateProjectGithubOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 1d209d7ea..103300a65 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -182,9 +182,9 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
landingPage += "/#/?version=2"
var badgeHyperlink string
- if len(missingUsers) > 0{
+ if len(missingUsers) > 0 {
badgeHyperlink = signURL
- }else{
+ } else {
badgeHyperlink = landingPage
}
@@ -252,7 +252,7 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
result += ""
}
- if result != ""{
+ if result != "" {
body += "
" + result
}
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
index 1dd8a458b..14c98dfef 100644
--- a/cla-backend-go/v2/gitlab-activity/service_test.go
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -125,7 +125,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
{ID: 1, Username: "neo"},
{ID: 2, Username: "oracle"},
},
- expectedMsgs: []string{signedContains, signedContains},
+ expectedMsgs: []string{signedContains, signedContains},
expectedBadge: "cla-signed.svg",
},
{
@@ -136,7 +136,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
missing: []*gatedGitlabUser{
{err: missingID, User: &gitlab.User{ID: 3, Username: "missing"}},
},
- expectedMsgs: []string{signedContains, missingUserContains},
+ expectedMsgs: []string{signedContains, missingUserContains},
expectedBadge: "cla-missing-id.svg",
},
{
@@ -147,7 +147,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
missing: []*gatedGitlabUser{
{err: missingCompanyAffiliation, User: &gitlab.User{ID: 4, Username: "affiliationUser"}},
},
- expectedMsgs: []string{signedContains, missingAffiliationContains},
+ expectedMsgs: []string{signedContains, missingAffiliationContains},
expectedBadge: "cla-confirmation-needed.svg",
},
{
@@ -158,7 +158,7 @@ func TestPrepareMrCommentContent(t *testing.T) {
missing: []*gatedGitlabUser{
{err: missingCompanyApproval, User: &gitlab.User{ID: 5, Username: "approvalUser"}},
},
- expectedMsgs: []string{signedContains, missingApprovalContains},
+ expectedMsgs: []string{signedContains, missingApprovalContains},
expectedBadge: "cla-not-signed.svg",
},
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 9fd0214d7..e91f48e27 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -21,7 +21,6 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
- "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/sirupsen/logrus"
@@ -160,7 +159,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
f["autoEnabled"] = utils.BoolValue(params.Body.AutoEnabled)
f["autoEnabledClaGroupID"] = params.Body.AutoEnabledClaGroupID
- if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
+ if !utils.ValidateAutoEnabledClaGroupID(*params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
msg := "AutoEnabledClaGroupID can't be empty when AutoEnabled"
err := fmt.Errorf(msg)
log.WithFields(f).Warn(msg)
@@ -194,29 +193,21 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_organizations.NewAddProjectGitlabOrganizationOK().WithPayload(result)
})
- api.GitlabOrganizationsUpdateProjectGitlabOrganizationConfigHandler = gitlab_organizations.UpdateProjectGitlabOrganizationConfigHandlerFunc(func(params gitlab_organizations.UpdateProjectGitlabOrganizationConfigParams, authUser *auth.User) middleware.Responder {
+ api.GitlabOrganizationsUpdateProjectGitlabGroupConfigHandler = gitlab_organizations.UpdateProjectGitlabGroupConfigHandlerFunc(func(params gitlab_organizations.UpdateProjectGitlabGroupConfigParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
- if params.Body.AutoEnabled == nil {
- return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "EasyCLA - 400 Bad Request - missing auto enable value in body",
- })
- }
if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
- return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(&models.ErrorResponse{
- Code: "400",
- Message: "EasyCLA - 400 Bad Request - AutoEnabledClaGroupID can't be empty when AutoEnabled",
- })
+ msg := "AutoEnabledClaGroupID can't be empty when AutoEnabled is set to true"
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, 0, params.OrgName, "", *params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.GitLabGroupID, "", "", params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
if err != nil {
if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
- return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
}
- return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, "updating gitlab org", err))
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, "updating gitlab org", err))
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
@@ -225,52 +216,53 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
LfUsername: authUser.UserName,
UserName: authUser.UserName,
EventData: &events.GitlabOrganizationUpdatedEventData{
- GitlabOrganizationName: params.OrgName,
- AutoEnabled: *params.Body.AutoEnabled,
+ GitLabGroupID: params.GitLabGroupID,
+ AutoEnabledClaGroupID: params.Body.AutoEnabledClaGroupID,
+ AutoEnabled: params.Body.AutoEnabled,
},
})
- return gitlab_organizations.NewUpdateProjectGitlabOrganizationConfigOK()
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigOK()
})
- api.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler = gitlab_organizations.DeleteProjectGitlabOrganizationHandlerFunc(func(params gitlab_organizations.DeleteProjectGitlabOrganizationParams, authUser *auth.User) middleware.Responder {
+ api.GitlabOrganizationsDeleteProjectGitlabGroupConfigHandler = gitlab_organizations.DeleteProjectGitlabGroupConfigHandlerFunc(func(params gitlab_organizations.DeleteProjectGitlabGroupConfigParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsDeleteProjectGitlabOrganizationHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": params.ProjectSFID,
- "orgName": params.OrgName,
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
+ "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsDeleteProjectGitlabGroupConfigHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": params.ProjectSFID,
+ "organizationFullPath": params.OrganizationFullPath,
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
}
// Load the project
psc := project_service.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
- return gitlab_organizations.NewDeleteProjectGitlabOrganizationNotFound().WithPayload(
+ return gitlab_organizations.NewDeleteProjectGitlabGroupConfigNotFound().WithPayload(
utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Delete Project GitLab Organizations for Project '%s' with scope of %s",
+ msg := fmt.Sprintf("user %s does not have access to Delete Project GitLab Group/Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
- return gitlab_organizations.NewDeleteProjectGitlabOrganizationForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ return gitlab_organizations.NewDeleteProjectGitlabGroupConfigForbidden().WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- err = service.DeleteGitLabOrganization(ctx, params.ProjectSFID, params.OrgName)
+ err = service.DeleteGitLabOrganizationByFullPath(ctx, params.ProjectSFID, params.OrganizationFullPath)
if err != nil {
if strings.Contains(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("project not found with given SFID: %s", params.ProjectSFID)
log.WithFields(f).Debug(msg)
- return gitlab_organizations.NewDeleteProjectGitlabOrganizationNotFound().WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
+ return gitlab_organizations.NewDeleteProjectGitlabGroupConfigNotFound().WithPayload(utils.ErrorResponseNotFoundWithError(reqID, msg, err))
}
- msg := fmt.Sprintf("problem deleting Gitlab Organization with project SFID: %s for organization: %s", params.ProjectSFID, params.OrgName)
- log.WithFields(f).Debug(msg)
- return gitlab_organizations.NewDeleteProjectGitlabOrganizationBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ msg := fmt.Sprintf("problem deleting Gitlab Group with project SFID: %s with path: %s", params.ProjectSFID, params.OrganizationFullPath)
+ log.WithFields(f).Warn(msg)
+ return gitlab_organizations.NewDeleteProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
@@ -278,11 +270,11 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
EventType: events.GitlabOrganizationDeleted,
ProjectSFID: params.ProjectSFID,
EventData: &events.GitlabOrganizationDeletedEventData{
- GitlabOrganizationName: params.OrgName,
+ GitlabOrganizationName: params.OrganizationFullPath,
},
})
- return gitlab_organizations.NewDeleteProjectGitlabOrganizationNoContent()
+ return gitlab_organizations.NewDeleteProjectGitlabGroupConfigNoContent()
})
api.GitlabActivityGitlabOauthCallbackHandler = gitlab_activity.GitlabOauthCallbackHandlerFunc(func(params gitlab_activity.GitlabOauthCallbackParams) middleware.Responder {
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index 54d206003..af939445b 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -48,7 +48,7 @@ type RepositoryInterface interface {
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
+ DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgFullPath string) error
}
// Repository object/struct
@@ -212,7 +212,7 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
- log.WithFields(f).Warnf("problem building query expression, error: %+v2Models", err)
+ log.WithFields(f).WithError(err).Warnf("problem building query expression, error: %+v", err)
return nil, err
}
@@ -293,7 +293,7 @@ func (repo *Repository) GetGitLabOrganizationByName(ctx context.Context, gitLabO
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
+ log.WithFields(f).WithError(err).Warnf("problem decoding database results, error: %+v", err)
return nil, err
}
@@ -341,7 +341,7 @@ func (repo *Repository) GetGitLabOrganizationByExternalID(ctx context.Context, g
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
+ log.WithFields(f).WithError(err).Warnf("problem decoding database results, error: %+v", err)
return nil, err
}
@@ -389,7 +389,7 @@ func (repo *Repository) GetGitLabOrganizationByFullPath(ctx context.Context, gro
var resultOutput []*common.GitLabOrganization
err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
if err != nil {
- log.WithFields(f).Warnf("problem decoding database results, error: %+v2Models", err)
+ log.WithFields(f).WithError(err).Warnf("problem decoding database results, error: %+v", err)
return nil, err
}
@@ -445,7 +445,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
_, currentTime := utils.CurrentTime()
gitlabOrg, lookupErr := repo.GetGitLabOrganization(ctx, organizationID)
if lookupErr != nil || gitlabOrg == nil {
- log.WithFields(f).Warnf("error looking up Gitlab organization by id: %s, error: %+v2Models", organizationID, lookupErr)
+ log.WithFields(f).WithError(lookupErr).Warnf("error looking up Gitlab organization by id: %s, error: %+v", organizationID, lookupErr)
return lookupErr
}
@@ -500,7 +500,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
log.WithFields(f).Debug("updating gitlab organization record...")
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
- log.WithFields(f).Warnf("unable to update Gitlab organization record, error: %+v2Models", updateErr)
+ log.WithFields(f).WithError(updateErr).Warnf("unable to update Gitlab organization record, error: %+v", updateErr)
return updateErr
}
@@ -510,7 +510,7 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
// UpdateGitLabOrganization updates the GitLab group based on the specified values
func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
f := logrus.Fields{
- "functionName": "gitlab_organizations.repository.UpdateGitLabOrganizationByExternalID",
+ "functionName": "gitlab_organizations.repository.UpdateGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
"groupID": groupID,
@@ -528,7 +528,7 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v2Models", groupID, groupFullPath)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v", groupID, groupFullPath)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
@@ -536,7 +536,7 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v2Models", groupFullPath, getErr)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v", groupFullPath, getErr)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
@@ -597,55 +597,60 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
TableName: aws.String(repo.gitlabOrgTableName),
}
- log.WithFields(f).Debugf("updating GitLab organization record: %+v2Models", input)
+ log.WithFields(f).Debugf("updating GitLab organization record: %+v", input)
_, updateErr := repo.dynamoDBClient.UpdateItem(input)
if updateErr != nil {
- log.WithFields(f).Warnf("unable to update GitLab organization record, error: %+v2Models", updateErr)
+ log.WithFields(f).WithError(updateErr).Warnf("unable to update GitLab organization record, error: %+v", updateErr)
return updateErr
}
return nil
}
-// DeleteGitLabOrganization deletes the specified GitLab organization
-func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error {
+// DeleteGitLabOrganizationByFullPath deletes the specified GitLab organization
+func (repo *Repository) DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgFullPath string) error {
f := logrus.Fields{
- "functionName": "v1.gitlab_organizations.repository.DeleteGitLabOrganization",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "gitlabOrgName": gitlabOrgName,
+ "functionName": "v1.gitlab_organizations.repository.DeleteGitLabOrganizationByFullPath",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "gitlabOrgFullPath": gitlabOrgFullPath,
}
- var gitlabOrganizationID string
- orgs, orgErr := repo.GetGitLabOrganizations(ctx, projectSFID)
+ log.WithFields(f).Debugf("loading GitLab group/organizations list for path: %s", gitlabOrgFullPath)
+ org, orgErr := repo.GetGitLabOrganizationByFullPath(ctx, gitlabOrgFullPath)
if orgErr != nil {
- errMsg := fmt.Sprintf("gitlab organization is not found using projectSFID: %s, error: %+v2Models", projectSFID, orgErr)
- log.WithFields(f).Warn(errMsg)
+ errMsg := fmt.Sprintf("GitLab group/organization is not found using group/organization: %s, error: %+v", gitlabOrgFullPath, orgErr)
+ log.WithFields(f).WithError(orgErr).Warn(errMsg)
return errors.New(errMsg)
}
-
- for _, gitLabOrg := range orgs.List {
- if strings.EqualFold(gitLabOrg.OrganizationName, gitlabOrgName) {
- gitlabOrganizationID = gitLabOrg.OrganizationID
- break
- }
+ // Nothing to delete or disable
+ if org == nil || !org.Enabled {
+ return nil
}
- log.WithFields(f).Debug("Deleting GitLab organization...")
+ log.WithFields(f).Debugf("deleting GitLab group/organization under path: %s...", gitlabOrgFullPath)
// Update enabled flag as false
_, currentTime := utils.CurrentTime()
- note := fmt.Sprintf("Enabled set to false due to org deletion at %s ", currentTime)
+ note := fmt.Sprintf("Enabled set to false due to org deletion on %s by %s.", currentTime, utils.GetUserNameFromContext(ctx))
+ if org.Note != "" {
+ note = fmt.Sprintf("%s. %s.", org.Note, note)
+ }
_, err := repo.dynamoDBClient.UpdateItem(
&dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
GitLabOrganizationsOrganizationIDColumn: {
- S: aws.String(gitlabOrganizationID),
+ S: aws.String(org.OrganizationID),
},
},
ExpressionAttributeNames: map[string]*string{
- "#E": aws.String(GitLabOrganizationsEnabledColumn),
- "#N": aws.String(GitLabOrganizationsNoteColumn),
- "#D": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#E": aws.String(GitLabOrganizationsEnabledColumn),
+ "#N": aws.String(GitLabOrganizationsNoteColumn),
+ "#D": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#AI": aws.String(GitLabOrganizationsAuthInfoColumn),
+ "#AE": aws.String(GitLabOrganizationsAutoEnabledColumn),
+ "#AECLA": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
+ "#EID": aws.String(GitLabOrganizationsExternalGitLabGroupIDColumn),
+ "#BP": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
},
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":e": {
@@ -657,14 +662,29 @@ func (repo *Repository) DeleteGitLabOrganization(ctx context.Context, projectSFI
":d": {
S: aws.String(currentTime),
},
+ ":ai": {
+ S: aws.String(""),
+ },
+ ":ae": {
+ BOOL: aws.Bool(false),
+ },
+ ":aecla": {
+ S: aws.String(""),
+ },
+ ":eid": {
+ N: aws.String("0"),
+ },
+ ":bp": {
+ BOOL: aws.Bool(false),
+ },
},
- UpdateExpression: aws.String("SET #E = :e, #N = :n, #D = :d"),
+ UpdateExpression: aws.String("SET #E = :e, #N = :n, #D = :d, #AI = :ai, #AE = :ae, #AECLA = :aecla, #EID = :eid, #BP = :bp"),
TableName: aws.String(repo.gitlabOrgTableName),
},
)
if err != nil {
- errMsg := fmt.Sprintf("error deleting gitlab organization: %s - %+v2Models", gitlabOrgName, err)
- log.WithFields(f).Warnf(errMsg)
+ errMsg := fmt.Sprintf("error updating gitlab organization by path: %s using GitLab group/organization ID: %s - %+v", gitlabOrgFullPath, org.OrganizationID, err)
+ log.WithFields(f).WithError(err).Warnf(errMsg)
return errors.New(errMsg)
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 5117ac230..d4c81331d 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -37,11 +37,13 @@ type ServiceInterface interface {
GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
+ GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*models.GitlabOrganization, error)
+ GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
- DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitlabOrgName string) error
+ DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID string, gitlabOrgFullPath string) error
}
// Service data model
@@ -74,6 +76,32 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
"groupFullPath": input.OrganizationFullPath,
}
+ var existingModel *models.GitlabOrganization
+ var getErr error
+ if input.OrganizationFullPath != "" {
+ existingModel, getErr = s.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
+ if getErr != nil {
+ log.WithFields(f).WithError(getErr).Warnf("problem querying GitLab group/organization using full path: %s", input.OrganizationFullPath)
+ return nil, getErr
+ }
+ }
+ if input.GroupID > 0 {
+ existingModel, getErr = s.GetGitLabOrganizationByGroupID(ctx, input.GroupID)
+ if getErr != nil {
+ log.WithFields(f).WithError(getErr).Warnf("problem querying GitLab group/organization using group ID: %d", input.GroupID)
+ return nil, getErr
+ }
+ }
+
+ if existingModel != nil {
+ updateErr := s.UpdateGitLabOrganization(ctx, projectSFID, input.GroupID, "", input.OrganizationFullPath, utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled))
+ if updateErr != nil {
+ log.WithFields(f).WithError(updateErr).Warnf("problem updating GitLab group/organization, error: %+v", updateErr)
+ return nil, getErr
+ }
+ return s.GetGitLabOrganizations(ctx, projectSFID)
+ }
+
psc := v2ProjectService.GetClient()
project, err := psc.GetProject(projectSFID)
if err != nil {
@@ -157,7 +185,40 @@ func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganiz
}
return common.ToModel(dbModel), nil
+}
+
+// GetGitLabOrganizationByFullPath returns the GitLab group/organization using the specified full path
+func (s *Service) GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByFullPath",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabOrganizationFullPath": gitLabOrganizationFullPath,
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab group/organization using full path: %s", gitLabOrganizationFullPath)
+ dbModel, err := s.repo.GetGitLabOrganizationByFullPath(ctx, gitLabOrganizationFullPath)
+ if err != nil {
+ return nil, err
+ }
+
+ return common.ToModel(dbModel), nil
+}
+
+// GetGitLabOrganizationByGroupID returns the GitLab group/organization using the specified group ID
+func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*models.GitlabOrganization, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByGroupID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabGroupID": gitLabGroupID,
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab group/organization using group ID: %d", gitLabGroupID)
+ dbModel, err := s.repo.GetGitLabOrganizationByExternalID(ctx, gitLabGroupID)
+ if err != nil {
+ return nil, err
+ }
+ return common.ToModel(dbModel), nil
}
// GetGitLabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
@@ -225,7 +286,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
log.WithFields(f).WithError(repoErr).Debugf("no GitLab repositories onboarded for project : %s", projectSFID)
} else {
- log.WithFields(f).WithError(repoErr).Debugf("unexpected error while fetching GitLab group repositories for project: %s, error: %v", projectSFID, repoErr)
+ log.WithFields(f).WithError(repoErr).Debugf("unexpected error while fetching GitLab group repositories for project: %s, error type: %T, error: %v", projectSFID, repoErr, repoErr)
}
}
@@ -389,32 +450,49 @@ func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID stri
return s.repo.UpdateGitLabOrganization(ctx, projectSFID, groupID, organizationName, groupFullPath, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
}
-// DeleteGitLabOrganization deletes the specified GitLab organization
-func (s *Service) DeleteGitLabOrganization(ctx context.Context, projectSFID string, gitLabOrgName string) error {
+// DeleteGitLabOrganizationByFullPath deletes the specified GitLab organization by full path
+func (s *Service) DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID string, gitLabOrgFullPath string) error {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.DeleteGitLabOrganization",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "gitLabOrgName": gitLabOrgName,
+ "functionName": "v2.gitlab_organizations.service.DeleteGitLabOrganizationByFullPath",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "gitLabOrgFullPath": gitLabOrgFullPath,
+ }
+
+ // Check for enabled repos...
+ repoList, getRepListErr := s.v2GitRepoService.GitLabGetRepositoriesByProjectSFID(ctx, projectSFID)
+ if getRepListErr != nil {
+ // If nothing to delete...
+ if _, ok := getRepListErr.(*utils.GitLabRepositoryNotFound); ok {
+ log.WithFields(f).Debugf("no repositories found under GitLab group/organization: %s", gitLabOrgFullPath)
+ } else {
+ return getRepListErr
+ }
}
- // Lookup the parent
- parentProjectSFID, projErr := v2ProjectService.GetClient().GetParentProject(projectSFID)
- if projErr != nil {
- log.WithFields(f).Warnf("problem fetching project parent SFID, error: %+v", projErr)
- return projErr
+ // Check to see if we still have enabled repos belonging to this GitLab organization/group
+ var enabledRepoList []string
+ if repoList != nil && len(repoList.List) > 0 {
+ for _, repo := range repoList.List {
+ if strings.HasPrefix(repo.RepositoryName, gitLabOrgFullPath) && repo.Enabled {
+ enabledRepoList = append(enabledRepoList, repo.RepositoryName)
+ }
+ }
}
- log.WithFields(f).Debugf("retrieved parent of project sfid : %s -> %s", projectSFID, parentProjectSFID)
+ if len(enabledRepoList) > 0 {
+ return fmt.Errorf("the following repositories are still enabled under the GitLab Group/Organization: %s - %s", gitLabOrgFullPath, strings.Join(enabledRepoList, ","))
+ }
- // Todo: Enable this when the repositories are implemented
- //err := s.ghRepository.GitHubDisableRepositoriesOfOrganization(ctx, parentProjectSFID, gitLabOrgName)
- //if err != nil {
- // log.WithFields(f).Warnf("problem disabling repositories for github organizations, error: %+v", projErr)
- // return err
- //}
+ // First delete the GitLab project/repos
+ log.WithFields(f).Debugf("deleting GitLab repos under group: %s", gitLabOrgFullPath)
+ repoDeleteErr := s.v2GitRepoService.GitLabDeleteRepositories(ctx, gitLabOrgFullPath)
+ if repoDeleteErr != nil {
+ log.WithFields(f).WithError(repoDeleteErr).Warnf("problem deleting GitLab repos under group: %s", gitLabOrgFullPath)
+ return repoDeleteErr
+ }
- return s.repo.DeleteGitLabOrganization(ctx, projectSFID, gitLabOrgName)
+ return s.repo.DeleteGitLabOrganizationByFullPath(ctx, projectSFID, gitLabOrgFullPath)
}
func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 303ececdf..83d69b212 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "sort"
"strconv"
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
@@ -25,38 +26,38 @@ import (
)
// GitLabAddRepositories service function
-func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesEnable) (*v2Models.GitlabRepositoriesList, error) {
+func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *GitLabAddRepoModel) (*v2Models.GitlabRepositoriesList, error) {
f := logrus.Fields{
- "functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "organizationName": input.GitlabOrganizationName,
- "claGroupID": input.ClaGroupID,
- "groupFullPath": input.OrganizationFullPath,
- "groupID": input.OrganizationExternalID,
+ "functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "projectSFID": projectSFID,
+ "groupName": input.GroupName,
+ "claGroupID": input.ClaGroupID,
+ "groupFullPath": input.GroupFullPath,
+ "groupID": input.ExternalID,
}
var gitLabOrgModel *common.GitLabOrganization
var getOrgErr error
- if input.GitlabOrganizationName != "" {
- log.WithFields(f).Debugf("fetching GitLab organization by name: %s", input.GitlabOrganizationName)
- gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GitlabOrganizationName)
+ if input.GroupName != "" {
+ log.WithFields(f).Debugf("fetching GitLab group/organization by name: %s", input.GroupName)
+ gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByName(ctx, input.GroupName)
if getOrgErr != nil {
- msg := fmt.Sprintf("problem loading GitLab organization by name: %s, error: %v", input.GitlabOrganizationName, getOrgErr)
+ msg := fmt.Sprintf("problem loading GitLab group/organization by name: %s, error: %v", input.GroupName, getOrgErr)
log.WithFields(f).WithError(getOrgErr).Warn(msg)
return nil, errors.New(msg)
}
- } else if input.OrganizationFullPath != "" {
- log.WithFields(f).Debugf("fetching GitLab organization by full path: %s", input.OrganizationFullPath)
- gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
+ } else if input.GroupFullPath != "" {
+ log.WithFields(f).Debugf("fetching GitLab group/organization by full path: %s", input.GroupFullPath)
+ gitLabOrgModel, getOrgErr = s.glOrgRepo.GetGitLabOrganizationByFullPath(ctx, input.GroupFullPath)
if getOrgErr != nil {
- msg := fmt.Sprintf("problem loading GitLab organization by full path: %s, error: %v", input.OrganizationFullPath, getOrgErr)
+ msg := fmt.Sprintf("problem loading GitLab group/organization by full path: %s, error: %v", input.GroupFullPath, getOrgErr)
log.WithFields(f).WithError(getOrgErr).Warn(msg)
return nil, errors.New(msg)
}
}
if gitLabOrgModel == nil {
- msg := fmt.Sprintf("problem loading GitLab organization by name '%s' or full path '%s'", input.GitlabOrganizationName, input.OrganizationFullPath)
+ msg := fmt.Sprintf("problem loading GitLab group/organization by name '%s' or full path '%s'", input.GroupName, input.GroupFullPath)
log.WithFields(f).Warn(msg)
return nil, errors.New(msg)
}
@@ -73,10 +74,10 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositoryFullPath string
Error error
}
- addRepoRespChan := make(chan *GitLabAddRepositoryResponse, len(input.RepositoryGitlabIds))
+ addRepoRespChan := make(chan *GitLabAddRepositoryResponse, len(input.ProjectIDList))
// Add each repo - could be a lot of repos, so we run this in a go routine
- for _, gitLabProjectID := range input.RepositoryGitlabIds {
+ for _, gitLabProjectID := range input.ProjectIDList {
go func(gitLabProjectID int) {
log.WithFields(f).Debugf("loading GitLab project from GitLab using projectID: %d...", gitLabProjectID)
project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, gitLabProjectID)
@@ -99,7 +100,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositoryExternalID: repositoryExternalIDString,
RepositoryName: project.PathWithNamespace, // Name column is actually the full path for both GitHub and GitLab
RepositoryURL: project.WebURL,
- RepositoryOrganizationName: input.GitlabOrganizationName,
+ RepositoryOrganizationName: input.GroupName,
RepositoryCLAGroupID: input.ClaGroupID,
RepositoryType: utils.GitLabLower, // should always be gitlab
Enabled: false, // we don't enable by default
@@ -135,7 +136,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
// Wait for the go routines to finish and load up the results
log.WithFields(f).Debug("waiting for add repos to finish...")
var lastErr error
- for range input.RepositoryGitlabIds {
+ for range input.ProjectIDList {
select {
case response := <-addRepoRespChan:
if response.Error != nil {
@@ -195,12 +196,12 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
}
// Build input to the add function
- input := &v2Models.GitlabRepositoriesEnable{
- ClaGroupID: projectCLAGroupModel.ClaGroupID,
- GitlabOrganizationName: gitLabOrgModel.OrganizationName,
- OrganizationExternalID: int64(gitLabOrgModel.ExternalGroupID),
- OrganizationFullPath: gitLabOrgModel.OrganizationFullPath,
- RepositoryGitlabIds: listProjectIDs,
+ input := &GitLabAddRepoModel{
+ ClaGroupID: projectCLAGroupModel.ClaGroupID,
+ GroupName: gitLabOrgModel.OrganizationName,
+ GroupFullPath: gitLabOrgModel.OrganizationFullPath,
+ ExternalID: int64(gitLabOrgModel.ExternalGroupID),
+ ProjectIDList: listProjectIDs,
}
log.WithFields(f).Debugf("adding %d GitLab repositories", len(listProjectIDs))
_, addRepoErr := s.GitLabAddRepositories(ctx, gitLabOrgModel.ProjectSFID, input)
@@ -262,6 +263,11 @@ func (s *Service) GitLabGetRepositoriesByProjectSFID(ctx context.Context, projec
return nil, err
}
+ // sort result by name
+ sort.Slice(responses, func(i, j int) bool {
+ return responses[i].RepositoryName < responses[j].RepositoryName
+ })
+
return &v2Models.GitlabRepositoriesList{
List: responses,
}, nil
@@ -307,11 +313,13 @@ func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, o
}, nil
}
-// GitLabEnableRepositories assigns repos to
-func (s *Service) GitLabEnableRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64) error {
+// GitLabEnrollRepositories assigns repos to a CLA Group
+func (s *Service) GitLabEnrollRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64, enrollValue bool) error {
f := logrus.Fields{
- "functionName": "v2.repositories.gitlab_services.GitLabEnableRepositories",
+ "functionName": "v2.repositories.gitlab_services.GitLabEnrollRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "claGroupID": claGroupID,
+ "enrollValue": enrollValue,
}
type GitLabUpdateRepositoryResponse struct {
@@ -322,7 +330,7 @@ func (s *Service) GitLabEnableRepositories(ctx context.Context, claGroupID strin
for _, repoID := range repositoryIDList {
go func(claGroupID string, repoID int64) {
- updateErr := s.GitLabEnableRepository(ctx, claGroupID, repoID)
+ updateErr := s.GitLabEnrollRepository(ctx, claGroupID, repoID, enrollValue)
updateRepoChan <- &GitLabUpdateRepositoryResponse{
RepositoryID: repoID,
Error: updateErr,
@@ -351,19 +359,19 @@ func (s *Service) GitLabEnableRepositories(ctx context.Context, claGroupID strin
return lastErr
}
-// GitLabEnableRepository service function
-func (s *Service) GitLabEnableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
- return s.gitV2Repository.GitLabEnableRepositoryByID(ctx, claGroupID, repositoryExternalID)
+// GitLabEnrollRepository service function enrolls a single GitLab repository to the specified CLA Group
+func (s *Service) GitLabEnrollRepository(ctx context.Context, claGroupID string, repositoryExternalID int64, enrollValue bool) error {
+ return s.gitV2Repository.GitLabEnrollRepositoryByID(ctx, claGroupID, repositoryExternalID, enrollValue)
}
-// GitLabDisableRepository service function
-func (s *Service) GitLabDisableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
- return s.gitV2Repository.GitLabDisableRepositoryByID(ctx, claGroupID, repositoryExternalID)
+// GitLabEnrollCLAGroupRepositories service function
+func (s *Service) GitLabEnrollCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error {
+ return s.gitV2Repository.GitLabEnableCLAGroupRepositories(ctx, claGroupID, enrollValue)
}
-// GitLabDisableCLAGroupRepositories service function
-func (s *Service) GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
- return s.gitV2Repository.GitLabDisableCLAGroupRepositories(ctx, claGroupID)
+// GitLabDeleteRepositories deletes the repositories under the specified path
+func (s *Service) GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error {
+ return s.gitV2Repository.GitLabDeleteRepositories(ctx, gitLabGroupPath)
}
// dbModelToGitLabRepository converts the database model to a v2 response model
diff --git a/cla-backend-go/v2/repositories/handlers.go b/cla-backend-go/v2/repositories/handlers.go
index 9c66a9e2f..c547cab27 100644
--- a/cla-backend-go/v2/repositories/handlers.go
+++ b/cla-backend-go/v2/repositories/handlers.go
@@ -456,28 +456,25 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_repositories.NewGetProjectGitLabRepositoriesOK().WithPayload(response)
})
- api.GitlabRepositoriesEnableGitLabRepositoryHandler = gitlab_repositories.EnableGitLabRepositoryHandlerFunc(
- func(params gitlab_repositories.EnableGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
+ api.GitlabRepositoriesEnrollGitLabRepositoryHandler = gitlab_repositories.EnrollGitLabRepositoryHandlerFunc(
+ func(params gitlab_repositories.EnrollGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
f := logrus.Fields{
- "functionName": "v2.repositories.handlers.GitlabRepositoriesEnableGitLabRepositoryHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
- "projectSFID": params.ProjectSFID,
- "organizationName": params.GitlabRepositoriesEnable.GitlabOrganizationName,
- "claGroupID": params.GitlabRepositoriesEnable.ClaGroupID,
- "groupFullPath": params.GitlabRepositoriesEnable.OrganizationFullPath,
- "groupID": params.GitlabRepositoriesEnable.OrganizationExternalID,
+ "functionName": "v2.repositories.handlers.GitlabRepositoriesEnableGitLabRepositoryHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "claGroupID": params.GitlabRepositoriesEnroll.ClaGroupID,
}
// Load the project
psc := project_service.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
- return gitlab_repositories.NewEnableGitLabRepositoryNotFound().WithPayload(
+ return gitlab_repositories.NewEnrollGitLabRepositoryNotFound().WithPayload(
utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
@@ -485,91 +482,46 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
msg := fmt.Sprintf("user %s does not have access to Add GitLab Repositories for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
log.WithFields(f).Debug(msg)
- return gitlab_repositories.NewEnableGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
- }
-
- if len(params.GitlabRepositoriesEnable.RepositoryGitlabIds) == 0 {
- msg := "missing repository GitLab ID values"
- return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
+ return gitlab_repositories.NewEnrollGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
}
- log.WithFields(f).Debugf("assigning GitLab repository for project: %s and CLA Group: %s", params.ProjectSFID, params.GitlabRepositoriesEnable.ClaGroupID)
- enableErr := service.GitLabEnableRepositories(ctx, params.GitlabRepositoriesEnable.ClaGroupID, params.GitlabRepositoriesEnable.RepositoryGitlabIds)
- if enableErr != nil {
- msg := fmt.Sprintf("problem adding GitLab repositories for projectSFID: %s", params.ProjectSFID)
- log.WithFields(f).WithError(enableErr).Warn(msg)
- return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, enableErr))
- }
-
- repoList, getErr := service.GitLabGetRepositoriesByProjectSFID(ctx, params.ProjectSFID)
- if getErr != nil {
- msg := fmt.Sprintf("problem fetching GitLab repositories for projectSFID: %s", params.ProjectSFID)
- log.WithFields(f).WithError(getErr).Warn(msg)
- return gitlab_repositories.NewEnableGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, getErr))
+ if len(params.GitlabRepositoriesEnroll.Enroll) == 0 && len(params.GitlabRepositoriesEnroll.Unenroll) == 0 {
+ msg := "missing GitLab ID values for enroll or unenroll - should have at least one value in either list"
+ return gitlab_repositories.NewEnrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- return gitlab_repositories.NewEnableGitLabRepositoryOK().WithPayload(repoList)
- })
-
- api.GitlabRepositoriesUnenrollGitLabRepositoryHandler = gitlab_repositories.UnenrollGitLabRepositoryHandlerFunc(
- func(params gitlab_repositories.UnenrollGitLabRepositoryParams, authUser *auth.User) middleware.Responder {
- reqID := utils.GetRequestID(params.XREQUESTID)
- utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
- ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
- f := logrus.Fields{
- "functionName": "v2.repositories.handlers.GitlabRepositoriesDeleteProjectGitLabRepositoryHandler",
- utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "authUser": authUser.UserName,
- "authEmail": authUser.Email,
- "projectSFID": params.ProjectSFID,
- "repositoryExternalID": params.RepositoryExternalID,
+ if duplicates := utils.FindInt64Duplicates(params.GitlabRepositoriesEnroll.Enroll, params.GitlabRepositoriesEnroll.Unenroll); len(duplicates) > 0 {
+ msg := fmt.Sprintf("found duplicate entries in both enroll and unenroll: %+v", duplicates)
+ return gitlab_repositories.NewEnrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- // Load the project
- psc := project_service.GetClient()
- projectModel, err := psc.GetProject(params.ProjectSFID)
- if err != nil || projectModel == nil {
- return gitlab_repositories.NewUnenrollGitLabRepositoryNotFound().WithPayload(
- utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
- }
-
- if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
- msg := fmt.Sprintf("user %s does not have access to Unenroll Gitlab Repositories with Project scope of %s",
- authUser.UserName, params.ProjectSFID)
- log.WithFields(f).Debug(msg)
- return gitlab_repositories.NewUnenrollGitLabRepositoryForbidden().WithXRequestID(reqID).WithPayload(utils.ErrorResponseForbidden(reqID, msg))
+ if len(params.GitlabRepositoriesEnroll.Enroll) > 0 {
+ log.WithFields(f).Debugf("enrolling GitLab repository for project: %s and CLA Group: %s", params.ProjectSFID, params.GitlabRepositoriesEnroll.ClaGroupID)
+ enableErr := service.GitLabEnrollRepositories(ctx, params.GitlabRepositoriesEnroll.ClaGroupID, params.GitlabRepositoriesEnroll.Enroll, true)
+ if enableErr != nil {
+ msg := fmt.Sprintf("problem enrolling GitLab repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(enableErr).Warn(msg)
+ return gitlab_repositories.NewEnrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, enableErr))
+ }
}
- ghRepo, err := service.GitLabGetRepositoryByExternalID(ctx, params.RepositoryExternalID)
- if err != nil {
- if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
- msg := fmt.Sprintf("repository not found for repository external ID: %d", params.RepositoryExternalID)
- log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewUnenrollGitLabRepositoryNotFound().WithXRequestID(reqID).WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ if len(params.GitlabRepositoriesEnroll.Unenroll) > 0 {
+ log.WithFields(f).Debugf("unenrolling GitLab repository for project: %s and CLA Group: %s", params.ProjectSFID, params.GitlabRepositoriesEnroll.ClaGroupID)
+ enableErr := service.GitLabEnrollRepositories(ctx, params.GitlabRepositoriesEnroll.ClaGroupID, params.GitlabRepositoriesEnroll.Unenroll, false)
+ if enableErr != nil {
+ msg := fmt.Sprintf("problem unenrolling GitLab repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(enableErr).Warn(msg)
+ return gitlab_repositories.NewEnrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, enableErr))
}
-
- msg := fmt.Sprintf("problem looking up repository using the repostiory external ID: %d", params.RepositoryExternalID)
- log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewUnenrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- err = service.GitLabDisableRepository(ctx, "", params.RepositoryExternalID)
- if err != nil {
- msg := fmt.Sprintf("problem disabling repository for projectSFID: %s, error: %+v", params.ProjectSFID, err)
- log.WithFields(f).WithError(err).Warn(msg)
- return gitlab_repositories.NewUnenrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ repoList, getErr := service.GitLabGetRepositoriesByProjectSFID(ctx, params.ProjectSFID)
+ if getErr != nil {
+ msg := fmt.Sprintf("problem fetching GitLab repositories for projectSFID: %s", params.ProjectSFID)
+ log.WithFields(f).WithError(getErr).Warn(msg)
+ return gitlab_repositories.NewEnrollGitLabRepositoryBadRequest().WithXRequestID(reqID).WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, getErr))
}
- eventService.LogEventWithContext(ctx, &events.LogEventArgs{
- EventType: events.RepositoryDisabled,
- ProjectSFID: params.ProjectSFID,
- LfUsername: authUser.UserName,
- EventData: &events.RepositoryDisabledEventData{
- RepositoryName: ghRepo.RepositoryName,
- RepositoryExternalID: ghRepo.RepositoryExternalID,
- },
- })
-
- return gitlab_repositories.NewUnenrollGitLabRepositoryNoContent().WithXRequestID(reqID)
+ return gitlab_repositories.NewEnrollGitLabRepositoryOK().WithPayload(repoList)
})
}
diff --git a/cla-backend-go/v2/repositories/models.go b/cla-backend-go/v2/repositories/models.go
new file mode 100644
index 000000000..d25c6a5c0
--- /dev/null
+++ b/cla-backend-go/v2/repositories/models.go
@@ -0,0 +1,13 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package repositories
+
+// GitLabAddRepoModel data model for GitLab add repository
+type GitLabAddRepoModel struct {
+ ClaGroupID string
+ GroupName string
+ ExternalID int64
+ GroupFullPath string
+ ProjectIDList []int64
+}
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index c5afe0ca5..ccbf1485d 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -31,11 +31,12 @@ type RepositoryInterface interface {
GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error)
GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error)
+ GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) ([]*repoModels.RepositoryDBModel, error)
GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*repoModels.RepositoryDBModel, error)
GitLabAddRepository(ctx context.Context, projectSFID string, input *repoModels.RepositoryDBModel) (*repoModels.RepositoryDBModel, error)
- GitLabEnableRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64) error
- GitLabDisableRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64) error
- GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
+ GitLabEnrollRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64, enrollValue bool) error
+ GitLabEnableCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error
+ GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error
}
// Repository object/struct
@@ -121,6 +122,31 @@ func (r *Repository) GitLabGetRepositoryByName(ctx context.Context, repositoryNa
return record, nil
}
+// GitLabGetRepositoriesByNamePrefix returns a list of repositories matching the specified name prefix
+func (r *Repository) GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
+ filter := expression.Name(repoModels.RepositoryNameColumn).BeginsWith(repositoryNamePrefix)
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryTypeIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ RepositoryName: repositoryNamePrefix,
+ }
+ }
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabDuplicateRepositoriesFound); ok {
+ return nil, &utils.GitLabDuplicateRepositoriesFound{
+ RepositoryName: repositoryNamePrefix,
+ }
+ }
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
// GitLabGetRepositoryByExternalID returns the database model for the specified repository by external ID
func (r *Repository) GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*repoModels.RepositoryDBModel, error) {
str := strconv.FormatInt(repositoryExternalID, 10)
@@ -312,18 +338,13 @@ func (r *Repository) GitLabAddRepository(ctx context.Context, projectSFID string
return input, nil
}
-// GitLabEnableRepositoryByID enables the specified repository
-func (r *Repository) GitLabEnableRepositoryByID(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
- return r.setRepositoryEnabledValue(ctx, claGroupID, repositoryExternalID, true)
-}
-
-// GitLabDisableRepositoryByID disables the specified repository
-func (r *Repository) GitLabDisableRepositoryByID(ctx context.Context, claGroupID string, repositoryExternalID int64) error {
- return r.setRepositoryEnabledValue(ctx, claGroupID, repositoryExternalID, false)
+// GitLabEnrollRepositoryByID enables the specified repository
+func (r *Repository) GitLabEnrollRepositoryByID(ctx context.Context, claGroupID string, repositoryExternalID int64, enrollValue bool) error {
+ return r.setRepositoryEnabledValue(ctx, claGroupID, repositoryExternalID, enrollValue)
}
// GitLabEnableCLAGroupRepositories enables the specified CLA Group repositories
-func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
+func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error {
repositories, err := r.GitHubGetRepositoriesByCLAGroup(ctx, claGroupID)
if err != nil {
return err
@@ -335,7 +356,7 @@ func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGr
return parseErr
}
- enableErr := r.GitLabEnableRepositoryByID(ctx, claGroupID, int64I)
+ enableErr := r.GitLabEnrollRepositoryByID(ctx, claGroupID, int64I, enrollValue)
if enableErr != nil {
return enableErr
}
@@ -344,26 +365,74 @@ func (r *Repository) GitLabEnableCLAGroupRepositories(ctx context.Context, claGr
return nil
}
-// GitLabDisableCLAGroupRepositories disables the GitLab repositories by the specified CLA Group
-func (r *Repository) GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error {
- repositories, err := r.GitHubGetRepositoriesByCLAGroup(ctx, claGroupID)
+// GitLabDeleteRepositories deletes the specified repositories under the GitLap group path
+func (r *Repository) GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repository.GitLabDeleteRepositories",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabGroupPath": gitLabGroupPath,
+ }
+
+ log.WithFields(f).Debugf("loading repositories with name prefix: %s", gitLabGroupPath)
+ repositories, err := r.GitLabGetRepositoriesByNamePrefix(ctx, gitLabGroupPath)
if err != nil {
+ // If nothing to delete...
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil
+ }
+ log.WithFields(f).WithError(err).Warnf("problem loading repositories with name prefix: %s", gitLabGroupPath)
return err
}
+ log.WithFields(f).Debugf("processing repository delete request for %d repositories", len(repositories))
- for _, repo := range repositories {
- int64I, parseErr := strconv.ParseInt(repo.RepositoryExternalID, 10, 64)
- if parseErr != nil {
- return parseErr
- }
+ type GitLabDeleteRepositoryResponse struct {
+ RepositoryID string
+ RepositoryName string
+ RepositoryFullPath string
+ Error error
+ }
+ deleteRepoRespChan := make(chan *GitLabDeleteRepositoryResponse, len(repositories))
- enableErr := r.GitLabDisableRepositoryByID(ctx, claGroupID, int64I)
- if enableErr != nil {
- return enableErr
+ for _, repo := range repositories {
+ go func(repo *repoModels.RepositoryDBModel) {
+ _, err = r.dynamoDBClient.DeleteItem(&dynamodb.DeleteItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ repoModels.RepositoryIDColumn: {S: aws.String(repo.RepositoryID)},
+ },
+ TableName: aws.String(r.repositoryTableName),
+ })
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("error deleting repository with ID:%s", repo.RepositoryID)
+ }
+ deleteRepoRespChan <- &GitLabDeleteRepositoryResponse{
+ RepositoryID: repo.RepositoryID,
+ RepositoryName: repo.RepositoryName,
+ RepositoryFullPath: repo.RepositoryFullPath,
+ Error: err,
+ }
+ }(repo)
+ }
+
+ // Wait for the go routines to finish and load up the results
+ log.WithFields(f).Debug("waiting for delete repos to finish...")
+ var lastErr error
+ for range repositories {
+ select {
+ case response := <-deleteRepoRespChan:
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warn(response.Error.Error())
+ lastErr = response.Error
+ } else {
+ log.WithFields(f).Debugf("delete repo: %s with ID: %s with full path: %s", response.RepositoryName, response.RepositoryID, response.RepositoryFullPath)
+ }
+ case <-ctx.Done():
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for delete repositories timed out")
+ lastErr = fmt.Errorf("delete repositories failed with timeout, error: %v", ctx.Err())
}
}
- return nil
+ // Return the last error, hopefully nil if no error occurred...
+ return lastErr
}
// getRepositoryWithConditionFilter fetches the repository entry based on the specified condition and filter criteria using the provided index
@@ -397,7 +466,6 @@ func (r *Repository) getRepositoryWithConditionFilter(ctx context.Context, condi
}
if len(results.Items) == 0 {
- log.WithFields(f).Debugf("no repositories found matching filter critera: %+v", queryInput)
// Generic - no details as we don't know what filter content was provided
return nil, &utils.GitLabRepositoryNotFound{}
}
@@ -493,6 +561,11 @@ func (r *Repository) setRepositoryEnabledValue(ctx context.Context, claGroupID s
existingNote = strings.TrimSpace(existingModel.Note) + " "
}
}
+ userNameFromCtx := utils.GetUserNameFromContext(ctx)
+ byUserStr := ""
+ if userNameFromCtx != "" {
+ byUserStr = fmt.Sprintf("by user: %s", userNameFromCtx)
+ }
_, now := utils.CurrentTime()
updateInput := &dynamodb.UpdateItemInput{
@@ -506,7 +579,7 @@ func (r *Repository) setRepositoryEnabledValue(ctx context.Context, claGroupID s
BOOL: aws.Bool(enabledValue),
},
":noteValue": {
- S: aws.String(fmt.Sprintf("%s Updated enabled flag to %t on %s.", existingNote, enabledValue, now)),
+ S: aws.String(fmt.Sprintf("%s Updated enabled flag to %t on %s %s.", existingNote, enabledValue, now, byUserStr)),
},
":dateModifiedValue": {
S: aws.String(now),
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index 117ee5277..c303177b7 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -59,12 +59,12 @@ type ServiceInterface interface {
GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error)
- GitLabAddRepositories(ctx context.Context, projectSFID string, input *v2Models.GitlabRepositoriesEnable) (*v2Models.GitlabRepositoriesList, error)
+ GitLabAddRepositories(ctx context.Context, projectSFID string, input *GitLabAddRepoModel) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
- GitLabEnableRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64) error
- GitLabEnableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error
- GitLabDisableRepository(ctx context.Context, claGroupID string, repositoryExternalID int64) error
- GitLabDisableCLAGroupRepositories(ctx context.Context, claGroupID string) error
+ GitLabEnrollRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64, enrollValue bool) error
+ GitLabEnrollRepository(ctx context.Context, claGroupID string, repositoryExternalID int64, enrollValue bool) error
+ GitLabEnrollCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error
+ GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error
}
// GithubOrgRepo redefine the interface here to avoid circular dependency issues
@@ -77,7 +77,7 @@ type GithubOrgRepo interface {
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- DeleteGitLabOrganization(ctx context.Context, projectSFID, gitlabOrgName string) error
+ DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgName string) error
}
// Service is the service model/structure
From 6f8c4c38d23ff5cdeb7eaef744942465bb21c513 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 24 Aug 2021 20:28:41 -0700
Subject: [PATCH 0458/1276] Added Repository Type Index Lambda Permissions
(#3195)
---
cla-backend-go/serverless.yml | 1 +
cla-backend/serverless.yml | 1 +
2 files changed, 2 insertions(+)
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index cb5c744ee..9808f2d07 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -176,6 +176,7 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/sfdc-repository-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/project-sfid-repository-organization-name-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/project-sfid-repository-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/repository-type-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/github-org-sfid-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/project-sfid-organization-name-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/organization-name-lower-search-index"
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index 74873fe0b..53adfde2b 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -267,6 +267,7 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/sfdc-repository-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/project-sfid-repository-organization-name-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/project-sfid-repository-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-repositories/index/repository-type-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/github-org-sfid-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/project-sfid-organization-name-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-github-orgs/index/organization-name-lower-search-index"
From eb893d4c892cc74f4211e3dc599ba2d333a15861 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 25 Aug 2021 18:28:15 +0300
Subject: [PATCH 0459/1276] Feature/gitlab webhook secret check (#3196)
---
cla-backend-go/go.sum | 149 ++++++++++++++++++
cla-backend-go/swagger/cla.v2.yaml | 9 +-
cla-backend-go/v2/gitlab-activity/handlers.go | 12 +-
cla-backend-go/v2/gitlab-activity/service.go | 10 +-
.../v2/gitlab_organizations/service.go | 7 +
5 files changed, 182 insertions(+), 5 deletions(-)
diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum
index 7a0dc864a..944fa3c75 100644
--- a/cla-backend-go/go.sum
+++ b/cla-backend-go/go.sum
@@ -17,28 +17,38 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM=
github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2 h1:ZLAgTj9+H3RTmjbRpUamMO8SWS1m4ZKJGGeh9lT985U=
github.com/LF-Engineering/aws-lambda-go-api-proxy v0.3.2/go.mod h1:LQj48zwkRwdjVmDCqtPlviW/7IFaSKzz2gDhxRwVrA4=
@@ -46,17 +56,23 @@ github.com/LF-Engineering/lfx-kit v0.1.25 h1:Bb3Snc72ppBmbS5CMoLBGFg1Tt7ZhZktZLJ
github.com/LF-Engineering/lfx-kit v0.1.25/go.mod h1:B+pko2SqvGNSG9hWDC35JNZ38nTPt+r5KB6k75xM5vY=
github.com/LF-Engineering/lfx-models v0.6.44 h1:a4/6+Hc05caUCzd9eQnZIioZUhWxtgpfgVRuf/M2SRY=
github.com/LF-Engineering/lfx-models v0.6.44/go.mod h1:AaV7psgE2IPXhaLXYXoFviobYoh09XJ2P/ALOU11OuE=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -70,32 +86,46 @@ github.com/aws/aws-sdk-go v1.36.27 h1:wc3xLJJHog2SwiqlLnrLUuct/n+dBjB45QhuZw2psV
github.com/aws/aws-sdk-go v1.36.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bketelsen/crypt v0.0.4 h1:w/jqZtC9YD4DS/Vp9GhWfWcCpuAL58oTnLoI8vE9YHU=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.1.0/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@@ -107,13 +137,17 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fnproject/fdk-go v0.0.2 h1:nebofQYAY8SbcjqmoaBo6KLNTwUrJq6lGdi7RCbq/EA=
github.com/fnproject/fdk-go v0.0.2/go.mod h1:9m+nEyku9SqJAVJQsfZOZBQzFkCs+jvmbZJhvgDX4ts=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -122,10 +156,14 @@ github.com/gin-gonic/gin v0.0.0-20180126034611-783c7ee9c14e/go.mod h1:7cKuhb5qV2
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a h1:l4yNPeA/3kNJwE0uDBVXtFX8hfiHrlqkXBLPOrchWzk=
github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -206,38 +244,55 @@ github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd h1:hSkbZ9XSyjyBirMeqSqUrK+9HboWrweVlzRNqoBi2d4=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0 h1:31atYa/UW9V5q8vMJ+W6wd64OaaTHUrCUXER358zLM4=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3 h1:3GQ53z7E3o00C/yy7Ko8VXqQXoJGLkrTQCLTF1EjoXU=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1 h1:iQ0D6SpNXIxu52WESsD+KoQ7af2e3nCfnSBoSF/hKe0=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211 h1:mSVZ4vj4khv+oThUfS+SQU3UuFIZ5Zo6UNcvK8E8Mz8=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1 h1:dLg+zb+uOyd/mKeQUYIbwbNmfRsr9hd/WtYWepmayhI=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2 h1:fq9WcL1BYrm36SzK6+aAnZ8hcp+SrmnDyAxhNx8dvJk=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0 h1:Ir9W9XIm9j7bhhkKE9cokvtTl1vBm62A/fene/ZCj6A=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/dep v0.5.4 h1:WfV5qbGwsBNUDhk+pfI6emWm7SdDFsnSWkqCMNG3BRs=
github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -267,8 +322,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -289,9 +346,12 @@ github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3
github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -303,8 +363,11 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -313,41 +376,61 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979 h1:UsXWMy9j+GSCN/I1/Oyc4wGaeW2CDYqeqAkEvWPu+cs=
github.com/gorilla/mux v0.0.0-20180120075819-c0091a029979/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ=
@@ -355,6 +438,7 @@ github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94=
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
@@ -364,6 +448,7 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
+github.com/jmank88/nuts v0.4.0 h1:3rHp+7YcvtkTPohGBA++MwneB9OlX/rpORvleiRivMQ=
github.com/jmank88/nuts v0.4.0/go.mod h1:TKOSbm0p73pdAzgQ7lcZheG2cinZiXqy60KM5ooL3j8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -371,12 +456,14 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v0.0.0-20180128142709-bca911dae073/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -384,25 +471,35 @@ github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f h1:a3Vd00a20dTKLpyS2h
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f/go.mod h1:+7K7MqWi5xWI+s1LyB2g0Di71jZo27y+XOlmhNtV1Y0=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2 h1:McU3wXjBrKfJcOt2Pali5qEir9NLrqOh4EECzdWHknM=
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2/go.mod h1:3mJ64RiWU2x9U6IigvcoVLra6LZQTOwMuHpk02OtOJc=
+github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
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/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
+github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
@@ -417,9 +514,12 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -428,11 +528,17 @@ github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1y
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -445,17 +551,21 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9rNM=
github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd h1:b2wg8HW/u55DT7Y/vamdEn/jdvtsGkxzl+0+iHa5YmE=
github.com/onsi/ginkgo v0.0.0-20180119174237-747514b53ddd/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.3.0 h1:yPHEatyQC4jN3vdfvqJXG7O9vfC6LhaAV1NEdYpP+h0=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc h1:JI2yIEkVFpe4eYIM/fTNtlIayTiGj4m+iku5JLx8uOY=
github.com/pdfcpu/pdfcpu v0.3.5-0.20200802160406-be1e0eb55afc/go.mod h1:3wwz3xi60q88WM0kKZeOJvdQ4YgW4Og7whEiodseWs8=
@@ -467,27 +577,37 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
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/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429 h1:W/FQ2o7cG+X0Wkb8NefNCTRDEodfo6MtfH9BaO8ncMA=
github.com/savaki/dynastore v0.0.0-20171109173440-28d8558bb429/go.mod h1:fK0DIsn9VGLYVur3nQ54Yz4LSLLCyDil0gzq5Y8Yzls=
+github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62Wd/ZOea4XYqE+y1sf2pdm+VItsc0c=
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU=
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@@ -514,6 +634,7 @@ github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -535,23 +656,33 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
+github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862 h1:eg5xqGZGatsyRpVnFJkdeUWSFk46lDgkXLvOryv5ySg=
github.com/urfave/negroni v0.0.0-20180130044549-22c5532ea862/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ=
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA=
github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ=
github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@@ -564,12 +695,15 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -595,6 +729,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -613,8 +748,10 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -624,6 +761,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -752,6 +890,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -826,6 +965,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -853,6 +993,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -903,6 +1044,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -923,6 +1065,7 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -942,7 +1085,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -965,7 +1110,11 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
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/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 8809d7dde..ba88beaac 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3947,8 +3947,7 @@ paths:
operationId: gitlabActivity
parameters:
- $ref: "#/parameters/x-request-id"
- - $ref: "#/parameters/x-github-event"
- - $ref: "#/parameters/x-hub-signature"
+ - $ref: "#/parameters/x-gitlab-token"
- name: gitlabActivityInput
in: body
schema:
@@ -4329,6 +4328,12 @@ parameters:
description: Github event signature which is used for validation of the request body
in: header
type: string
+ x-gitlab-token:
+ name: X-Gitlab-Token
+ description: Gitlab webhook secret token sent for futher verification
+ in: header
+ type: string
+ required: true
definitions:
# Common definitions
diff --git a/cla-backend-go/v2/gitlab-activity/handlers.go b/cla-backend-go/v2/gitlab-activity/handlers.go
index d2dd835ed..e65d3be08 100644
--- a/cla-backend-go/v2/gitlab-activity/handlers.go
+++ b/cla-backend-go/v2/gitlab-activity/handlers.go
@@ -5,6 +5,7 @@ package gitlab_activity
import (
"context"
+ "errors"
"fmt"
"github.com/communitybridge/easycla/cla-backend-go/events"
@@ -30,6 +31,11 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
log.WithFields(f).Debugf("handling gitlab activity callback")
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID)
+ if params.XGitlabToken == ""{
+ return gitlab_activity.NewGitlabActivityUnauthorized().WithPayload(
+ utils.ErrorResponseUnauthorized(reqID, "missing webhook secret token"))
+ }
+
jsonData, err := params.GitlabActivityInput.MarshalJSON()
if err != nil {
msg := fmt.Sprintf("unmarshall event data failed : %v", err)
@@ -61,10 +67,14 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
utils.ErrorResponseBadRequest(reqID, msg))
}
- err = service.ProcessMergeOpenedActivity(ctx, mergeEvent)
+ err = service.ProcessMergeOpenedActivity(ctx, params.XGitlabToken, mergeEvent)
if err != nil {
msg := fmt.Sprintf("processing gitlab merge event failed : %v", err)
log.WithFields(f).Errorf(msg)
+ if errors.Is(err, secretTokenMismatch){
+ return gitlab_activity.NewGitlabActivityUnauthorized().WithPayload(
+ utils.ErrorResponseUnauthorized(reqID, msg))
+ }
return gitlab_activity.NewGitlabActivityInternalServerError().WithPayload(
utils.ErrorResponseBadRequest(reqID, msg))
}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index 103300a65..ed0c9e26d 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -38,6 +38,7 @@ var (
missingID = errors.New("user missing in easyCLA records")
missingCompanyAffiliation = errors.New("must confirm affiliation with their company")
missingCompanyApproval = errors.New("missing in company approval lists")
+ secretTokenMismatch = errors.New("secret token mismatch")
)
type gatedGitlabUser struct {
@@ -46,7 +47,7 @@ type gatedGitlabUser struct {
}
type Service interface {
- ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *gitlab.MergeEvent) error
+ ProcessMergeOpenedActivity(ctx context.Context, secretToken string, mergeEvent *gitlab.MergeEvent) error
}
type service struct {
@@ -76,7 +77,7 @@ func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRe
}
}
-func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *gitlab.MergeEvent) error {
+func (s service) ProcessMergeOpenedActivity(ctx context.Context, secretToken string, mergeEvent *gitlab.MergeEvent) error {
projectName := mergeEvent.Project.Name
projectID := mergeEvent.Project.ID
mergeID := mergeEvent.ObjectAttributes.IID
@@ -98,6 +99,11 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, mergeEvent *git
return fmt.Errorf("fetching internal gitlab org for following path : %s failed : %v", repositoryPath, err)
}
+ log.WithFields(f).Debugf("checking gitlab org : %s auth state agains the webhook secret token", gitlabOrg.OrganizationName)
+ if gitlabOrg.AuthState != secretToken {
+ return secretTokenMismatch
+ }
+
log.WithFields(f).Debugf("internal gitlab org : %s:%s is associated with external path : %s", gitlabOrg.OrganizationID, gitlabOrg.OrganizationName, repositoryPath)
gitlabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, s.gitLabApp)
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index d4c81331d..75836c960 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -201,6 +201,9 @@ func (s *Service) GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrg
return nil, err
}
+ if dbModel == nil {
+ return nil, nil
+ }
return common.ToModel(dbModel), nil
}
@@ -218,6 +221,10 @@ func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGrou
return nil, err
}
+ if dbModel == nil {
+ return nil, nil
+ }
+
return common.ToModel(dbModel), nil
}
From 49b3afff34c9fa818dc1f0247ce24f0e9c92acdb Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Wed, 25 Aug 2021 08:43:18 -0700
Subject: [PATCH 0460/1276] Feature/Gitlab Docusign Flow (#3197)
Co-authored-by: Harold Wanyama
---
cla-backend-go/users/repository.go | 6 +
cla-backend-go/utils/conversion.go | 10 +
cla-backend/cla/config.py | 1 +
cla-backend/cla/controllers/signing.py | 13 ++
cla-backend/cla/models/docusign_models.py | 217 ++++++++++++++++++++++
cla-backend/cla/routes.py | 19 +-
6 files changed, 265 insertions(+), 1 deletion(-)
diff --git a/cla-backend-go/users/repository.go b/cla-backend-go/users/repository.go
index 484878d02..f9b120fa9 100644
--- a/cla-backend-go/users/repository.go
+++ b/cla-backend-go/users/repository.go
@@ -123,6 +123,12 @@ func (repo repository) CreateUser(user *models.User) (*models.User, error) {
}
}
+ if len(user.Emails) > 0 {
+ attributes["user_emails"] = &dynamodb.AttributeValue{
+ SS: utils.ArrayStringPointer(user.Emails),
+ }
+ }
+
if user.LfUsername != "" {
attributes["lf_username"] = &dynamodb.AttributeValue{
S: aws.String(user.LfUsername),
diff --git a/cla-backend-go/utils/conversion.go b/cla-backend-go/utils/conversion.go
index 973251cc1..f98ab0c7e 100644
--- a/cla-backend-go/utils/conversion.go
+++ b/cla-backend-go/utils/conversion.go
@@ -50,3 +50,13 @@ func GetNilSliceIfEmpty(slice []string) []string {
return slice
}
+
+// ArrayStringPointer converts Array string to Array string pointer
+func ArrayStringPointer(input []string) []*string {
+ var conversion []*string
+ for _, v := range input {
+ v2 := v
+ conversion = append(conversion, &v2)
+ }
+ return conversion
+}
diff --git a/cla-backend/cla/config.py b/cla-backend/cla/config.py
index 0a58daeba..8577b98fe 100644
--- a/cla-backend/cla/config.py
+++ b/cla-backend/cla/config.py
@@ -130,6 +130,7 @@ def get_ssm_key(region, key):
# PDF Generation.
PDF_SERVICE = 'DocRaptor'
+
AUTH0_PLATFORM_URL = os.getenv("AUTH0_PLATFORM_URL", "")
AUTH0_PLATFORM_CLIENT_ID = os.getenv("AUTH0_PLATFORM_CLIENT_ID", "")
AUTH0_PLATFORM_CLIENT_SECRET = os.getenv("AUTH0_PLATFORM_CLIENT_SECRET", "")
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index 1b761a184..701e3d82c 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -42,6 +42,8 @@ def request_individual_signature(project_id, user_id, return_url_type, return_ur
primary_user_email = github.get_primary_user_email(request)
return signing_service.request_individual_signature(str(project_id), str(user_id), return_url,
preferred_email=primary_user_email)
+ elif return_url_type is not None and return_url_type.lower() == "gitlab":
+ return signing_service.request_individual_signature_gitlab(str(project_id), str(user_id), return_url)
def request_corporate_signature(auth_user,
@@ -179,6 +181,17 @@ def post_individual_signed(content, installation_id, github_repository_id, chang
"""
get_signing_service().signed_individual_callback(content, installation_id, github_repository_id, change_request_id)
+def post_individual_signed_gitlab(content, user_id):
+ """
+ Handle the posted callback from the signing service after ICLA signature.
+
+ :param content: The POST body from the signing service callback.
+ :type content: string
+ :param user_id: The ID of the user that signed.
+ :type user_id: string
+ """
+ get_signing_service().signed_individual_callback_gitlab(content,user_id)
+
def post_individual_signed_gerrit(content, user_id):
"""
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 83cc5aed9..027488f8b 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -125,6 +125,145 @@ def initialize(self, config):
integrator_key=integrator_key)
self.s3storage = S3Storage()
self.s3storage.initialize(None)
+
+ def request_individual_signature_gitlab(self, project_id, user_id, return_url=None, callback_url=None):
+ request_info = 'project: {project_id}, user: {user_id} with return_url: {return_url}'.format(
+ project_id=project_id, user_id=user_id, return_url=return_url)
+ cla.log.debug('Individual Signature - creating new signature for: {}'.format(request_info))
+
+ # Ensure this is a valid user
+ user_id = str(user_id)
+ try:
+ user = User()
+ user.load(user_id)
+ cla.log.debug('Individual Signature - loaded user name: {}, '
+ 'user email: {}, gh user: {}, gh id: {}'.
+ format(user.get_user_name(), user.get_user_email(), user.get_github_username(),
+ user.get_user_github_id()))
+ except DoesNotExist as err:
+ cla.log.warning('Individual Signature - user ID was NOT found for: {}'.format(request_info))
+ return {'errors': {'user_id': str(err)}}
+
+ # Ensure the project exists
+ try:
+ project = Project()
+ project.load(project_id)
+ cla.log.debug('Individual Signature - loaded project id: {}, name: {}, '.
+ format(project.get_project_id(), project.get_project_name()))
+ except DoesNotExist as err:
+ cla.log.warning('Individual Signature - project ID NOT found for: {}'.format(request_info))
+ return {'errors': {'project_id': str(err)}}
+
+ # Check for active signature object with this project. If the user has
+ # signed the most recent major version, they do not need to sign again.
+ cla.log.debug('Individual Signature - loading latest user signature for user: {}, project: {}'.
+ format(user, project))
+ latest_signature = user.get_latest_signature(str(project_id))
+ cla.log.debug('Individual Signature - loaded latest user signature for user: {}, project: {}'.
+ format(user, project))
+
+ cla.log.debug('Individual Signature - loading latest individual document for project: {}'.
+ format(project))
+ last_document = project.get_latest_individual_document()
+ cla.log.debug('Individual Signature - loaded latest individual document for project: {}'.
+ format(project))
+
+ cla.log.debug('Individual Signature - creating default individual values for user: {}'.format(user))
+ default_cla_values = create_default_individual_values(user)
+ cla.log.debug('Individual Signature - created default individual values: {}'.format(default_cla_values))
+
+ # Generate signature callback url
+ cla.log.debug('Individual Signature - get active signature metadata')
+ signature_metadata = cla.utils.get_active_signature_metadata(user_id)
+ cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
+
+ cla.log.debug('Individual Signature - get individual signature callback url')
+ callback_url = self._generate_individual_signature_callback_url_gitlab(user_id)
+ cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
+
+ if latest_signature is not None and \
+ last_document.get_document_major_version() == latest_signature.get_signature_document_major_version():
+ cla.log.debug('Individual Signature - user already has a signatures with this project: {}'.
+ format(latest_signature.get_signature_id()))
+
+ # Re-generate and set the signing url - this will update the signature record
+ self.populate_sign_url(latest_signature, callback_url, default_values=default_cla_values,
+ preferred_email=user.get_user_email())
+
+ return {'user_id': user_id,
+ 'project_id': project_id,
+ 'signature_id': latest_signature.get_signature_id(),
+ 'sign_url': latest_signature.get_signature_sign_url()}
+ else:
+ cla.log.debug('Individual Signature - user does NOT have a signatures with this project: {}'.
+ format(project))
+
+ # Get signature return URL
+ if return_url is None:
+ return_url = cla.utils.get_active_signature_return_url(user_id, signature_metadata)
+ cla.log.debug('Individual Signature - setting signature return_url to {}'.format(return_url))
+
+ if return_url is None:
+ cla.log.warning('No active signature found for user - cannot generate '
+ 'return_url without knowing where the user came from')
+ return {'user_id': str(user_id),
+ 'project_id': str(project_id),
+ 'signature_id': None,
+ 'sign_url': None,
+ 'error': 'No active signature found for user - cannot generate return_url without knowing where the user came from'}
+
+ # Get latest document
+ try:
+ cla.log.debug('Individual Signature - loading project latest individual document...')
+ document = project.get_latest_individual_document()
+ cla.log.debug('Individual Signature - loaded project latest individual document: {}'.format(document))
+ except DoesNotExist as err:
+ cla.log.warning('Individual Signature - project individual document does NOT exist for: {}'.
+ format(request_info))
+ return {'errors': {'project_id': project_id, 'message': str(err)}}
+
+ # If the CCLA/ICLA template is missing (not created in the project console), we won't have a document
+ # return an error
+ if not document:
+ return {'errors': {'project_id': project_id, 'message': 'missing template document'}}
+
+ # Create new Signature object
+ cla.log.debug('Individual Signature - creating new signature document '
+ 'project_id: {}, user_id: {}, return_url: {}, callback_url: {}'.
+ format(project_id, user_id, return_url, callback_url))
+ signature = Signature(signature_id=str(uuid.uuid4()),
+ signature_project_id=project_id,
+ signature_document_major_version=document.get_document_major_version(),
+ signature_document_minor_version=document.get_document_minor_version(),
+ signature_reference_id=user_id,
+ signature_reference_type='user',
+ signature_reference_name=user.get_user_name(),
+ signature_type='cla',
+ signature_return_url_type='Github',
+ signature_signed=False,
+ signature_approved=True,
+ signature_return_url=return_url,
+ signature_callback_url=callback_url)
+
+ # Set signature ACL
+ cla.log.debug('Individual Signature - setting ACL using user GH id: {}'.format(user.get_user_github_id()))
+ signature.set_signature_acl('github:{}'.format(user.get_user_github_id()))
+
+ # Populate sign url
+ self.populate_sign_url(signature, callback_url, default_values=default_cla_values,
+ preferred_email=user.get_user_email())
+
+ # Save signature
+ signature.save()
+ cla.log.debug('Individual Signature - Saved signature for: {}'.format(request_info))
+
+ response = {'user_id': str(user_id),
+ 'project_id': project_id,
+ 'signature_id': signature.get_signature_id(),
+ 'sign_url': signature.get_signature_sign_url()}
+
+ cla.log.debug('Individual Signature - returning response: {}'.format(response))
+ return response
def request_individual_signature(self, project_id, user_id, return_url=None, callback_url=None,
preferred_email=None):
@@ -846,6 +985,13 @@ def _generate_individual_signature_callback_url_gerrit(self, user_id):
"""
return os.path.join(api_base_url, 'v2/signed/gerrit/individual', str(user_id))
+
+ def _generate_individual_signature_callback_url_gitlab(self, user_id):
+ """
+ Helper function to get a user's active signature callback URL for GitLab
+
+ """
+ return os.path.join(api_base_url, 'v2/signed/gitlab/individual', str(user_id))
def _get_corporate_signature_callback_url(self, project_id, company_id):
"""
@@ -1407,6 +1553,7 @@ def populate_sign_url(self, signature, callback_url=None,
signature.save()
cla.log.debug(f'{fn} - {sig_type} - saved signature to database - id: {signature.get_signature_id()}...')
cla.log.debug(f'populate_sign_url - {sig_type} - complete')
+
def signed_individual_callback(self, content, installation_id, github_repository_id, change_request_id):
"""
@@ -1583,6 +1730,76 @@ def signed_individual_callback_gerrit(self, content, user_id):
project_id = signature.get_signature_project_id()
self.send_to_s3(document_data, project_id, signature_id, 'icla', user_id)
cla.log.debug(f'{fn} - uploaded ICLA document to s3')
+
+ def signed_individual_callback_gitlab(self, content, user_id):
+ fn = 'models.docusign_models.signed_individual_callback_gitlab'
+ cla.log.debug(f'{fn} - Docusign GitLab ICLA signed callback POST data: {content}')
+ tree = ET.fromstring(content)
+ # Get envelope ID.
+ envelope_id = tree.find('.//' + self.TAGS['envelope_id']).text
+ # Assume only one signature per signature.
+ signature_id = tree.find('.//' + self.TAGS['client_user_id']).text
+ signature = cla.utils.get_signature_instance()
+ try:
+ signature.load(signature_id)
+ except DoesNotExist:
+ cla.log.error(f'{fn} - DocuSign GitLab ICLA callback returned signed info '
+ f'on invalid signature: {content}')
+ return
+ # Iterate through recipients and update the signature signature status if changed.
+ elem = tree.find('.//' + self.TAGS['recipient_statuses'] +
+ '/' + self.TAGS['recipient_status'])
+ status = elem.find(self.TAGS['status']).text
+ if status == 'Completed' and not signature.get_signature_signed():
+ cla.log.info(f'{fn} - ICLA signature signed ({signature_id}) - notifying repository service provider')
+ # Get User
+ user = cla.utils.get_user_instance()
+ user.load(user_id)
+
+ cla.log.debug(f'{fn} - updating signature in database - setting signed=true...')
+ signature.set_signature_signed(True)
+ populate_signature_from_icla_callback(content, tree, signature)
+ signature.save()
+
+ # Load the Project by ID and send audit event
+ project = Project()
+ try:
+ project.load(signature.get_signature_project_id())
+ event_data = (f'The user {user.get_user_name()} signed an individual CLA for '
+ f'project {project.get_project_name()}.')
+ event_summary = (f'The user {user.get_user_name()} signed an individual CLA for '
+ f'project {project.get_project_name()} with project ID: {project.get_project_id()}.')
+ Event.create_event(
+ event_type=EventType.IndividualSignatureSigned,
+ event_cla_group_id=signature.get_signature_project_id(),
+ event_company_id=None,
+ event_user_id=user.get_user_id(),
+ event_user_name=user.get_user_name(),
+ event_data=event_data,
+ event_summary=event_summary,
+ contains_pii=False,
+ )
+ except DoesNotExist as err:
+ msg = (f'{fn} - unable to load project by CLA Group ID: {signature.get_signature_project_id()}, '
+ f'unable to send audit event, error: {err}')
+ cla.log.warning(msg)
+ return
+
+
+ # Get signed document
+ document_data = self.get_signed_document(envelope_id, user)
+ # Send email with signed document.
+ self.send_signed_document(signature, document_data, user, icla=True)
+
+ # Verify user id exist for saving on storage
+ if user_id is None:
+ cla.log.warning(f'{fn} - missing user_id on ICLA for saving signed file on s3 storage')
+ raise SigningError('Missing user_id on ICLA for saving signed file on s3 storage.')
+
+ # Store document on S3
+ project_id = signature.get_signature_project_id()
+ self.send_to_s3(document_data, project_id, signature_id, 'icla', user_id)
+ cla.log.debug(f'{fn} - uploaded ICLA document to s3')
def signed_corporate_callback(self, content, project_id, company_id):
"""
diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py
index 5c17879f2..735d7a056 100755
--- a/cla-backend/cla/routes.py
+++ b/cla-backend/cla/routes.py
@@ -1198,7 +1198,7 @@ def request_individual_signature(
DATA: {'project_id': 'some-project-id',
'user_id': 'some-user-id',
- 'return_url_type': Gerrit/Github. Optional depending on presence of return_url
+ 'return_url_type': Gerrit/Github/GitLab. Optional depending on presence of return_url
'return_url': }
Creates a new signature given project and user IDs. The user will be redirected to the
@@ -1348,6 +1348,23 @@ def post_individual_signed(
content, installation_id, github_repository_id, change_request_id
)
+@hug.post(
+ "/signed/gitlab/individual/{user_id}", versions=2,
+)
+def post_individual_signed_gitlab(
+ body,
+ user_id: hug.types.uuid
+):
+ """
+ POST: /signed/gerritindividual/{user_id}
+
+ Callback URL from signing service upon ICLA signature for a Gitlab user.
+ """
+ content = body.read()
+ return cla.controllers.signing.post_individual_signed_gitlab(
+ content, user_id
+ )
+
@hug.post("/signed/gerrit/individual/{user_id}", versions=2)
def post_individual_signed_gerrit(body, user_id: hug.types.uuid):
From e72c738f41826540161eaf9bf3b035389b241fd6 Mon Sep 17 00:00:00 2001
From: Denis Kyorov
Date: Wed, 25 Aug 2021 19:12:35 +0300
Subject: [PATCH 0461/1276] gitlab trigger endpoint (#3198)
---
cla-backend-go/cmd/server.go | 2 +-
cla-backend-go/swagger/cla.v2.yaml | 44 +++++++++
cla-backend-go/v2/gitlab-activity/handlers.go | 96 ++++++++++++++++++-
cla-backend-go/v2/gitlab-activity/service.go | 48 ++++++++--
.../v2/gitlab_organizations/service.go | 2 +-
5 files changed, 181 insertions(+), 11 deletions(-)
diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go
index e1f88712e..a32ceac31 100644
--- a/cla-backend-go/cmd/server.go
+++ b/cla-backend-go/cmd/server.go
@@ -351,7 +351,7 @@ func server(localMode bool) http.Handler {
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
gitlab_organizations.Configure(v2API, gitlabOrganizationsService, eventsService)
gitlab_sign.Configure(v2API, gitlabSignService, eventsService, configFile.CLAContributorv2Base)
- gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
+ gitlab_activity.Configure(v2API, gitlabActivityService, gitlabOrganizationRepo, eventsService, gitlabApp)
v1Repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index ba88beaac..3ec200636 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -3966,6 +3966,33 @@ paths:
tags:
- gitlab-activity
+ /gitlab/trigger:
+ post:
+ summary: Gitlab Activity MR Trigger
+ description: Endpoint is used to trigger specific MR for gitlab
+ security: [ ]
+ operationId: gitlabTrigger
+ parameters:
+ - $ref: "#/parameters/x-request-id"
+ - name: gitlabTriggerInput
+ in: body
+ schema:
+ $ref: '#/definitions/gitlab-trigger-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
+
+
/repository-provider/gitlab/sign/{organizationID}/{gitlabRepositoryID}/{mergeRequestID}:
get:
summary: Gitlab sign request handler
@@ -4372,6 +4399,23 @@ definitions:
type: string
additionalProperties: true
+ gitlab-trigger-input:
+ type: object
+ required:
+ - gitlab_organization_id
+ - gitlab_external_repository_id
+ - gitlab_mr_id
+ properties:
+ gitlab_organization_id:
+ type: string
+ description: the gitlab organization id to which mr belongs to
+ gitlab_external_repository_id:
+ type: integer
+ description: gitlab project identifier associated with mr
+ gitlab_mr_id:
+ type: integer
+ description: gitlab mr id
+
github-repository-input:
type: object
required:
diff --git a/cla-backend-go/v2/gitlab-activity/handlers.go b/cla-backend-go/v2/gitlab-activity/handlers.go
index e65d3be08..fb83dca16 100644
--- a/cla-backend-go/v2/gitlab-activity/handlers.go
+++ b/cla-backend-go/v2/gitlab-activity/handlers.go
@@ -7,6 +7,8 @@ import (
"context"
"errors"
"fmt"
+ gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations"
@@ -19,7 +21,95 @@ import (
gitlabsdk "github.com/xanzy/go-gitlab"
)
-func Configure(api *operations.EasyclaAPI, service Service, eventService events.Service) {
+func Configure(api *operations.EasyclaAPI, service Service, gitlabOrgRepo gitlab_organizations.RepositoryInterface, eventService events.Service, gitLabApp *gitlab_api.App) {
+
+ api.GitlabActivityGitlabTriggerHandler = gitlab_activity.GitlabTriggerHandlerFunc(func(params gitlab_activity.GitlabTriggerParams) middleware.Responder {
+ requestID, _ := uuid.NewV4()
+ reqID := requestID.String()
+
+ if params.GitlabTriggerInput == nil || params.GitlabTriggerInput.GitlabOrganizationID == nil || params.GitlabTriggerInput.GitlabExternalRepositoryID == nil || params.GitlabTriggerInput.GitlabMrID == nil{
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, "missing parameter"))
+ }
+
+ gitlabOrganizationID := *params.GitlabTriggerInput.GitlabOrganizationID
+ gitlabExternalRepositoryID := *params.GitlabTriggerInput.GitlabExternalRepositoryID
+ gitlabMrID := *params.GitlabTriggerInput.GitlabMrID
+
+ f := logrus.Fields{
+ "functionName": "gitlab_activity.handlers.GitlabActivityGitlabTriggerHandler",
+ "requestID": reqID,
+ "gitlabOrganizationID":gitlabOrganizationID,
+ "gitlabExternalRepositoryID":gitlabExternalRepositoryID,
+ "gitlabMrID":gitlabMrID,
+ }
+
+ log.WithFields(f).Debugf("handling gitlab trigger")
+ ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID)
+
+ gitlabOrg, err := gitlabOrgRepo.GetGitLabOrganization(ctx, gitlabOrganizationID)
+ if err != nil {
+ msg := fmt.Sprintf("fetching gitlab org failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ if gitlabOrg == nil {
+ msg := fmt.Sprintf("fetching gitlab org failed no results returned")
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ gitlabClient, err := gitlab_api.NewGitlabOauthClient(gitlabOrg.AuthInfo, gitLabApp)
+ if err != nil {
+ msg := fmt.Sprintf("initializing gitlab client : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ log.WithFields(f).Debugf("fetching gitlab repository via external id")
+ gitlabProject, err := gitlab_api.GetProjectByID(ctx, gitlabClient, int(gitlabExternalRepositoryID))
+ if err != nil {
+ msg := fmt.Sprintf("fetching gitlab project failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ gitlabMr, err := gitlab_api.FetchMrInfo(gitlabClient, int(gitlabExternalRepositoryID), int(gitlabMrID))
+ if err != nil {
+ msg := fmt.Sprintf("fetching gitlab mr failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ err = service.ProcessMergeActivity(ctx, gitlabOrg.AuthState, &ProcessMergeActivityInput{
+ ProjectName: gitlabProject.Name,
+ ProjectPath: gitlabProject.PathWithNamespace,
+ ProjectNamespace: gitlabProject.Namespace.Name,
+ ProjectID: gitlabProject.ID,
+ MergeID: int(gitlabMrID),
+ RepositoryPath: gitlabProject.PathWithNamespace,
+ LastCommitSha: gitlabMr.SHA,
+ })
+ if err != nil {
+ msg := fmt.Sprintf("processing gitlab merge event failed : %v", err)
+ log.WithFields(f).Errorf(msg)
+ if errors.Is(err, secretTokenMismatch) {
+ return gitlab_activity.NewGitlabActivityUnauthorized().WithPayload(
+ utils.ErrorResponseUnauthorized(reqID, msg))
+ }
+ return gitlab_activity.NewGitlabActivityInternalServerError().WithPayload(
+ utils.ErrorResponseBadRequest(reqID, msg))
+ }
+
+ return gitlab_activity.NewGitlabActivityOK()
+
+ })
api.GitlabActivityGitlabActivityHandler = gitlab_activity.GitlabActivityHandlerFunc(func(params gitlab_activity.GitlabActivityParams) middleware.Responder {
requestID, _ := uuid.NewV4()
@@ -31,7 +121,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
log.WithFields(f).Debugf("handling gitlab activity callback")
ctx := context.WithValue(context.Background(), utils.XREQUESTID, reqID)
- if params.XGitlabToken == ""{
+ if params.XGitlabToken == "" {
return gitlab_activity.NewGitlabActivityUnauthorized().WithPayload(
utils.ErrorResponseUnauthorized(reqID, "missing webhook secret token"))
}
@@ -71,7 +161,7 @@ func Configure(api *operations.EasyclaAPI, service Service, eventService events.
if err != nil {
msg := fmt.Sprintf("processing gitlab merge event failed : %v", err)
log.WithFields(f).Errorf(msg)
- if errors.Is(err, secretTokenMismatch){
+ if errors.Is(err, secretTokenMismatch) {
return gitlab_activity.NewGitlabActivityUnauthorized().WithPayload(
utils.ErrorResponseUnauthorized(reqID, msg))
}
diff --git a/cla-backend-go/v2/gitlab-activity/service.go b/cla-backend-go/v2/gitlab-activity/service.go
index ed0c9e26d..09b6a01f2 100644
--- a/cla-backend-go/v2/gitlab-activity/service.go
+++ b/cla-backend-go/v2/gitlab-activity/service.go
@@ -41,6 +41,17 @@ var (
secretTokenMismatch = errors.New("secret token mismatch")
)
+// ProcessMergeActivityInput is used to pass the data needed to trigger a gitlab mr check
+type ProcessMergeActivityInput struct {
+ ProjectName string
+ ProjectPath string
+ ProjectNamespace string
+ ProjectID int
+ MergeID int
+ RepositoryPath string
+ LastCommitSha string
+}
+
type gatedGitlabUser struct {
*gitlab.User
err error
@@ -48,6 +59,7 @@ type gatedGitlabUser struct {
type Service interface {
ProcessMergeOpenedActivity(ctx context.Context, secretToken string, mergeEvent *gitlab.MergeEvent) error
+ ProcessMergeActivity(ctx context.Context, secretToken string, input *ProcessMergeActivityInput) error
}
type service struct {
@@ -79,13 +91,38 @@ func NewService(gitlabRepository gitlab_organizations.RepositoryInterface, gitRe
func (s service) ProcessMergeOpenedActivity(ctx context.Context, secretToken string, mergeEvent *gitlab.MergeEvent) error {
projectName := mergeEvent.Project.Name
+ projectPath := mergeEvent.Project.PathWithNamespace
+ projectNamespace := mergeEvent.Project.Namespace
projectID := mergeEvent.Project.ID
mergeID := mergeEvent.ObjectAttributes.IID
repositoryPath := mergeEvent.Project.PathWithNamespace
lastCommitSha := mergeEvent.ObjectAttributes.LastCommit.ID
+ input := &ProcessMergeActivityInput{
+ ProjectName: projectName,
+ ProjectPath: projectPath,
+ ProjectNamespace: projectNamespace,
+ ProjectID: projectID,
+ MergeID: mergeID,
+ RepositoryPath: repositoryPath,
+ LastCommitSha: lastCommitSha,
+ }
+
+ return s.ProcessMergeActivity(ctx, secretToken, input)
+
+}
+
+func (s *service) ProcessMergeActivity(ctx context.Context, secretToken string, input *ProcessMergeActivityInput) error {
+ projectName := input.ProjectName
+ projectPath := input.ProjectPath
+ projectNamespace := input.ProjectNamespace
+ projectID := input.ProjectID
+ mergeID := input.MergeID
+ repositoryPath := input.RepositoryPath
+ lastCommitSha := input.LastCommitSha
+
f := logrus.Fields{
- "functionName": "ProcessMergeOpenedActivity",
+ "functionName": "ProcessMergeActivity",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"gitlabProjectName": projectName,
"gitlabProjectID": projectID,
@@ -94,7 +131,7 @@ func (s service) ProcessMergeOpenedActivity(ctx context.Context, secretToken str
}
log.WithFields(f).Debugf("looking up for gitlab org in easycla records ...")
- gitlabOrg, err := s.getGitlabOrganizationFromMergeEvent(ctx, mergeEvent)
+ gitlabOrg, err := s.getGitlabOrganizationFromProjectPath(ctx, projectPath, projectNamespace)
if err != nil {
return fmt.Errorf("fetching internal gitlab org for following path : %s failed : %v", repositoryPath, err)
}
@@ -279,15 +316,14 @@ func getAuthorInfo(gitlabUser *gitlab.User) string {
return fmt.Sprintf("%d:%s", gitlabUser.ID, gitlabUser.Username)
}
-func (s service) getGitlabOrganizationFromMergeEvent(ctx context.Context, mergeEvent *gitlab.MergeEvent) (*common.GitLabOrganization, error) {
- repositoryPath := mergeEvent.Project.PathWithNamespace
- parts := strings.Split(repositoryPath, "/")
+func (s service) getGitlabOrganizationFromProjectPath(ctx context.Context, projectPath, projectNameSpace string) (*common.GitLabOrganization, error) {
+ parts := strings.Split(projectPath, "/")
organizationName := parts[0]
gitlabOrg, err := s.gitlabRepository.GetGitLabOrganizationByName(ctx, organizationName)
if err != nil || gitlabOrg == nil {
// try getting it with project name as well
- gitlabOrg, err = s.gitlabRepository.GetGitLabOrganizationByName(ctx, mergeEvent.Project.Namespace)
+ gitlabOrg, err = s.gitlabRepository.GetGitLabOrganizationByName(ctx, projectNameSpace)
if err != nil || gitlabOrg == nil {
return nil, fmt.Errorf("gitlab org : %s doesn't exist : %v", organizationName, err)
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 75836c960..0b81d0ace 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -224,7 +224,7 @@ func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGrou
if dbModel == nil {
return nil, nil
}
-
+
return common.ToModel(dbModel), nil
}
From 2d66f2d4b347b55aa86f195825889ed4cdcb393a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 25 Aug 2021 11:28:58 -0700
Subject: [PATCH 0462/1276] Added GitLab Onboarding Filter (#3199)
---
cla-backend-go/v2/repositories/gitlab_services.go | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 83d69b212..90f2222b0 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -9,6 +9,7 @@ import (
"fmt"
"sort"
"strconv"
+ "strings"
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
@@ -191,8 +192,14 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
// Build a list of project IDs
for _, proj := range projectList {
- log.WithFields(f).Debugf("id: %d, repo: %s, path: %s, full path: %s, weburl: %s", proj.ID, proj.Name, proj.Path, proj.PathWithNamespace, proj.WebURL)
- listProjectIDs = append(listProjectIDs, int64(proj.ID))
+ // Ensure we only add GitLab projects/repositories that are in the tree path of the GitLab group/organization - we don't want to reach over into a different tree if the user has access
+ // The GetGroupProjectListByGroupID call in some cases returns more than expected (need to investigate why)
+ if strings.HasPrefix(proj.PathWithNamespace, gitLabOrgModel.OrganizationFullPath) {
+ log.WithFields(f).Debugf("adding - id: %d, repo: %s, path: %s, full path: %s, weburl: %s", proj.ID, proj.Name, proj.Path, proj.PathWithNamespace, proj.WebURL)
+ listProjectIDs = append(listProjectIDs, int64(proj.ID))
+ } else {
+ log.WithFields(f).Debugf("skipping - id: %d, repo: %s, path: %s, full path: %s, weburl: %s", proj.ID, proj.Name, proj.Path, proj.PathWithNamespace, proj.WebURL)
+ }
}
// Build input to the add function
From ef1cf883122e5234c160d619c89813219d8c966a Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 25 Aug 2021 12:57:15 -0700
Subject: [PATCH 0463/1276] Added GitLab Repo Query Filter by GitLab Group
Prefix (#3200)
- Added filter so that GitLab repos are filter based on the GitLab group
name as a prefix. This handles the case linuxfoundation/product/asitha
vs linuxfoundation/product/asitha-cla matching together.
Signed-off-by: David Deal
---
.../swagger/common/gitlab-organization.yaml | 4 ++++
cla-backend-go/v2/gitlab-activity/handlers.go | 13 +++++-----
.../v2/gitlab_organizations/service.go | 5 ++--
.../v2/repositories/gitlab_services.go | 19 ++++++++++++++-
cla-backend-go/v2/repositories/repository.go | 24 ++++++++++++++++++-
cla-backend-go/v2/repositories/service.go | 1 +
6 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/cla-backend-go/swagger/common/gitlab-organization.yaml b/cla-backend-go/swagger/common/gitlab-organization.yaml
index 1f577200f..113b4f23b 100644
--- a/cla-backend-go/swagger/common/gitlab-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-organization.yaml
@@ -56,6 +56,10 @@ properties:
auto_enabled_cla_group_id:
type: string
description: Specifies which Cla group ID to be used when autoEnabled flag in enabled for the Github Organization. If autoEnabled is on this field needs to be set as well.
+ branch_protection_enabled:
+ type: boolean
+ description: Flag to indicate if this GitHub Organization is configured to automatically setup branch protection on CLA enabled repositories.
+ x-omitempty: false
auth_info:
type: string
description: auth info
diff --git a/cla-backend-go/v2/gitlab-activity/handlers.go b/cla-backend-go/v2/gitlab-activity/handlers.go
index fb83dca16..ee08cec23 100644
--- a/cla-backend-go/v2/gitlab-activity/handlers.go
+++ b/cla-backend-go/v2/gitlab-activity/handlers.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+
gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
@@ -27,7 +28,7 @@ func Configure(api *operations.EasyclaAPI, service Service, gitlabOrgRepo gitlab
requestID, _ := uuid.NewV4()
reqID := requestID.String()
- if params.GitlabTriggerInput == nil || params.GitlabTriggerInput.GitlabOrganizationID == nil || params.GitlabTriggerInput.GitlabExternalRepositoryID == nil || params.GitlabTriggerInput.GitlabMrID == nil{
+ if params.GitlabTriggerInput == nil || params.GitlabTriggerInput.GitlabOrganizationID == nil || params.GitlabTriggerInput.GitlabExternalRepositoryID == nil || params.GitlabTriggerInput.GitlabMrID == nil {
return gitlab_activity.NewGitlabActivityBadRequest().WithPayload(
utils.ErrorResponseBadRequest(reqID, "missing parameter"))
}
@@ -37,11 +38,11 @@ func Configure(api *operations.EasyclaAPI, service Service, gitlabOrgRepo gitlab
gitlabMrID := *params.GitlabTriggerInput.GitlabMrID
f := logrus.Fields{
- "functionName": "gitlab_activity.handlers.GitlabActivityGitlabTriggerHandler",
- "requestID": reqID,
- "gitlabOrganizationID":gitlabOrganizationID,
- "gitlabExternalRepositoryID":gitlabExternalRepositoryID,
- "gitlabMrID":gitlabMrID,
+ "functionName": "gitlab_activity.handlers.GitlabActivityGitlabTriggerHandler",
+ "requestID": reqID,
+ "gitlabOrganizationID": gitlabOrganizationID,
+ "gitlabExternalRepositoryID": gitlabExternalRepositoryID,
+ "gitlabMrID": gitlabMrID,
}
log.WithFields(f).Debugf("handling gitlab trigger")
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index 0b81d0ace..d7b8ed1db 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -288,7 +288,8 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
continue
}
- repoList, repoErr := s.v2GitRepoService.GitLabGetRepositoriesByProjectSFID(ctx, projectSFID)
+ log.WithFields(f).Debugf("filtering repositories based on group path: %s", org.OrganizationFullPath)
+ repoList, repoErr := s.v2GitRepoService.GitLabGetRepositoriesByNamePrefix(ctx, fmt.Sprintf("%s/", org.OrganizationFullPath))
if repoErr != nil {
if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
log.WithFields(f).WithError(repoErr).Debugf("no GitLab repositories onboarded for project : %s", projectSFID)
@@ -306,7 +307,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
OrganizationFullPath: org.OrganizationFullPath,
OrganizationExternalID: org.OrganizationExternalID,
InstallationURL: buildInstallationURL(org.OrganizationID, orgDetailed.AuthState),
- BranchProtectionEnabled: false, // TODO review this - why not modeled?
+ BranchProtectionEnabled: org.BranchProtectionEnabled,
ConnectionStatus: "", // updated below
Repositories: []*models.GitlabProjectRepository{}, // updated below
}
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 90f2222b0..ccd37c90c 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -305,7 +305,24 @@ func (s *Service) GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupI
// GitLabGetRepositoriesByOrganizationName returns the list of repositories associated with the Organization/Group name
func (s *Service) GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error) {
- dbModels, err := s.gitV2Repository.GitHubGetRepositoriesByOrganizationName(ctx, orgName)
+ dbModels, err := s.gitV2Repository.GitLabGetRepositoriesByOrganizationName(ctx, orgName)
+ if err != nil {
+ return nil, err
+ }
+
+ responses, err := dbModelsToGitLabRepositories(dbModels)
+ if err != nil {
+ return nil, err
+ }
+
+ return &v2Models.GitlabRepositoriesList{
+ List: responses,
+ }, nil
+}
+
+// GitLabGetRepositoriesByNamePrefix returns a list of repositories that match the name prefix
+func (s *Service) GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) (*v2Models.GitlabRepositoriesList, error) {
+ dbModels, err := s.gitV2Repository.GitLabGetRepositoriesByNamePrefix(ctx, repositoryNamePrefix)
if err != nil {
return nil, err
}
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index ccbf1485d..4bc6f3848 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -31,6 +31,7 @@ type RepositoryInterface interface {
GitLabGetRepository(ctx context.Context, repositoryID string) (*repoModels.RepositoryDBModel, error)
GitLabGetRepositoryByName(ctx context.Context, repositoryName string) (*repoModels.RepositoryDBModel, error)
+ GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) ([]*repoModels.RepositoryDBModel, error)
GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) ([]*repoModels.RepositoryDBModel, error)
GitLabGetRepositoryByExternalID(ctx context.Context, repositoryExternalID int64) (*repoModels.RepositoryDBModel, error)
GitLabAddRepository(ctx context.Context, projectSFID string, input *repoModels.RepositoryDBModel) (*repoModels.RepositoryDBModel, error)
@@ -256,8 +257,29 @@ func (r *Repository) GitHubGetRepositoriesByProjectSFID(ctx context.Context, pro
return records, nil
}
-// GitHubGetRepositoriesByOrganizationName returns a list of repositories associated with the specified organization name
+// GitHubGetRepositoriesByOrganizationName returns a list of GitHub repositories associated with the specified organization name
func (r *Repository) GitHubGetRepositoriesByOrganizationName(ctx context.Context, orgName string) ([]*repoModels.RepositoryDBModel, error) {
+ condition := expression.Key(repoModels.RepositoryOrganizationNameColumn).Equal(expression.Value(orgName))
+ filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitHubType))
+
+ records, err := r.getRepositoriesWithConditionFilter(ctx, condition, filter, repoModels.RepositoryOrganizationNameIndex)
+ if err != nil {
+ // Catch the error - return the same error with the appropriate details
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil, &utils.GitLabRepositoryNotFound{
+ OrganizationName: orgName,
+ }
+ }
+
+ // Some other error
+ return nil, err
+ }
+
+ return records, nil
+}
+
+// GitLabGetRepositoriesByOrganizationName returns a list of GitLab repositories associated with the specified organization name
+func (r *Repository) GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) ([]*repoModels.RepositoryDBModel, error) {
condition := expression.Key(repoModels.RepositoryOrganizationNameColumn).Equal(expression.Value(orgName))
filter := expression.Name(repoModels.RepositoryTypeColumn).Equal(expression.Value(utils.GitLabLower))
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index c303177b7..fca7deff8 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -59,6 +59,7 @@ type ServiceInterface interface {
GitLabGetRepositoriesByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByCLAGroup(ctx context.Context, claGroupID string, enabled bool) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error)
+ GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositories(ctx context.Context, projectSFID string, input *GitLabAddRepoModel) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
GitLabEnrollRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64, enrollValue bool) error
From 5c94335e7cc7ea659bc8d0697a786843208297a3 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 25 Aug 2021 15:05:30 -0700
Subject: [PATCH 0464/1276] Updated GitLab Group Configuration Update API
(#3201)
- Updated GitLab group configuration API logic, return status in the
response, resolved branch protection flag issue, added note to record.
Signed-off-by: David Deal
---
cla-backend-go/swagger/cla.v2.yaml | 2 +
cla-backend-go/v2/common/models.go | 27 ++++-----
.../v2/gitlab_organizations/handlers.go | 58 +++++++++++++++++--
.../v2/gitlab_organizations/repository.go | 29 ++++++----
4 files changed, 87 insertions(+), 29 deletions(-)
diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml
index 3ec200636..c014011ae 100644
--- a/cla-backend-go/swagger/cla.v2.yaml
+++ b/cla-backend-go/swagger/cla.v2.yaml
@@ -1718,6 +1718,8 @@ paths:
x-request-id:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
+ schema:
+ $ref: '#/definitions/gitlab-project-organizations'
'400':
$ref: '#/responses/invalid-request'
'401':
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index 5b94929c3..427ed6c2f 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -32,19 +32,20 @@ type GitLabOrganization struct {
// ToModel converts to models.GitlabOrganization
func ToModel(in *GitLabOrganization) *models2.GitlabOrganization {
return &models2.GitlabOrganization{
- OrganizationID: in.OrganizationID,
- DateCreated: in.DateCreated,
- DateModified: in.DateModified,
- OrganizationName: in.OrganizationName,
- OrganizationFullPath: in.OrganizationFullPath,
- OrganizationURL: in.OrganizationURL,
- OrganizationSfid: in.OrganizationSFID,
- Version: in.Version,
- Enabled: in.Enabled,
- AutoEnabled: in.AutoEnabled,
- AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
- ProjectSfid: in.ProjectSFID,
- OrganizationExternalID: int64(in.ExternalGroupID),
+ OrganizationID: in.OrganizationID,
+ DateCreated: in.DateCreated,
+ DateModified: in.DateModified,
+ OrganizationName: in.OrganizationName,
+ OrganizationFullPath: in.OrganizationFullPath,
+ OrganizationURL: in.OrganizationURL,
+ OrganizationSfid: in.OrganizationSFID,
+ Version: in.Version,
+ Enabled: in.Enabled,
+ AutoEnabled: in.AutoEnabled,
+ AutoEnabledClaGroupID: in.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: in.BranchProtectionEnabled,
+ ProjectSfid: in.ProjectSFID,
+ OrganizationExternalID: int64(in.ExternalGroupID),
}
}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index e91f48e27..502606e55 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -195,24 +195,57 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
api.GitlabOrganizationsUpdateProjectGitlabGroupConfigHandler = gitlab_organizations.UpdateProjectGitlabGroupConfigHandlerFunc(func(params gitlab_organizations.UpdateProjectGitlabGroupConfigParams, authUser *auth.User) middleware.Responder {
reqID := utils.GetRequestID(params.XREQUESTID)
+ utils.SetAuthUserProperties(authUser, params.XUSERNAME, params.XEMAIL)
ctx := utils.ContextWithRequestAndUser(params.HTTPRequest.Context(), reqID, authUser) // nolint
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.handlers.GitlabOrganizationsAddProjectGitlabOrganizationHandler",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "authUser": authUser.UserName,
+ "authEmail": authUser.Email,
+ "projectSFID": params.ProjectSFID,
+ "gitLabGroupID": params.GitLabGroupID,
+ "autoEnabled": params.Body.AutoEnabled,
+ "autoEnabledCLAGroupID": params.Body.AutoEnabledClaGroupID,
+ "branchProtectionEnabled": params.Body.BranchProtectionEnabled,
+ }
+
+ // Load the project
+ psc := project_service.GetClient()
+ projectModel, err := psc.GetProject(params.ProjectSFID)
+ if err != nil || projectModel == nil {
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
+ }
+
+ if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
+ msg := fmt.Sprintf("user %s does not have access to Update Project GitLab Group/Organizations for Project '%s' with scope of %s",
+ authUser.UserName, projectModel.Name, params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigForbidden().WithPayload(
+ utils.ErrorResponseForbidden(reqID, msg))
+ }
+
if !utils.ValidateAutoEnabledClaGroupID(params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID) {
msg := "AutoEnabledClaGroupID can't be empty when AutoEnabled is set to true"
return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- err := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.GitLabGroupID, "", "", params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
- if err != nil {
- if errors.Is(err, projects_cla_groups.ErrCLAGroupDoesNotExist) {
- return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, err.Error()))
+ updateErr := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.GitLabGroupID, "", "", params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ if updateErr != nil {
+ if errors.Is(updateErr, projects_cla_groups.ErrCLAGroupDoesNotExist) {
+ msg := fmt.Sprintf("problem updating GitLab group/organization for project %s with SFID: %s - CLA Group wth ID: %s was not found, error: %+v", projectModel.Name, projectModel.ID, params.Body.AutoEnabledClaGroupID, updateErr)
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, msg))
}
- return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, "updating gitlab org", err))
+ msg := fmt.Sprintf("problem updating GitLab group/organization for project %s with SFID: %s, error: %+v", projectModel.Name, projectModel.ID, updateErr)
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, updateErr))
}
eventService.LogEventWithContext(ctx, &events.LogEventArgs{
EventType: events.GitlabOrganizationUpdated,
ProjectSFID: params.ProjectSFID,
+ ProjectName: projectModel.Name,
+ CLAGroupID: params.Body.AutoEnabledClaGroupID,
LfUsername: authUser.UserName,
UserName: authUser.UserName,
EventData: &events.GitlabOrganizationUpdatedEventData{
@@ -222,7 +255,20 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
},
})
- return gitlab_organizations.NewUpdateProjectGitlabGroupConfigOK()
+ results, err := service.GetGitLabOrganizations(ctx, params.ProjectSFID)
+ if err != nil {
+ if strings.ContainsAny(err.Error(), "getProjectNotFound") {
+ msg := fmt.Sprintf("Gitlab organization with project SFID not found: %s", params.ProjectSFID)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(utils.ErrorResponseNotFound(reqID, msg))
+ }
+
+ msg := fmt.Sprintf("failed to locate Gitlab organization by project SFID: %s, error: %+v", params.ProjectSFID, err)
+ log.WithFields(f).Debug(msg)
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequestWithError(reqID, msg, err))
+ }
+
+ return gitlab_organizations.NewUpdateProjectGitlabGroupConfigOK().WithPayload(results)
})
api.GitlabOrganizationsDeleteProjectGitlabGroupConfigHandler = gitlab_organizations.DeleteProjectGitlabGroupConfigHandlerFunc(func(params gitlab_organizations.DeleteProjectGitlabGroupConfigParams, authUser *auth.User) middleware.Responder {
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index af939445b..f244da8b3 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -549,21 +549,27 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
}
_, currentTime := utils.CurrentTime()
+ note := fmt.Sprintf("Updated configuration on %s by %s.", currentTime, utils.GetUserNameFromContext(ctx))
+ if existingRecord.Note != "" {
+ note = fmt.Sprintf("%s. %s", existingRecord.Note, note)
+ }
+
expressionAttributeNames := map[string]*string{
- "#A": aws.String(GitLabOrganizationsAutoEnabledColumn),
- "#C": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
- "#B": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
- "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
- "#E": aws.String(GitLabOrganizationsEnabledColumn),
+ "#AE": aws.String(GitLabOrganizationsAutoEnabledColumn),
+ "#AECLA": aws.String(GitLabOrganizationsAutoEnabledCLAGroupIDColumn),
+ "#BP": aws.String(GitLabOrganizationsBranchProtectionEnabledColumn),
+ "#M": aws.String(GitLabOrganizationsDateModifiedColumn),
+ "#E": aws.String(GitLabOrganizationsEnabledColumn),
+ "#N": aws.String(GitLabOrganizationsNoteColumn),
}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{
- ":a": {
+ ":ae": {
BOOL: aws.Bool(autoEnabled),
},
- ":c": {
+ ":aecla": {
S: aws.String(autoEnabledClaGroupID),
},
- ":b": {
+ ":bp": {
BOOL: aws.Bool(branchProtectionEnabled),
},
":m": {
@@ -572,8 +578,11 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
":e": {
BOOL: aws.Bool(enabled),
},
+ ":n": {
+ S: aws.String(note),
+ },
}
- updateExpression := "SET #A = :a, #C = :c, #B = :b, #M = :m, #E = :e "
+ updateExpression := "SET #AE = :ae, #AECLA = :aecla, #BP = :bp, #M = :m, #E = :e, #N = :n "
if organizationName != "" {
expressionAttributeNames["#N"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
@@ -633,7 +642,7 @@ func (repo *Repository) DeleteGitLabOrganizationByFullPath(ctx context.Context,
_, currentTime := utils.CurrentTime()
note := fmt.Sprintf("Enabled set to false due to org deletion on %s by %s.", currentTime, utils.GetUserNameFromContext(ctx))
if org.Note != "" {
- note = fmt.Sprintf("%s. %s.", org.Note, note)
+ note = fmt.Sprintf("%s. %s", org.Note, note)
}
_, err := repo.dynamoDBClient.UpdateItem(
&dynamodb.UpdateItemInput{
From e65799efb2916b52b22e53b5e740a80ede5bc0f8 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 25 Aug 2021 17:32:00 -0700
Subject: [PATCH 0465/1276] Added Checks for Duplicate GitLab Groups/Orgs Used
By Other Projects (#3202)
---
cla-backend-go/utils/errors.go | 34 +++++++++++++++++++
.../v2/gitlab_organizations/handlers.go | 4 +++
.../v2/gitlab_organizations/service.go | 32 +++++++++++++++++
3 files changed, 70 insertions(+)
diff --git a/cla-backend-go/utils/errors.go b/cla-backend-go/utils/errors.go
index 67193de67..d93b59f5f 100644
--- a/cla-backend-go/utils/errors.go
+++ b/cla-backend-go/utils/errors.go
@@ -66,6 +66,40 @@ func (e *CLAGroupNotFound) Unwrap() error {
return e.Err
}
+// ProjectSummary is a quick data model for the project name and ID
+type ProjectSummary struct {
+ ID string
+ Name string
+}
+
+// ProjectConflict is an error model for project conflict
+type ProjectConflict struct {
+ Message string
+ ProjectA ProjectSummary
+ ProjectB ProjectSummary
+ Err error
+}
+
+// Error is an error string function for CLA Group not found errors
+func (e *ProjectConflict) Error() string {
+ msg := fmt.Sprintf("%s - ", e.Message)
+ msg = fmt.Sprintf("%s conflict between project %s (%s) and project %s (%s)",
+ msg,
+ e.ProjectA.Name, e.ProjectA.ID,
+ e.ProjectB.Name, e.ProjectB.ID,
+ )
+ if e.Err == nil {
+ return strings.TrimSpace(msg)
+ }
+
+ return strings.TrimSpace(fmt.Sprintf("%s, error: %+v", msg, e.Err))
+}
+
+// Unwrap method returns its contained error
+func (e *ProjectConflict) Unwrap() error {
+ return e.Err
+}
+
// CLAGroupNameConflict is an error model for CLA Group name conflicts
type CLAGroupNameConflict struct {
CLAGroupID string
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 502606e55..7c2be448b 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -169,6 +169,10 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
result, err := service.AddGitLabOrganization(ctx, params.ProjectSFID, params.Body)
if err != nil {
+ if _, ok := err.(*utils.ProjectConflict); ok {
+ return gitlab_organizations.NewAddProjectGitlabOrganizationConflict().WithPayload(
+ utils.ErrorResponseConflict(reqID, err.Error()))
+ }
msg := fmt.Sprintf("unable to add GitLab organization, error: %+v", err)
log.WithFields(f).WithError(err).Warn(msg)
return gitlab_organizations.NewAddProjectGitlabOrganizationBadRequest().WithPayload(
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index d7b8ed1db..ba3e06f9d 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -93,7 +93,39 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
}
}
+ // If we have an existing record/entry
if existingModel != nil {
+ // Check to make sure another project doesn't own this GitLab Group - only care about conflicts if it is enabled
+ if existingModel.ProjectSfid != projectSFID && existingModel.Enabled {
+ psc := project_service.GetClient()
+ requestedProjectModel, projectLookupErr := psc.GetProject(projectSFID)
+ if projectLookupErr != nil || requestedProjectModel == nil {
+ return nil, projectLookupErr
+ }
+ existingProjectModel, projectLookupErr := psc.GetProject(existingModel.ProjectSfid)
+ if projectLookupErr != nil || existingProjectModel == nil {
+ log.WithFields(f).WithError(projectLookupErr).Warnf("unable to lookup project with SFID: %s", existingModel.ProjectSfid)
+ return nil, projectLookupErr
+ }
+ msg := fmt.Sprintf("unable to add or update the GitLab Group/Organization - already taken by another project: %s (%s) - unable to add to this project: %s (%s)",
+ existingProjectModel.Name, existingModel.ProjectSfid,
+ requestedProjectModel.Name, projectSFID)
+ log.WithFields(f).Warn(msg)
+
+ // Return the error model
+ return nil, &utils.ProjectConflict{
+ Message: "unable to add or update the GitLab Group/Organization - already taken by another project",
+ ProjectA: utils.ProjectSummary{
+ Name: requestedProjectModel.Name,
+ ID: projectSFID,
+ },
+ ProjectB: utils.ProjectSummary{
+ Name: existingProjectModel.Name,
+ ID: existingModel.ProjectSfid,
+ },
+ }
+ }
+
updateErr := s.UpdateGitLabOrganization(ctx, projectSFID, input.GroupID, "", input.OrganizationFullPath, utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled))
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warnf("problem updating GitLab group/organization, error: %+v", updateErr)
From e1601bc30dbad3823ce97c369070b935b3907fcc Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Thu, 26 Aug 2021 08:34:49 -0700
Subject: [PATCH 0466/1276] Feature/GitLab MR Update (#3204)
- Added integration with /v4/gitlab/trigger endpoint
- Updated ICLA callback for gitlab upon icla sign completion
Signed-off-by: Harold Wanyama
---
cla-backend/cla/controllers/signing.py | 4 +-
cla-backend/cla/models/docusign_models.py | 38 ++++++++++++++----
cla-backend/cla/routes.py | 11 +++--
cla-backend/cla/utils.py | 49 ++++++++++++++++++++++-
4 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index 701e3d82c..b8cf5f6d0 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -181,7 +181,7 @@ def post_individual_signed(content, installation_id, github_repository_id, chang
"""
get_signing_service().signed_individual_callback(content, installation_id, github_repository_id, change_request_id)
-def post_individual_signed_gitlab(content, user_id):
+def post_individual_signed_gitlab(content, user_id, organization_id, gitlab_repository_id, merge_request_id):
"""
Handle the posted callback from the signing service after ICLA signature.
@@ -190,7 +190,7 @@ def post_individual_signed_gitlab(content, user_id):
:param user_id: The ID of the user that signed.
:type user_id: string
"""
- get_signing_service().signed_individual_callback_gitlab(content,user_id)
+ get_signing_service().signed_individual_callback_gitlab(content,user_id, organization_id, gitlab_repository_id, merge_request_id)
def post_individual_signed_gerrit(content, user_id):
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 027488f8b..a742dd9ff 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -16,6 +16,7 @@
import xml.etree.ElementTree as ET
from typing import Dict, Any, Optional, List
from urllib.parse import urlparse
+import requests
import pydocusign # type: ignore
from attr import dataclass
@@ -178,7 +179,7 @@ def request_individual_signature_gitlab(self, project_id, user_id, return_url=No
cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
cla.log.debug('Individual Signature - get individual signature callback url')
- callback_url = self._generate_individual_signature_callback_url_gitlab(user_id)
+ callback_url = cla.utils.get_individual_signature_callback_url_gitlab(user_id, signature_metadata)
cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
if latest_signature is not None and \
@@ -985,13 +986,7 @@ def _generate_individual_signature_callback_url_gerrit(self, user_id):
"""
return os.path.join(api_base_url, 'v2/signed/gerrit/individual', str(user_id))
-
- def _generate_individual_signature_callback_url_gitlab(self, user_id):
- """
- Helper function to get a user's active signature callback URL for GitLab
- """
- return os.path.join(api_base_url, 'v2/signed/gitlab/individual', str(user_id))
def _get_corporate_signature_callback_url(self, project_id, company_id):
"""
@@ -1731,7 +1726,31 @@ def signed_individual_callback_gerrit(self, content, user_id):
self.send_to_s3(document_data, project_id, signature_id, 'icla', user_id)
cla.log.debug(f'{fn} - uploaded ICLA document to s3')
- def signed_individual_callback_gitlab(self, content, user_id):
+ def _update_gitlab_mr(self, organization_id: str , gitlab_repository_id: int, merge_request_id: int) -> None:
+ """
+ Helper function that updates mr upon a successful signing
+ param organization_id: Gitlab group id
+ rtype organization_id: int
+ param gitlab_repository_id: Gitlab repository
+ rtype: int
+ param merge_request_id: Gitlab MR
+ rtype: int
+ """
+ fn = 'models.docusign_models._update_gitlab_mr'
+ try:
+ url = f'{cla.config.PLATFORM_GATEWAY_URL}/cla-service/v4/gitlab/trigger'
+ payload = {
+ "gitlab_external_repository_id": gitlab_repository_id,
+ "gitlab_mr_id": merge_request_id,
+ "gitlab_organization_id": organization_id
+ }
+ requests.post(url, data=payload)
+ cla.log.debug(f'{fn} - Updating GitLab MR with payload: {payload}')
+ except requests.exceptions.HTTPError as err:
+ msg = f'{fn} - Unable to update GitLab MR: {merge_request_id}, error: {err}'
+ cla.log.warning(msg)
+
+ def signed_individual_callback_gitlab(self, content, user_id, organization_id, gitlab_repository_id, merge_request_id):
fn = 'models.docusign_models.signed_individual_callback_gitlab'
cla.log.debug(f'{fn} - Docusign GitLab ICLA signed callback POST data: {content}')
tree = ET.fromstring(content)
@@ -1761,6 +1780,9 @@ def signed_individual_callback_gitlab(self, content, user_id):
populate_signature_from_icla_callback(content, tree, signature)
signature.save()
+ #Update repository provider (GitLab)
+ self._update_gitlab_mr(organization_id, gitlab_repository_id, merge_request_id)
+
# Load the Project by ID and send audit event
project = Project()
try:
diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py
index 735d7a056..4ab7db905 100755
--- a/cla-backend/cla/routes.py
+++ b/cla-backend/cla/routes.py
@@ -1349,20 +1349,23 @@ def post_individual_signed(
)
@hug.post(
- "/signed/gitlab/individual/{user_id}", versions=2,
+ "/signed/gitlab/individual/{user_id}/{organization_id}/{gitlab_repository_id}/{merge_request_id}", versions=2,
)
def post_individual_signed_gitlab(
body,
- user_id: hug.types.uuid
+ user_id: hug.types.uuid,
+ organization_id: hug.types.text,
+ gitlab_repository_id: hug.types.number,
+ merge_request_id: hug.types.number,
):
"""
- POST: /signed/gerritindividual/{user_id}
+ POST: /signed/gitlab/individual/{user_id}/{organization_id}/{gitlab_repository_id}/{merge_request_id}
Callback URL from signing service upon ICLA signature for a Gitlab user.
"""
content = body.read()
return cla.controllers.signing.post_individual_signed_gitlab(
- content, user_id
+ content, user_id, organization_id, gitlab_repository_id, merge_request_id
)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 917c9f873..8b4fd5074 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -24,7 +24,7 @@
from cla.models import DoesNotExist
from cla.models.dynamo_models import User, Signature, Repository, \
Company, Project, Document, \
- GitHubOrg, Gerrit, UserPermissions, Event, CompanyInvite, ProjectCLAGroup, CCLAWhitelistRequest, CLAManagerRequest
+ GitHubOrg, Gerrit, UserPermissions, Event, CompanyInvite, ProjectCLAGroup, CCLAWhitelistRequest, CLAManagerRequest, GitlabOrg
from cla.models.event_types import EventType
API_BASE_URL = os.environ.get('CLA_API_BASE', '')
@@ -1239,6 +1239,22 @@ def get_installation_id_from_github_repository(github_repository_id):
# Get this organization's installation ID
return organization.get_organization_installation_id()
+def get_organization_id_from_gitlab_repository(gitlab_repository_id):
+ # Get repository ID that references the gitlab ID.
+ try:
+ repository = Repository().get_repository_by_external_id(gitlab_repository_id, 'gitlab')
+ except DoesNotExist:
+ return None
+ # Get GitLabGroup from this repository
+ gitLabOrg = GitlabOrg()
+ try:
+ gitLabOrg.load(repository.get_repository_organization_name())
+ except DoesNotExist:
+ return None
+
+ #return GitLab organization ID
+ return gitLabOrg.get_organization_id()
+
def get_project_id_from_github_repository(github_repository_id):
# Get repository ID that references the github ID.
@@ -1281,6 +1297,37 @@ def get_individual_signature_callback_url(user_id, metadata=None):
return os.path.join(API_BASE_URL, 'v2/signed/individual', str(installation_id), str(metadata['repository_id']),
str(metadata['pull_request_id']))
+def get_individual_signature_callback_url_gitlab(user_id, metadata=None):
+ """
+ Helper function to get a user's active signature callback URL.
+
+ :param user_id: The user ID in question.
+ :type user_id: string
+ :param metadata: The signature metadata
+ :type metadata: dict
+ :return: The callback URL that will be hit by the signing service provider.
+ :rtype: string
+ """
+ if metadata is None:
+ metadata = get_active_signature_metadata(user_id)
+ if metadata is None:
+ cla.log.warning('Could not find active signature for user {}, callback URL request failed'.format(user_id))
+ return None
+
+ # Get GitLab ID from metadata
+ gitlab_repository_id = metadata['repository_id']
+
+ # Get organization id
+ organization_id = get_organization_id_from_gitlab_repository(gitlab_repository_id)
+
+ if organization_id is None:
+ cla.log.error('Could not find GitLab organization ID that is configured for this repository ID: %s',
+ gitlab_repository_id)
+ return None
+
+ return os.path.join(API_BASE_URL, 'v2/signed/gitlab/individual',str(user_id), str(organization_id), str(metadata['repository_id']),
+ str(metadata['merge_request_id']))
+
def request_individual_signature(installation_id, github_repository_id, user, change_request_id, callback_url=None):
"""
From 1137fddab6789e807bac82029ceee04919128822 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Thu, 26 Aug 2021 16:23:49 -0700
Subject: [PATCH 0467/1276] Added/Updated GitLab Org Response Models (#3206)
---
cla-backend-go/serverless.yml | 1 +
.../common/gitlab-project-organization.yaml | 11 +
cla-backend-go/utils/project_helpers.go | 2 +-
cla-backend-go/v2/common/models.go | 9 +-
.../v2/gitlab_organizations/constants.go | 2 +
.../v2/gitlab_organizations/handlers.go | 63 +++-
.../v2/gitlab_organizations/repository.go | 312 +++++++++++-------
.../v2/gitlab_organizations/service.go | 114 +++----
cla-backend-go/v2/project-service/client.go | 18 +-
.../v2/repositories/gitlab_services.go | 1 +
cla-backend-go/v2/repositories/service.go | 14 +-
cla-backend/serverless.yml | 3 +-
12 files changed, 337 insertions(+), 213 deletions(-)
diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml
index 9808f2d07..c8f8b0b3c 100644
--- a/cla-backend-go/serverless.yml
+++ b/cla-backend-go/serverless.yml
@@ -198,6 +198,7 @@ provider:
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/cla-group-id-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/foundation-sfid-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-org-sfid-index"
+ - "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
- "arn:aws:dynamodb:${self:custom.dynamodb.region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
diff --git a/cla-backend-go/swagger/common/gitlab-project-organization.yaml b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
index f33b7fcf5..be9d40e84 100644
--- a/cla-backend-go/swagger/common/gitlab-project-organization.yaml
+++ b/cla-backend-go/swagger/common/gitlab-project-organization.yaml
@@ -4,6 +4,12 @@
type: object
description: GitLab Project Organization
properties:
+ project_sfid:
+ description: The project SFID
+ $ref: './common/properties/external-id.yaml'
+ parent_project_sfid:
+ description: The parent project SFID
+ $ref: './common/properties/external-id.yaml'
auto_enabled:
type: boolean
description: Flag to indicate if auto-enabled flag should be enabled. Organizations with auto-enable turned on will automatically include any new repositories to the EasyCLA configuration.
@@ -51,6 +57,11 @@ properties:
- partial_connection
- connection_failure
- no_connection
+ connection_status_message:
+ type: string
+ description: An optional connection status message
+ example: 'Token was revoked. You have to re-authorize from the user.'
+ x-omitempty: true
repositories:
type: array
items:
diff --git a/cla-backend-go/utils/project_helpers.go b/cla-backend-go/utils/project_helpers.go
index 066b4c622..2934f3821 100644
--- a/cla-backend-go/utils/project_helpers.go
+++ b/cla-backend-go/utils/project_helpers.go
@@ -18,7 +18,7 @@ func IsProjectHaveParent(project *models.ProjectOutputDetailed) bool {
return project != nil && project.Foundation != nil && project.Foundation.ID != "" && project.Foundation.Name != ""
}
-// IsProjectHasRootParent determines if the a given project has a root parent. A root parent is a parent that is empty parent or the parent is TLF or LFProjects
+// IsProjectHasRootParent determines if a given project has a root parent. A root parent is a parent that is empty parent or the parent is TLF or LFProjects
func IsProjectHasRootParent(project *models.ProjectOutputDetailed) bool {
return project.Foundation == nil || (project.Foundation != nil && project.Foundation.ID != "" && (project.Foundation.Name == TheLinuxFoundation || project.Foundation.Name == LFProjectsLLC))
}
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index 427ed6c2f..b2fe363e7 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -61,14 +61,16 @@ func ToModels(input []*GitLabOrganization) []*models2.GitlabOrganization {
// GitLabAddOrganization is data model for GitLab add organization requests
type GitLabAddOrganization struct {
OrganizationID string `json:"organization_id"`
- ExternalGroupID int `json:"external_gitlab_group_id"`
+ ExternalGroupID int64 `json:"external_gitlab_group_id"`
DateCreated string `json:"date_created,omitempty"`
DateModified string `json:"date_modified,omitempty"`
OrganizationName string `json:"organization_name,omitempty"`
OrganizationNameLower string `json:"organization_name_lower,omitempty"`
+ OrganizationFullPath string `json:"organization_full_path,omitempty"`
OrganizationURL string `json:"organization_url,omitempty"`
OrganizationSFID string `json:"organization_sfid,omitempty"`
ProjectSFID string `json:"project_sfid"`
+ ParentProjectSFID string `json:"parent_project_sfid"`
Enabled bool `json:"enabled"`
AutoEnabled bool `json:"auto_enabled"`
BranchProtectionEnabled bool `json:"branch_protection_enabled"`
@@ -77,3 +79,8 @@ type GitLabAddOrganization struct {
AuthState string `json:"auth_state"`
Version string `json:"version,omitempty"`
}
+
+// ExternalGroupIDAsInt returns the external group ID as an integer value
+func (m *GitLabAddOrganization) ExternalGroupIDAsInt() int {
+ return int(m.ExternalGroupID)
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/constants.go b/cla-backend-go/v2/gitlab_organizations/constants.go
index d84966b2a..bbd072a0c 100644
--- a/cla-backend-go/v2/gitlab_organizations/constants.go
+++ b/cla-backend-go/v2/gitlab_organizations/constants.go
@@ -4,6 +4,8 @@
package gitlab_organizations
const (
+ // GitLabOrganizationsProjectSFIDColumn constant
+ GitLabOrganizationsProjectSFIDColumn = "project_sfid"
// GitLabOrganizationsOrganizationIDColumn constant
GitLabOrganizationsOrganizationIDColumn = "organization_id"
// GitLabOrganizationsOrganizationSFIDColumn constant
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 7c2be448b..7d147b366 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -11,12 +11,14 @@ import (
"regexp"
"strings"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/common"
+
"github.com/go-openapi/runtime"
- project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+ projectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/restapi/operations/gitlab_activity"
- gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ gitlabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/gofrs/uuid"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
@@ -50,7 +52,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Load the project
- psc := project_service.GetClient()
+ psc := projectService.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
return gitlab_organizations.NewGetProjectGitlabOrganizationsNotFound().WithPayload(
@@ -100,13 +102,20 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Load the project
- psc := project_service.GetClient()
+ psc := projectService.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
+ // Load the project parent
+ parentProjectModel, err := psc.GetParentProjectModel(params.ProjectSFID)
+ if err != nil || parentProjectModel == nil {
+ return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate parent project from project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Add Project GitLab Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
@@ -123,6 +132,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequest(reqID, msg))
}
+ orgURL := params.Body.OrganizationFullPath
// Clean up/filter the Group Full Path, if needed
if params.Body.OrganizationFullPath != "" {
r, regexErr := regexp.Compile(`^http(s)?://`)
@@ -167,7 +177,25 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseBadRequestWithError(reqID, msg, err))
}
- result, err := service.AddGitLabOrganization(ctx, params.ProjectSFID, params.Body)
+ // If the parent is TLF, then use the same project SFID value for the parent SFID value
+ parentProjectSFID := parentProjectModel.ID
+ if utils.IsProjectHasRootParent(projectModel) {
+ parentProjectSFID = params.ProjectSFID
+ }
+
+ // Convert the various input parameters and values to an add GitLab Group/Org model
+ inputModel := &common.GitLabAddOrganization{
+ ProjectSFID: params.ProjectSFID,
+ ParentProjectSFID: parentProjectSFID, // could be the same SFID as the project SFID if parent is TLF
+ AutoEnabled: utils.BoolValue(params.Body.AutoEnabled),
+ AutoEnabledClaGroupID: params.Body.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: utils.BoolValue(params.Body.BranchProtectionEnabled),
+ ExternalGroupID: params.Body.GroupID,
+ OrganizationURL: orgURL,
+ OrganizationFullPath: params.Body.OrganizationFullPath,
+ }
+
+ result, err := service.AddGitLabOrganization(ctx, inputModel)
if err != nil {
if _, ok := err.(*utils.ProjectConflict); ok {
return gitlab_organizations.NewAddProjectGitlabOrganizationConflict().WithPayload(
@@ -215,13 +243,20 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Load the project
- psc := project_service.GetClient()
+ psc := projectService.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
return gitlab_organizations.NewUpdateProjectGitlabGroupConfigNotFound().WithPayload(
utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate project with ID: %s", params.ProjectSFID)))
}
+ // Load the project parent
+ parentProjectModel, err := psc.GetParentProjectModel(params.ProjectSFID)
+ if err != nil || parentProjectModel == nil {
+ return gitlab_organizations.NewAddProjectGitlabOrganizationForbidden().WithPayload(
+ utils.ErrorResponseNotFound(reqID, fmt.Sprintf("unable to locate parent project from project with ID: %s", params.ProjectSFID)))
+ }
+
if !utils.IsUserAuthorizedForProjectTree(ctx, authUser, params.ProjectSFID, utils.ALLOW_ADMIN_SCOPE) {
msg := fmt.Sprintf("user %s does not have access to Update Project GitLab Group/Organizations for Project '%s' with scope of %s",
authUser.UserName, projectModel.Name, params.ProjectSFID)
@@ -235,7 +270,17 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
return gitlab_organizations.NewUpdateProjectGitlabGroupConfigBadRequest().WithPayload(utils.ErrorResponseBadRequest(reqID, msg))
}
- updateErr := service.UpdateGitLabOrganization(ctx, params.ProjectSFID, params.GitLabGroupID, "", "", params.Body.AutoEnabled, params.Body.AutoEnabledClaGroupID, params.Body.BranchProtectionEnabled)
+ inputModel := &common.GitLabAddOrganization{
+ ProjectSFID: params.ProjectSFID,
+ ParentProjectSFID: parentProjectModel.ID,
+ AutoEnabled: params.Body.AutoEnabled,
+ AutoEnabledClaGroupID: params.Body.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: params.Body.BranchProtectionEnabled,
+ ExternalGroupID: params.GitLabGroupID,
+ Enabled: true,
+ }
+
+ updateErr := service.UpdateGitLabOrganization(ctx, inputModel)
if updateErr != nil {
if errors.Is(updateErr, projects_cla_groups.ErrCLAGroupDoesNotExist) {
msg := fmt.Sprintf("problem updating GitLab group/organization for project %s with SFID: %s - CLA Group wth ID: %s was not found, error: %+v", projectModel.Name, projectModel.ID, params.Body.AutoEnabledClaGroupID, updateErr)
@@ -289,7 +334,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// Load the project
- psc := project_service.GetClient()
+ psc := projectService.GetClient()
projectModel, err := psc.GetProject(params.ProjectSFID)
if err != nil || projectModel == nil {
return gitlab_organizations.NewDeleteProjectGitlabGroupConfigNotFound().WithPayload(
@@ -368,7 +413,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
}
// now fetch the oauth credentials and store to db
- oauthResp, err := gitlab_api.FetchOauthCredentials(params.Code)
+ oauthResp, err := gitlabApi.FetchOauthCredentials(params.Code)
if err != nil {
msg := fmt.Sprintf("fetching gitlab credentials failed : %s : %v", gitlabOrganizationID, err)
log.WithFields(f).WithError(err).Warn(msg)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index f244da8b3..b12b893ef 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -28,10 +28,12 @@ import (
// indexes
const (
- // GitlabOrgSFIDIndex the index for the SFID
- GitlabOrgSFIDIndex = "gitlab-org-sfid-index"
- // GitlabOrgLowerNameIndex the index for the group/org naem in lower case
- GitlabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
+ // GitLabOrgOrganizationSFIDIndex the index for the Project Parent SFID
+ GitLabOrgOrganizationSFIDIndex = "gitlab-org-sfid-index"
+ // GitLabOrgProjectSFIDIndex the index for the Project SFID
+ GitLabOrgProjectSFIDIndex = "gitlab-project-sfid-index"
+ // GitLabOrgLowerNameIndex the index for the group/org name in lower case
+ GitLabOrgLowerNameIndex = "gitlab-organization-name-lower-search-index"
// GitLabExternalIDIndex the index for the external ID
GitLabExternalIDIndex = "gitlab-external-group-id-index"
// GitLabFullPathIndex the index for the full path
@@ -40,14 +42,14 @@ const (
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
- AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, groupName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
+ AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) (*v2Models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
+ UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) error
DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgFullPath string) error
}
@@ -68,60 +70,59 @@ func NewRepository(awsSession *session.Session, stage string) RepositoryInterfac
}
// AddGitLabOrganization adds the specified values to the GitLab Group/Org table
-func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error) {
+func (repo *Repository) AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "parentProjectSFID": parentProjectSFID,
- "projectSFID": projectSFID,
- "groupID": groupID,
- "organizationName": organizationName,
- "groupFullPath": groupFullPath,
- "autoEnabled": autoEnabled,
- "autoEnabledClaGroupID": autoEnabledClaGroupID,
- "branchProtectionEnabled": branchProtectionEnabled,
+ "parentProjectSFID": input.ParentProjectSFID,
+ "projectSFID": input.ProjectSFID,
+ "groupID": input.ExternalGroupID,
+ "organizationName": input.OrganizationName,
+ "groupFullPath": input.OrganizationFullPath,
+ "autoEnabled": input.AutoEnabled,
+ "autoEnabledClaGroupID": input.AutoEnabledClaGroupID,
+ "branchProtectionEnabled": input.BranchProtectionEnabled,
"enabled": enabled,
}
var existingRecord *common.GitLabOrganization
var getErr error
- if groupID != 0 {
- log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
+ if input.ExternalGroupID != 0 {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", input.ExternalGroupID)
// First, let's check to see if we have an existing gitlab organization with the same name
- existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
+ existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, input.ExternalGroupID)
if getErr != nil {
- log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by ID: %d - ok to create a new record", groupID)
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by ID: %d - ok to create a new record", input.ExternalGroupID)
}
- } else if groupFullPath != "" {
- log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
+ } else if input.OrganizationFullPath != "" {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", input.OrganizationFullPath)
// First, let's check to see if we have an existing gitlab organization with the same name
- existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
+ existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
if getErr != nil {
- log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by full path: %s - ok to create a new record", groupFullPath)
+ log.WithFields(f).WithError(getErr).Debugf("unable to locate existing GitLab group by full path: %s - ok to create a new record", input.OrganizationFullPath)
}
}
if existingRecord != nil {
- log.WithFields(f).Debugf("An existing GitLab organization with ID %d or full path: %s exists in our database", groupID, groupFullPath)
+ log.WithFields(f).Debugf("An existing GitLab organization with ID %d or full path: %s exists in our database", input.ExternalGroupID, input.OrganizationFullPath)
// If everything matches...
- if projectSFID == existingRecord.ProjectSFID {
+ if input.ProjectSFID == existingRecord.ProjectSFID {
log.WithFields(f).Debug("existing GitLab organization with same SFID - should be able to update it")
- updateErr := repo.UpdateGitLabOrganization(ctx, projectSFID, groupID, organizationName, groupFullPath,
- autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, enabled)
+ updateErr := repo.UpdateGitLabOrganization(ctx, input, enabled)
if updateErr != nil {
return nil, updateErr
}
- if groupID > 0 {
+ if input.ExternalGroupID > 0 {
// Return the updated record
- if gitlabOrg, err := repo.GetGitLabOrganizationByExternalID(ctx, groupID); err != nil {
+ if gitlabOrg, err := repo.GetGitLabOrganizationByExternalID(ctx, input.ExternalGroupID); err != nil {
return nil, err
} else {
return common.ToModel(gitlabOrg), nil
}
- } else if groupFullPath != "" {
+ } else if input.OrganizationFullPath != "" {
// Return the updated record
- if gitlabOrg, err := repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath); err != nil {
+ if gitlabOrg, err := repo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath); err != nil {
return nil, err
} else {
return common.ToModel(gitlabOrg), nil
@@ -152,50 +153,50 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, parentProject
OrganizationID: organizationID.String(),
DateCreated: currentTime,
DateModified: currentTime,
- OrganizationName: organizationName,
- OrganizationNameLower: strings.ToLower(organizationName),
- OrganizationFullPath: groupFullPath,
- ExternalGroupID: int(groupID),
- OrganizationSFID: parentProjectSFID,
- ProjectSFID: projectSFID,
+ OrganizationName: input.OrganizationName,
+ OrganizationNameLower: strings.ToLower(input.OrganizationName),
+ OrganizationURL: input.OrganizationURL,
+ OrganizationFullPath: input.OrganizationFullPath,
+ ExternalGroupID: input.ExternalGroupIDAsInt(),
+ OrganizationSFID: input.ParentProjectSFID,
+ ProjectSFID: input.ProjectSFID,
Enabled: enabled,
- AutoEnabled: autoEnabled,
- AutoEnabledClaGroupID: autoEnabledClaGroupID,
- BranchProtectionEnabled: branchProtectionEnabled,
+ AutoEnabled: input.AutoEnabled,
+ AutoEnabledClaGroupID: input.AutoEnabledClaGroupID,
+ BranchProtectionEnabled: input.BranchProtectionEnabled,
AuthState: authStateNonce.String(),
Version: "v1",
- // OrganizationURL: set later when we can authenticate to the API
}
- log.WithFields(f).Debug("Encoding GitLab organization record for adding to the database...")
+ log.WithFields(f).Debug("encoding GitLab organization record for adding to the database...")
av, err := dynamodbattribute.MarshalMap(gitlabOrg)
if err != nil {
log.WithFields(f).WithError(err).Warn("unable to marshall request for query")
return nil, err
}
- log.WithFields(f).Debug("Adding gitlab organization record to the database...")
+ log.WithFields(f).Debug("adding gitlab organization record to the database...")
_, err = repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{
Item: av,
TableName: aws.String(repo.gitlabOrgTableName),
ConditionExpression: aws.String("attribute_not_exists(organization_name)"),
})
if err != nil {
- if aerr, ok := err.(awserr.Error); ok {
- switch aerr.Code() {
+ if aErr, ok := err.(awserr.Error); ok {
+ switch aErr.Code() {
case dynamodb.ErrCodeConditionalCheckFailedException:
- log.WithFields(f).WithError(err).Warn("gitlab organization already exists")
- return nil, fmt.Errorf("gitlab organization already exists")
+ log.WithFields(f).WithError(err).Warn("gitlab group/organization already exists")
+ return nil, fmt.Errorf("gitlab group/organization already exists")
}
}
- log.WithFields(f).WithError(err).Warn("cannot put gitlab organization in dynamodb")
+ log.WithFields(f).WithError(err).Warn("cannot put gitlab group/organization in dynamodb")
return nil, err
}
return common.ToModel(gitlabOrg), nil
}
-// GetGitLabOrganizations get GitLab organizations based on the project SFID
+// GetGitLabOrganizations get GitLab organizations based on the project SFID or parent project SFID
func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizations",
@@ -203,52 +204,56 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
"projectSFID": projectSFID,
}
- condition := expression.Key(GitLabOrganizationsOrganizationSFIDColumn).Equal(expression.Value(projectSFID))
- builder := expression.NewBuilder().WithKeyCondition(condition)
-
- filter := expression.Name("enabled").Equal(expression.Value(true))
- builder = builder.WithFilter(filter)
-
- // Use the nice builder to create the expression
- expr, err := builder.Build()
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("problem building query expression, error: %+v", err)
- return nil, err
+ type getResponseChannelModel struct {
+ Response *v2Models.GitlabOrganizations
+ Error error
}
+ // A channel for the responses from the go routines
+ responseChannel := make(chan *getResponseChannelModel)
- // Assemble the query input parameters
- queryInput := &dynamodb.QueryInput{
- ExpressionAttributeNames: expr.Names(),
- ExpressionAttributeValues: expr.Values(),
- KeyConditionExpression: expr.KeyCondition(),
- ProjectionExpression: expr.Projection(),
- FilterExpression: expr.Filter(),
- TableName: aws.String(repo.gitlabOrgTableName),
- IndexName: aws.String(GitlabOrgSFIDIndex),
- }
-
- results, err := repo.dynamoDBClient.Query(queryInput)
- if err != nil {
- log.WithFields(f).Warnf("error retrieving gitlab_organizations using project_sfid = %s. error = %s", projectSFID, err.Error())
- return nil, err
- }
+ // Search the project SFID column
+ go func(ctx context.Context, projectSFID string) {
+ condition := expression.Key(GitLabOrganizationsProjectSFIDColumn).Equal(expression.Value(projectSFID))
+ filter := expression.Name("enabled").Equal(expression.Value(true))
+ response, err := repo.getOrganizationsWithConditionFilter(ctx, condition, filter, GitLabOrgProjectSFIDIndex)
+ responseChannel <- &getResponseChannelModel{
+ Response: response,
+ Error: err,
+ }
+ }(ctx, projectSFID)
+
+ // Search the organization SFID (parent sfid) column
+ go func(ctx context.Context, projectSFID string) {
+ condition := expression.Key(GitLabOrganizationsOrganizationSFIDColumn).Equal(expression.Value(projectSFID))
+ filter := expression.Name("enabled").Equal(expression.Value(true))
+ response, err := repo.getOrganizationsWithConditionFilter(ctx, condition, filter, GitLabOrgOrganizationSFIDIndex)
+ responseChannel <- &getResponseChannelModel{
+ Response: response,
+ Error: err,
+ }
+ }(ctx, projectSFID)
- if len(results.Items) == 0 {
- log.WithFields(f).Debug("no results from query")
- return &v2Models.GitlabOrganizations{
- List: []*v2Models.GitlabOrganization{},
- }, nil
+ // Fetch the results and combine, no duplicates
+ fullResponse := &v2Models.GitlabOrganizations{
+ List: nil,
}
- var resultOutput []*common.GitLabOrganization
- err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
- if err != nil {
- return nil, err
+ for i := 0; i < 2; i++ {
+ select {
+ case response := <-responseChannel:
+ if response.Error != nil {
+ log.WithFields(f).WithError(response.Error).Warnf("unable to load CLA Group")
+ return nil, response.Error
+ }
+ // Update the full response with the data from the channel
+ updateResponse(fullResponse, response.Response)
+ case <-ctx.Done():
+ log.WithFields(f).WithError(ctx.Err()).Warnf("waiting for GitLab group/organization query timed out")
+ return nil, fmt.Errorf("querying GitLab group/organization failed : %v", ctx.Err())
+ }
}
- log.WithFields(f).Debug("building response model...")
- gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
- return &v2Models.GitlabOrganizations{List: gitlabOrgList}, nil
+ return fullResponse, nil
}
// GetGitLabOrganizationByName get GitLab organization by name
@@ -276,7 +281,7 @@ func (repo *Repository) GetGitLabOrganizationByName(ctx context.Context, gitLabO
ProjectionExpression: expr.Projection(),
FilterExpression: expr.Filter(),
TableName: aws.String(repo.gitlabOrgTableName),
- IndexName: aws.String(GitlabOrgLowerNameIndex),
+ IndexName: aws.String(GitLabOrgLowerNameIndex),
}
log.WithFields(f).Debugf("querying for GitLab organization by name using organization_name_lower=%s...", strings.ToLower(gitLabOrganizationName))
@@ -508,42 +513,43 @@ func (repo *Repository) UpdateGitLabOrganizationAuth(ctx context.Context, organi
}
// UpdateGitLabOrganization updates the GitLab group based on the specified values
-func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error {
+func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) error {
f := logrus.Fields{
"functionName": "gitlab_organizations.repository.UpdateGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "groupID": groupID,
- "groupFullPath": groupFullPath,
- "organizationName": organizationName,
- "autoEnabled": autoEnabled,
- "autoEnabledClaGroupID": autoEnabledClaGroupID,
- "branchProtectionEnabled": branchProtectionEnabled,
+ "projectSFID": input.ProjectSFID,
+ "groupID": input.ExternalGroupID,
+ "groupFullPath": input.OrganizationFullPath,
+ "organizationName": input.OrganizationName,
+ "autoEnabled": input.AutoEnabled,
+ "autoEnabledClaGroupID": input.AutoEnabledClaGroupID,
+ "branchProtectionEnabled": input.BranchProtectionEnabled,
+ "enabled": enabled,
"tableName": repo.gitlabOrgTableName,
}
var existingRecord *common.GitLabOrganization
var getErr error
- if groupID > 0 {
- log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", groupID)
- existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, groupID)
+ if input.ExternalGroupID > 0 {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab organization with ID: %d", input.ExternalGroupID)
+ existingRecord, getErr = repo.GetGitLabOrganizationByExternalID(ctx, input.ExternalGroupID)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v", groupID, groupFullPath)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by ID: %d, error: %+v", input.ExternalGroupID, input.OrganizationFullPath)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
- } else if groupFullPath != "" {
- log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", groupFullPath)
- existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, groupFullPath)
+ } else if input.OrganizationFullPath != "" {
+ log.WithFields(f).Debugf("checking to see if we have an existing GitLab group full path with value: %s", input.OrganizationFullPath)
+ existingRecord, getErr = repo.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
if getErr != nil {
- msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v", groupFullPath, getErr)
+ msg := fmt.Sprintf("unable to locate existing GitLab group by full path: %s, error: %+v", input.OrganizationFullPath, getErr)
log.WithFields(f).WithError(getErr).Warn(msg)
return errors.New(msg)
}
}
if existingRecord == nil {
- msg := fmt.Sprintf("error looking up GitLab group using group ID: %d or full path: %s - no results", groupID, groupFullPath)
+ msg := fmt.Sprintf("error looking up GitLab group using group ID: %d or full path: %s - no results", input.ExternalGroupID, input.OrganizationFullPath)
log.WithFields(f).Warn(msg)
return errors.New(msg)
}
@@ -564,13 +570,13 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
}
expressionAttributeValues := map[string]*dynamodb.AttributeValue{
":ae": {
- BOOL: aws.Bool(autoEnabled),
+ BOOL: aws.Bool(input.AutoEnabled),
},
":aecla": {
- S: aws.String(autoEnabledClaGroupID),
+ S: aws.String(input.AutoEnabledClaGroupID),
},
":bp": {
- BOOL: aws.Bool(branchProtectionEnabled),
+ BOOL: aws.Bool(input.BranchProtectionEnabled),
},
":m": {
S: aws.String(currentTime),
@@ -584,17 +590,17 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
}
updateExpression := "SET #AE = :ae, #AECLA = :aecla, #BP = :bp, #M = :m, #E = :e, #N = :n "
- if organizationName != "" {
+ if input.OrganizationName != "" {
expressionAttributeNames["#N"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
- expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(organizationName)}
+ expressionAttributeValues[":n"] = &dynamodb.AttributeValue{S: aws.String(input.OrganizationName)}
updateExpression = fmt.Sprintf("%s, #N = :n ", updateExpression)
expressionAttributeNames["#NL"] = aws.String(GitLabOrganizationsOrganizationNameColumn)
- expressionAttributeValues[":nl"] = &dynamodb.AttributeValue{S: aws.String(strings.ToLower(organizationName))}
+ expressionAttributeValues[":nl"] = &dynamodb.AttributeValue{S: aws.String(strings.ToLower(input.OrganizationName))}
updateExpression = fmt.Sprintf("%s, #NL = :nl ", updateExpression)
}
- input := &dynamodb.UpdateItemInput{
+ updateItemInput := &dynamodb.UpdateItemInput{
Key: map[string]*dynamodb.AttributeValue{
GitLabOrganizationsOrganizationIDColumn: {
S: aws.String(existingRecord.OrganizationID),
@@ -607,7 +613,7 @@ func (repo *Repository) UpdateGitLabOrganization(ctx context.Context, projectSFI
}
log.WithFields(f).Debugf("updating GitLab organization record: %+v", input)
- _, updateErr := repo.dynamoDBClient.UpdateItem(input)
+ _, updateErr := repo.dynamoDBClient.UpdateItem(updateItemInput)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warnf("unable to update GitLab organization record, error: %+v", updateErr)
return updateErr
@@ -702,13 +708,87 @@ func (repo *Repository) DeleteGitLabOrganizationByFullPath(ctx context.Context,
func buildGitlabOrganizationListModels(ctx context.Context, gitlabOrganizations []*common.GitLabOrganization) []*v2Models.GitlabOrganization {
f := logrus.Fields{
- "functionName": "buildGitlabOrganizationListModels",
+ "functionName": "v2.gitlab_organizations.repository.buildGitlabOrganizationListModels",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
log.WithFields(f).Debugf("fetching gitlab info for the list")
// Convert the database model to a response model
return common.ToModels(gitlabOrganizations)
+}
+
+// getOrganizationsWithConditionFilter fetches the repository entry based on the specified condition and filter criteria
+// using the provided index
+func (repo *Repository) getOrganizationsWithConditionFilter(ctx context.Context, condition expression.KeyConditionBuilder, filter expression.ConditionBuilder, indexName string) (*v2Models.GitlabOrganizations, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.repository.getOrganizationsWithConditionFilter",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "indexName": indexName,
+ }
+
+ builder := expression.NewBuilder().WithKeyCondition(condition).WithFilter(filter)
- // TODO: Fetch the gitlab information
+ // Use the nice builder to create the expression
+ expr, err := builder.Build()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem building query expression, error: %+v", err)
+ return nil, err
+ }
+
+ // Assemble the query input parameters
+ queryInput := &dynamodb.QueryInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ KeyConditionExpression: expr.KeyCondition(),
+ ProjectionExpression: expr.Projection(),
+ FilterExpression: expr.Filter(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ IndexName: aws.String(indexName),
+ }
+
+ log.WithFields(f).Debugf("query: %+v", queryInput)
+ results, err := repo.dynamoDBClient.Query(queryInput)
+ if err != nil {
+ log.WithFields(f).Warnf("problem retrieving gitlab_organizations, error = %s", err.Error())
+ return nil, err
+ }
+
+ if len(results.Items) == 0 {
+ log.WithFields(f).Debug("no results from query")
+ return &v2Models.GitlabOrganizations{
+ List: []*v2Models.GitlabOrganization{},
+ }, nil
+ }
+
+ var resultOutput []*common.GitLabOrganization
+ err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &resultOutput)
+ if err != nil {
+ return nil, err
+ }
+
+ log.WithFields(f).Debugf("building response model for %d results...", len(resultOutput))
+ gitlabOrgList := buildGitlabOrganizationListModels(ctx, resultOutput)
+ return &v2Models.GitlabOrganizations{List: gitlabOrgList}, nil
+}
+
+func updateResponse(fullResponse, response *v2Models.GitlabOrganizations) {
+ if fullResponse.List == nil {
+ fullResponse.List = response.List
+ return
+ }
+
+ if response != nil && response.List != nil {
+ for _, item := range response.List {
+ found := false
+ for _, fr := range fullResponse.List {
+ if fr.OrganizationID == item.OrganizationID {
+ found = true
+ break
+ }
+ }
+ if !found {
+ fullResponse.List = append(fullResponse.List, item)
+ }
+ }
+ }
}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index ba3e06f9d..ee35c1fdb 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -17,10 +17,10 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
"github.com/communitybridge/easycla/cla-backend-go/config"
- gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ gitlabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/go-openapi/strfmt"
- project_service "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
+ projectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
"github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
@@ -33,7 +33,7 @@ import (
// ServiceInterface contains functions of GitlabOrganizations service
type ServiceInterface interface {
- AddGitLabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error)
+ AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*models.GitlabProjectOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
@@ -41,8 +41,8 @@ type ServiceInterface interface {
GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error
- UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error
+ UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) error
+ UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlabApi.OauthSuccessResponse) error
DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID string, gitlabOrgFullPath string) error
}
@@ -51,7 +51,7 @@ type Service struct {
repo RepositoryInterface
v2GitRepoService repositories.ServiceInterface
claGroupRepository projects_cla_groups.Repository
- gitLabApp *gitlab_api.App
+ gitLabApp *gitlabApi.App
}
// NewService creates a new gitlab organization service
@@ -60,19 +60,20 @@ func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceI
repo: repo,
v2GitRepoService: v2GitRepoService,
claGroupRepository: claGroupRepository,
- gitLabApp: gitlab_api.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitlabApi.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
// AddGitLabOrganization adds the specified GitLab organization
-func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string, input *models.GitlabCreateOrganization) (*models.GitlabProjectOrganizations, error) {
+func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
- "projectSFID": projectSFID,
- "autoEnabled": utils.BoolValue(input.AutoEnabled),
- "branchProtectionEnabled": utils.BoolValue(input.BranchProtectionEnabled),
- "groupID": input.GroupID,
+ "projectSFID": input.ProjectSFID,
+ "parentProjectSFID": input.ParentProjectSFID,
+ "autoEnabled": input.AutoEnabled,
+ "branchProtectionEnabled": input.BranchProtectionEnabled,
+ "groupID": input.ExternalGroupID,
"groupFullPath": input.OrganizationFullPath,
}
@@ -85,10 +86,10 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
return nil, getErr
}
}
- if input.GroupID > 0 {
- existingModel, getErr = s.GetGitLabOrganizationByGroupID(ctx, input.GroupID)
+ if input.ExternalGroupID > 0 {
+ existingModel, getErr = s.GetGitLabOrganizationByGroupID(ctx, input.ExternalGroupID)
if getErr != nil {
- log.WithFields(f).WithError(getErr).Warnf("problem querying GitLab group/organization using group ID: %d", input.GroupID)
+ log.WithFields(f).WithError(getErr).Warnf("problem querying GitLab group/organization using group ID: %d", input.ExternalGroupID)
return nil, getErr
}
}
@@ -96,9 +97,9 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
// If we have an existing record/entry
if existingModel != nil {
// Check to make sure another project doesn't own this GitLab Group - only care about conflicts if it is enabled
- if existingModel.ProjectSfid != projectSFID && existingModel.Enabled {
- psc := project_service.GetClient()
- requestedProjectModel, projectLookupErr := psc.GetProject(projectSFID)
+ if existingModel.ProjectSfid != input.ProjectSFID && existingModel.Enabled {
+ psc := projectService.GetClient()
+ requestedProjectModel, projectLookupErr := psc.GetProject(input.ProjectSFID)
if projectLookupErr != nil || requestedProjectModel == nil {
return nil, projectLookupErr
}
@@ -109,7 +110,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
}
msg := fmt.Sprintf("unable to add or update the GitLab Group/Organization - already taken by another project: %s (%s) - unable to add to this project: %s (%s)",
existingProjectModel.Name, existingModel.ProjectSfid,
- requestedProjectModel.Name, projectSFID)
+ requestedProjectModel.Name, input.ProjectSFID)
log.WithFields(f).Warn(msg)
// Return the error model
@@ -117,7 +118,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
Message: "unable to add or update the GitLab Group/Organization - already taken by another project",
ProjectA: utils.ProjectSummary{
Name: requestedProjectModel.Name,
- ID: projectSFID,
+ ID: input.ProjectSFID,
},
ProjectB: utils.ProjectSummary{
Name: existingProjectModel.Name,
@@ -126,49 +127,23 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, projectSFID string,
}
}
- updateErr := s.UpdateGitLabOrganization(ctx, projectSFID, input.GroupID, "", input.OrganizationFullPath, utils.BoolValue(input.AutoEnabled), input.AutoEnabledClaGroupID, utils.BoolValue(input.BranchProtectionEnabled))
+ updateErr := s.UpdateGitLabOrganization(ctx, input)
if updateErr != nil {
log.WithFields(f).WithError(updateErr).Warnf("problem updating GitLab group/organization, error: %+v", updateErr)
return nil, getErr
}
- return s.GetGitLabOrganizations(ctx, projectSFID)
+ return s.GetGitLabOrganizations(ctx, input.ProjectSFID)
}
- psc := v2ProjectService.GetClient()
- project, err := psc.GetProject(projectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warn("problem loading project details from the project service")
- return nil, err
- }
-
- var parentProjectSFID string
- if utils.StringValue(project.Parent) == "" || (project.Foundation != nil &&
- (project.Foundation.Name == utils.TheLinuxFoundation || project.Foundation.Name == utils.LFProjectsLLC)) {
- parentProjectSFID = projectSFID
- } else {
- parentProjectSFID = utils.StringValue(project.Parent)
- }
- f["parentProjectSFID"] = parentProjectSFID
- log.WithFields(f).Debug("located parentProjectID...")
-
log.WithFields(f).Debug("adding GitLab organization...")
- autoEnabled := false
- if input.AutoEnabled != nil {
- autoEnabled = utils.BoolValue(input.AutoEnabled)
- }
- branchProtectionEnabled := false
- if input.BranchProtectionEnabled != nil {
- branchProtectionEnabled = utils.BoolValue(input.BranchProtectionEnabled)
- }
-
- resp, err := s.repo.AddGitLabOrganization(ctx, parentProjectSFID, projectSFID, input.GroupID, "", input.OrganizationFullPath, autoEnabled, input.AutoEnabledClaGroupID, branchProtectionEnabled, true)
+ resp, err := s.repo.AddGitLabOrganization(ctx, input, true)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem adding gitlab organization for project")
return nil, err
}
log.WithFields(f).Debugf("created GitLab organization with ID: %s", resp.OrganizationID)
- return s.GetGitLabOrganizations(ctx, projectSFID)
+ return s.GetGitLabOrganizations(ctx, input.ProjectSFID)
}
// GetGitLabOrganization returns the GitLab organization based on the specified GitLab Organization ID
@@ -334,6 +309,8 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
AutoEnabled: org.AutoEnabled,
AutoEnableClaGroupID: org.AutoEnabledClaGroupID,
AutoEnabledClaGroupName: strings.TrimSpace(autoEnabledCLAGroupName),
+ ProjectSfid: org.ProjectSfid,
+ ParentProjectSfid: org.OrganizationSfid,
OrganizationName: org.OrganizationName,
OrganizationURL: org.OrganizationURL,
OrganizationFullPath: org.OrganizationFullPath,
@@ -348,21 +325,24 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
rorg.ConnectionStatus = utils.NoConnection
} else {
if err != nil {
- log.WithFields(f).Errorf("initializing gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
+ log.WithFields(f).Warnf("initializing gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
rorg.ConnectionStatus = utils.ConnectionFailure
+ rorg.ConnectionStatusMessage = err.Error()
} else {
// We've been authenticated by the user - great, see if we can determine the list of repos...
- glClient, clientErr := gitlab_api.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
+ glClient, clientErr := gitlabApi.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
if clientErr != nil {
- log.WithFields(f).Errorf("using gitlab client for gitlab org : %s failed : %v", org.OrganizationID, clientErr)
+ log.WithFields(f).Warnf("using gitlab client for gitlab group id: %d, internal group/org ID: %s failed: %v", org.OrganizationExternalID, org.OrganizationID, clientErr)
rorg.ConnectionStatus = utils.ConnectionFailure
+ rorg.ConnectionStatusMessage = clientErr.Error()
} else {
rorg.Repositories = s.updateRepositoryStatus(glClient, toGitLabProjectResponse(repoList))
user, _, userErr := glClient.Users.CurrentUser()
if userErr != nil {
- log.WithFields(f).Errorf("using gitlab client for gitlab org : %s failed : %v", org.OrganizationID, userErr)
+ log.WithFields(f).Warnf("using gitlab client for gitlab org: %s failed : %v", org.OrganizationID, userErr)
rorg.ConnectionStatus = utils.ConnectionFailure
+ rorg.ConnectionStatusMessage = userErr.Error()
} else {
log.WithFields(f).Debugf("connected to user : %s for gitlab org : %s", user.Name, org.OrganizationID)
rorg.ConnectionStatus = utils.Connected
@@ -379,9 +359,9 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
sort.Slice(out.List, func(i, j int) bool {
return strings.ToLower(out.List[i].OrganizationName) < strings.ToLower(out.List[j].OrganizationName)
})
- for _, orgList := range out.List {
- sort.Slice(orgList.Repositories, func(i, j int) bool {
- return strings.ToLower(orgList.Repositories[i].RepositoryName) < strings.ToLower(orgList.Repositories[j].RepositoryName)
+ for _, projectOrganization := range out.List {
+ sort.Slice(projectOrganization.Repositories, func(i, j int) bool {
+ return strings.ToLower(projectOrganization.Repositories[i].RepositoryName) < strings.ToLower(projectOrganization.Repositories[j].RepositoryName)
})
}
@@ -411,7 +391,7 @@ func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrgani
}
// UpdateGitLabOrganizationAuth updates the GitLab organization authentication information
-func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlab_api.OauthSuccessResponse) error {
+func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlabApi.OauthSuccessResponse) error {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.UpdateGitLabOrganizationAuth",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -419,7 +399,7 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
log.WithFields(f).Debugf("updating gitlab org auth")
- authInfoEncrypted, err := gitlab_api.EncryptAuthInfo(oauthResp, s.gitLabApp)
+ authInfoEncrypted, err := gitlabApi.EncryptAuthInfo(oauthResp, s.gitLabApp)
if err != nil {
return fmt.Errorf("encrypt failed : %v", err)
}
@@ -430,13 +410,13 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
}
// Get a reference to the GItLab client
- gitLabClient, err := gitlab_api.NewGitlabOauthClientFromAccessToken(oauthResp.AccessToken)
+ gitLabClient, err := gitlabApi.NewGitlabOauthClientFromAccessToken(oauthResp.AccessToken)
if err != nil {
return fmt.Errorf("initializing gitlab client : %v", err)
}
// Query the groups list
- groupsWithMaintainerPerms, groupListErr := gitlab_api.GetGroupsListAll(ctx, gitLabClient, goGitLab.MaintainerPermissions)
+ groupsWithMaintainerPerms, groupListErr := gitlabApi.GetGroupsListAll(ctx, gitLabClient, goGitLab.MaintainerPermissions)
if groupListErr != nil {
return groupListErr
}
@@ -474,20 +454,20 @@ func (s *Service) UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrgani
msg = fmt.Sprintf("full path: '%s'", gitLabOrgModel.OrganizationFullPath)
}
- return fmt.Errorf("unable to locate the provided GitLab group by %s using the provided permissions - discovered %d groups where user has maintainer or above permissions.",
+ return fmt.Errorf("unable to locate the provided GitLab group by %s using the provided permissions - discovered %d groups where user has maintainer or above permissions",
msg, len(groupsWithMaintainerPerms))
}
// UpdateGitLabOrganization updates the GitLab organization
-func (s *Service) UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool) error {
+func (s *Service) UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) error {
// check if valid cla group id is passed
- if autoEnabledClaGroupID != "" {
- if _, err := s.claGroupRepository.GetCLAGroupNameByID(ctx, autoEnabledClaGroupID); err != nil {
+ if input.AutoEnabledClaGroupID != "" {
+ if _, err := s.claGroupRepository.GetCLAGroupNameByID(ctx, input.AutoEnabledClaGroupID); err != nil {
return err
}
}
- return s.repo.UpdateGitLabOrganization(ctx, projectSFID, groupID, organizationName, groupFullPath, autoEnabled, autoEnabledClaGroupID, branchProtectionEnabled, true)
+ return s.repo.UpdateGitLabOrganization(ctx, input, true)
}
// DeleteGitLabOrganizationByFullPath deletes the specified GitLab organization by full path
@@ -563,7 +543,7 @@ func toGitLabProjectResponse(gitLabListRepos *models.GitlabRepositoriesList) []*
var repoList []*models.GitlabProjectRepository
for _, repo := range gitLabListRepos.List {
- parentProjectSFID, err := project_service.GetClient().GetParentProject(repo.RepositoryProjectSfid)
+ parentProjectSFID, err := projectService.GetClient().GetParentProject(repo.RepositoryProjectSfid)
if err != nil {
log.WithFields(f).Warnf("unable to lookup project parent SFID using SFID: %s", repo.RepositoryProjectSfid)
}
diff --git a/cla-backend-go/v2/project-service/client.go b/cla-backend-go/v2/project-service/client.go
index 601627266..b0dd1822b 100644
--- a/cla-backend-go/v2/project-service/client.go
+++ b/cla-backend-go/v2/project-service/client.go
@@ -149,13 +149,9 @@ func (pmm *Client) GetParentProject(projectSFID string) (string, error) {
"apiGWHost": apiGWHost,
}
- // Use our helper function to lookup the parent, if it exists
+ // Use our helper function to find the parent, if it exists
parentModel, err := pmm.GetParentProjectModel(projectSFID)
- if err != nil {
- log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
- return projectSFID, err
- }
- if parentModel == nil {
+ if err != nil || parentModel == nil {
log.WithFields(f).WithError(err).Warnf("unable to lookup parentProjectModel using projectSFID: '%s'", projectSFID)
return projectSFID, err
}
@@ -187,11 +183,11 @@ func (pmm *Client) GetParentProjectModel(projectSFID string) (*models.ProjectOut
return nil, nil
}
- // Does this project they have a parent? projectModel.Parent is deprecated and no longer returned, use project.Foundation.ID/Name attribute instead
- if existingModel.Foundation.Name == utils.TheLinuxFoundation || existingModel.Foundation.Name == utils.LFProjectsLLC {
- log.WithFields(f).Debugf("no parent for projectSFID %s or %s or %s is the parent...", projectSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC)
- return nil, nil
- }
+ //// Does this project they have a parent? projectModel.Parent is deprecated and no longer returned, use project.Foundation.ID/Name attribute instead
+ //if existingModel.Foundation.Name == utils.TheLinuxFoundation || existingModel.Foundation.Name == utils.LFProjectsLLC {
+ // log.WithFields(f).Debugf("no parent for projectSFID %s or %s or %s is the parent...", projectSFID, utils.TheLinuxFoundation, utils.LFProjectsLLC)
+ // return nil, nil
+ //}
// Grab the parent ID once
projectParentSFID := utils.GetProjectParentSFID(existingModel)
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index ccd37c90c..9eaaf2378 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -100,6 +100,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
ProjectSFID: projectSFID,
RepositoryExternalID: repositoryExternalIDString,
RepositoryName: project.PathWithNamespace, // Name column is actually the full path for both GitHub and GitLab
+ RepositoryFullPath: project.PathWithNamespace,
RepositoryURL: project.WebURL,
RepositoryOrganizationName: input.GroupName,
RepositoryCLAGroupID: input.ClaGroupID,
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index fca7deff8..e2411433d 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -68,17 +68,17 @@ type ServiceInterface interface {
GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error
}
-// GithubOrgRepo redefine the interface here to avoid circular dependency issues
-type GithubOrgRepo interface {
- AddGitLabOrganization(ctx context.Context, parentProjectSFID string, projectSFID string, groupID int64, groupName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) (*v2Models.GitlabOrganization, error)
+// GitLabOrgRepo redefine the interface here to avoid circular dependency issues
+type GitLabOrgRepo interface {
+ AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) (*v2Models.GitlabOrganization, error)
GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
GetGitLabOrganizationByFullPath(ctx context.Context, groupFullPath string) (*common.GitLabOrganization, error)
UpdateGitLabOrganizationAuth(ctx context.Context, organizationID string, gitLabGroupID int, authInfo, groupName, groupFullPath, organizationURL string) error
- UpdateGitLabOrganization(ctx context.Context, projectSFID string, groupID int64, organizationName, groupFullPath string, autoEnabled bool, autoEnabledClaGroupID string, branchProtectionEnabled bool, enabled bool) error
- DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgName string) error
+ UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) error
+ DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID, gitlabOrgFullPath string) error
}
// Service is the service model/structure
@@ -87,7 +87,7 @@ type Service struct {
gitV2Repository RepositoryInterface
projectsClaGroupsRepo projects_cla_groups.Repository
ghOrgRepo github_organizations.RepositoryInterface
- glOrgRepo GithubOrgRepo
+ glOrgRepo GitLabOrgRepo
gitLabApp *gitlab_api.App
eventService events.Service
}
@@ -99,7 +99,7 @@ var (
)
// NewService creates a new githubOrganizations service
-func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository RepositoryInterface, pcgRepo projects_cla_groups.Repository, ghOrgRepo github_organizations.RepositoryInterface, glOrgRepo GithubOrgRepo, eventService events.Service) ServiceInterface {
+func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository RepositoryInterface, pcgRepo projects_cla_groups.Repository, ghOrgRepo github_organizations.RepositoryInterface, glOrgRepo GitLabOrgRepo, eventService events.Service) ServiceInterface {
return &Service{
gitV1Repository: gitV1Repository,
gitV2Repository: gitV2Repository,
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index 53adfde2b..f3b6e1438 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -289,6 +289,7 @@ provider:
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/cla-group-id-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-projects-cla-groups/index/foundation-sfid-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-org-sfid-index"
+ - "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index"
- "arn:aws:dynamodb:#{AWS::Region}:#{AWS::AccountId}:table/cla-${opt:stage}-gitlab-orgs/index/gitlab-full-path-index"
@@ -609,7 +610,7 @@ resources:
isProd: { "Fn::Equals": [ "${env:STAGE}", "prod" ] }
isStaging: { "Fn::Equals": [ "${env:STAGE}", "staging" ] }
isDev: { "Fn::Equals": [ "${env:STAGE}", "dev" ] }
- isNotProd: {"Fn::Or": [{"Condition": "isDev"}, {"Condition": "isStaging" }]}
+ isNotProd: { "Fn::Or": [ { "Condition": "isDev" }, { "Condition": "isStaging" } ] }
# true when a TSL certificate should be created by serverless (false created externally)
ShouldGenerateCertificate:
Fn::Not: [ Fn::Equals: [ "${env:STAGE}", "prod" ] ]
From b4f1c84952804bd14f0705ab3e89abbb77485103 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Fri, 27 Aug 2021 09:02:08 -0700
Subject: [PATCH 0468/1276] Feature/GitLab Emp Signature Update (#3208)
- Updated Pynamodb API for User model with gitlab details
- Refactored individual signature docusign flow for gitlab user
- Added emp acknowledgement flow for a gitlab user
Signed-off-by: Harold Wanyama
---
cla-backend/cla/controllers/signing.py | 27 ++--
cla-backend/cla/models/docusign_models.py | 186 +++++-----------------
cla-backend/cla/models/dynamo_models.py | 56 ++++++-
3 files changed, 108 insertions(+), 161 deletions(-)
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index b8cf5f6d0..78a163239 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -11,7 +11,7 @@
import cla
from cla.models import DoesNotExist
-from cla.models.dynamo_models import Signature
+from cla.models.dynamo_models import Signature, User
from cla.user_service import UserService
from cla.utils import get_signing_service, get_signature_instance, get_email_service, \
get_repository_service, get_project_instance, get_company_instance
@@ -36,14 +36,22 @@ def request_individual_signature(project_id, user_id, return_url_type, return_ur
signing_service = get_signing_service()
if return_url_type is not None and return_url_type.lower() == "gerrit":
return signing_service.request_individual_signature_gerrit(str(project_id), str(user_id), return_url)
- elif return_url_type is not None and return_url_type.lower() == "github":
- # fetching the primary for the account
- github = get_repository_service("github")
- primary_user_email = github.get_primary_user_email(request)
- return signing_service.request_individual_signature(str(project_id), str(user_id), return_url,
+ elif return_url_type is not None and (return_url_type.lower() == "github" or return_url_type.lower == "gitlab"):
+ if return_url_type == "github":
+ # fetching the primary for the account
+ github = get_repository_service("github")
+ primary_user_email = github.get_primary_user_email(request)
+ elif return_url_type == "gitlab":
+ try:
+ cla.log.debug(f"Fetching user details for: {user_id}")
+ user = User()
+ user.load(user_id)
+ except DoesNotExist as err:
+ cla.log.warning('Individual Signature - user ID was NOT found for: {}'.format(user_id))
+ return {'errors': {'user_id': str(err)}}
+ primary_user_email = user.get_user_email()
+ return signing_service.request_individual_signature(str(project_id), str(user_id), return_url, return_url_type,
preferred_email=primary_user_email)
- elif return_url_type is not None and return_url_type.lower() == "gitlab":
- return signing_service.request_individual_signature_gitlab(str(project_id), str(user_id), return_url)
def request_corporate_signature(auth_user,
@@ -113,9 +121,10 @@ def request_employee_signature(project_id, company_id, user_id, return_url_type,
cla.log.error(f'{fn} - return type is gerrit - invoking: request_employee_signature_gerrit')
return signing_service.request_employee_signature_gerrit(str(project_id), str(company_id), str(user_id),
return_url)
- elif return_url_type is not None and return_url_type.lower() == "github":
+ elif return_url_type is not None and (return_url_type.lower() == "github" or return_url_type.lower() == "gitlab"):
cla.log.error(f'{fn} - return type is github - invoking: request_employee_signature')
return signing_service.request_employee_signature(str(project_id), str(company_id), str(user_id), return_url)
+
else:
msg = (f'{fn} - unsupported return type {return_url_type} for '
f'cla group: {project_id}, '
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index a742dd9ff..5194b1be9 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -126,147 +126,8 @@ def initialize(self, config):
integrator_key=integrator_key)
self.s3storage = S3Storage()
self.s3storage.initialize(None)
-
- def request_individual_signature_gitlab(self, project_id, user_id, return_url=None, callback_url=None):
- request_info = 'project: {project_id}, user: {user_id} with return_url: {return_url}'.format(
- project_id=project_id, user_id=user_id, return_url=return_url)
- cla.log.debug('Individual Signature - creating new signature for: {}'.format(request_info))
-
- # Ensure this is a valid user
- user_id = str(user_id)
- try:
- user = User()
- user.load(user_id)
- cla.log.debug('Individual Signature - loaded user name: {}, '
- 'user email: {}, gh user: {}, gh id: {}'.
- format(user.get_user_name(), user.get_user_email(), user.get_github_username(),
- user.get_user_github_id()))
- except DoesNotExist as err:
- cla.log.warning('Individual Signature - user ID was NOT found for: {}'.format(request_info))
- return {'errors': {'user_id': str(err)}}
-
- # Ensure the project exists
- try:
- project = Project()
- project.load(project_id)
- cla.log.debug('Individual Signature - loaded project id: {}, name: {}, '.
- format(project.get_project_id(), project.get_project_name()))
- except DoesNotExist as err:
- cla.log.warning('Individual Signature - project ID NOT found for: {}'.format(request_info))
- return {'errors': {'project_id': str(err)}}
-
- # Check for active signature object with this project. If the user has
- # signed the most recent major version, they do not need to sign again.
- cla.log.debug('Individual Signature - loading latest user signature for user: {}, project: {}'.
- format(user, project))
- latest_signature = user.get_latest_signature(str(project_id))
- cla.log.debug('Individual Signature - loaded latest user signature for user: {}, project: {}'.
- format(user, project))
-
- cla.log.debug('Individual Signature - loading latest individual document for project: {}'.
- format(project))
- last_document = project.get_latest_individual_document()
- cla.log.debug('Individual Signature - loaded latest individual document for project: {}'.
- format(project))
-
- cla.log.debug('Individual Signature - creating default individual values for user: {}'.format(user))
- default_cla_values = create_default_individual_values(user)
- cla.log.debug('Individual Signature - created default individual values: {}'.format(default_cla_values))
- # Generate signature callback url
- cla.log.debug('Individual Signature - get active signature metadata')
- signature_metadata = cla.utils.get_active_signature_metadata(user_id)
- cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
-
- cla.log.debug('Individual Signature - get individual signature callback url')
- callback_url = cla.utils.get_individual_signature_callback_url_gitlab(user_id, signature_metadata)
- cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
-
- if latest_signature is not None and \
- last_document.get_document_major_version() == latest_signature.get_signature_document_major_version():
- cla.log.debug('Individual Signature - user already has a signatures with this project: {}'.
- format(latest_signature.get_signature_id()))
-
- # Re-generate and set the signing url - this will update the signature record
- self.populate_sign_url(latest_signature, callback_url, default_values=default_cla_values,
- preferred_email=user.get_user_email())
-
- return {'user_id': user_id,
- 'project_id': project_id,
- 'signature_id': latest_signature.get_signature_id(),
- 'sign_url': latest_signature.get_signature_sign_url()}
- else:
- cla.log.debug('Individual Signature - user does NOT have a signatures with this project: {}'.
- format(project))
-
- # Get signature return URL
- if return_url is None:
- return_url = cla.utils.get_active_signature_return_url(user_id, signature_metadata)
- cla.log.debug('Individual Signature - setting signature return_url to {}'.format(return_url))
-
- if return_url is None:
- cla.log.warning('No active signature found for user - cannot generate '
- 'return_url without knowing where the user came from')
- return {'user_id': str(user_id),
- 'project_id': str(project_id),
- 'signature_id': None,
- 'sign_url': None,
- 'error': 'No active signature found for user - cannot generate return_url without knowing where the user came from'}
-
- # Get latest document
- try:
- cla.log.debug('Individual Signature - loading project latest individual document...')
- document = project.get_latest_individual_document()
- cla.log.debug('Individual Signature - loaded project latest individual document: {}'.format(document))
- except DoesNotExist as err:
- cla.log.warning('Individual Signature - project individual document does NOT exist for: {}'.
- format(request_info))
- return {'errors': {'project_id': project_id, 'message': str(err)}}
-
- # If the CCLA/ICLA template is missing (not created in the project console), we won't have a document
- # return an error
- if not document:
- return {'errors': {'project_id': project_id, 'message': 'missing template document'}}
-
- # Create new Signature object
- cla.log.debug('Individual Signature - creating new signature document '
- 'project_id: {}, user_id: {}, return_url: {}, callback_url: {}'.
- format(project_id, user_id, return_url, callback_url))
- signature = Signature(signature_id=str(uuid.uuid4()),
- signature_project_id=project_id,
- signature_document_major_version=document.get_document_major_version(),
- signature_document_minor_version=document.get_document_minor_version(),
- signature_reference_id=user_id,
- signature_reference_type='user',
- signature_reference_name=user.get_user_name(),
- signature_type='cla',
- signature_return_url_type='Github',
- signature_signed=False,
- signature_approved=True,
- signature_return_url=return_url,
- signature_callback_url=callback_url)
-
- # Set signature ACL
- cla.log.debug('Individual Signature - setting ACL using user GH id: {}'.format(user.get_user_github_id()))
- signature.set_signature_acl('github:{}'.format(user.get_user_github_id()))
-
- # Populate sign url
- self.populate_sign_url(signature, callback_url, default_values=default_cla_values,
- preferred_email=user.get_user_email())
-
- # Save signature
- signature.save()
- cla.log.debug('Individual Signature - Saved signature for: {}'.format(request_info))
-
- response = {'user_id': str(user_id),
- 'project_id': project_id,
- 'signature_id': signature.get_signature_id(),
- 'sign_url': signature.get_signature_sign_url()}
-
- cla.log.debug('Individual Signature - returning response: {}'.format(response))
- return response
-
- def request_individual_signature(self, project_id, user_id, return_url=None, callback_url=None,
+ def request_individual_signature(self, project_id, user_id, return_url=None, return_url_type="github", callback_url=None,
preferred_email=None):
request_info = 'project: {project_id}, user: {user_id} with return_url: {return_url}'.format(
project_id=project_id, user_id=user_id, return_url=return_url)
@@ -319,7 +180,11 @@ def request_individual_signature(self, project_id, user_id, return_url=None, cal
cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
cla.log.debug('Individual Signature - get individual signature callback url')
- callback_url = cla.utils.get_individual_signature_callback_url(user_id, signature_metadata)
+ if return_url_type == "github":
+ callback_url = cla.utils.get_individual_signature_callback_url(user_id, signature_metadata)
+ elif return_url_type == "gitlab":
+ callback_url = cla.utils.get_individual_signature_callback_url_gitlab(user_id, signature_metadata)
+
cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
if latest_signature is not None and \
@@ -387,8 +252,12 @@ def request_individual_signature(self, project_id, user_id, return_url=None, cal
signature_callback_url=callback_url)
# Set signature ACL
- cla.log.debug('Individual Signature - setting ACL using user GH id: {}'.format(user.get_user_github_id()))
- signature.set_signature_acl('github:{}'.format(user.get_user_github_id()))
+ if return_url_type == "github":
+ acl = user.get_user_github_id()
+ elif return_url_type == "gitlab":
+ acl = user.get_user_github_id()
+ cla.log.debug('Individual Signature - setting ACL using user {} id: {}'.format(return_url_type, acl))
+ signature.set_signature_acl('github:{}'.format(acl))
# Populate sign url
self.populate_sign_url(signature, callback_url, default_values=default_cla_values,
@@ -769,7 +638,7 @@ def check_and_prepare_employee_signature(project_id, company_id, user_id) -> dic
return {'success': {'the employee is ready to sign the CCLA'}}
- def request_employee_signature(self, project_id, company_id, user_id, return_url=None):
+ def request_employee_signature(self, project_id, company_id, user_id, return_url=None, return_url_type="github"):
fn = 'docusign_models.check_and_prepare_employee_signature'
request_info = f'cla group: {project_id}, company: {company_id}, user: {user_id} with return_url: {return_url}'
@@ -836,7 +705,10 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
cla.log.info(f'{fn} - created new signature document for: {request_info} - signature: {new_signature}')
# Set signature ACL
- acl_value = f'github:{user.get_user_github_id()}'
+ if return_url_type == "github":
+ acl_value = f'github:{user.get_user_github_id()}'
+ elif return_url_type == "gitlab":
+ acl_value = f'gitlab:{user.get_user_gitlab_id()}'
cla.log.info(f'{fn} - assigning signature acl with value: {acl_value} for: {request_info}')
new_signature.set_signature_acl(acl_value)
@@ -867,12 +739,24 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
github_repository_id = signature_metadata['repository_id']
change_request_id = signature_metadata['pull_request_id']
- # Get repository
- installation_id = cla.utils.get_installation_id_from_github_repository(github_repository_id)
- if installation_id is None:
- return {'errors': {'github_repository_id': 'The given github repository ID does not exist. '}}
+ if return_url_type == "github":
+ # Get repository
+ installation_id = cla.utils.get_installation_id_from_github_repository(github_repository_id)
+ if installation_id is None:
+ return {'errors': {'github_repository_id': 'The given github repository ID does not exist. '}}
+
+ update_repository_provider(installation_id, github_repository_id, change_request_id)
+
+ elif return_url_type == "gitlab":
+ gitlab_repository_id = signature_metadata['repository_id']
+ merge_request_id = signature_metadata['merge_request_id']
+ organization_id = cla.utils.get_organization_id_from_gitlab_repository(gitlab_repository_id)
+ self._update_gitlab_mr(organization_id, gitlab_repository_id, merge_request_id)
+
+ if organization_id is None:
+ return {'errors': {'gitlab_repository_id': 'The given github repository ID does not exist. '}}
+
- update_repository_provider(installation_id, github_repository_id, change_request_id)
cla.utils.delete_active_signature_metadata(user_id)
else:
cla.log.info(f'{fn} - cla group requires ICLA signature from employee - PR has been left unchanged')
@@ -1807,6 +1691,8 @@ def signed_individual_callback_gitlab(self, content, user_id, organization_id, g
cla.log.warning(msg)
return
+ # Remove the active signature metadata.
+ cla.utils.delete_active_signature_metadata(user.get_user_id())
# Get signed document
document_data = self.get_signed_document(envelope_id, user)
diff --git a/cla-backend/cla/models/dynamo_models.py b/cla-backend/cla/models/dynamo_models.py
index 73fdc9404..21d5a69e1 100644
--- a/cla-backend/cla/models/dynamo_models.py
+++ b/cla-backend/cla/models/dynamo_models.py
@@ -169,6 +169,34 @@ class Meta:
# This attribute is the hash key for the index.
user_github_username = UnicodeAttribute(hash_key=True)
+class GitLabIDIndex(GlobalSecondaryIndex):
+ """
+ This class represents a global secondary index for querying users by github username.
+ """
+
+ class Meta:
+ index_name = "gitlab-id-index"
+ write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
+ read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
+ projection = AllProjection()
+
+ # This attribute is the hash key for the index.
+ user_gitlab_id = UnicodeAttribute(hash_key=True)
+
+class GitLabUsernameIndex(GlobalSecondaryIndex):
+ """
+ This class represents a global secondary index for querying users by github username.
+ """
+
+ class Meta:
+ index_name = "gitlab-username-index"
+ write_capacity_units = int(cla.conf["DYNAMO_WRITE_UNITS"])
+ read_capacity_units = int(cla.conf["DYNAMO_READ_UNITS"])
+ projection = AllProjection()
+
+ # This attribute is the hash key for the index.
+ user_gitlab_username = UnicodeAttribute(hash_key=True)
+
class LFUsernameIndex(GlobalSecondaryIndex):
"""
@@ -1495,6 +1523,10 @@ class Meta:
user_github_id = NumberAttribute(null=True)
user_github_username = UnicodeAttribute(null=True)
user_github_username_index = GitHubUsernameIndex()
+ user_gitlab_id = NumberAttribute(null=True)
+ user_gitlab_username = UnicodeAttribute(null=True)
+ user_gitlab_id_index = GitLabIDIndex()
+ user_github_username_index = GitLabUsernameIndex()
user_ldap_id = UnicodeAttribute(null=True)
user_github_id_index = GitHubUserIndex()
github_user_external_id_index = GithubUserExternalIndex()
@@ -1516,6 +1548,8 @@ def __init__(
user_external_id=None,
user_github_id=None,
user_github_username=None,
+ user_gitlab_id=None,
+ user_gitlab_username=None,
user_ldap_id=None,
lf_username=None,
lf_sub=None,
@@ -1539,12 +1573,14 @@ def __init__(
self.model.user_company_id = user_company_id
self.model.note = note
self._preferred_email = preferred_email
+ self.model.user_gitlab_id = user_gitlab_id
+ self.model.user_gitlab_username = user_gitlab_username
def __str__(self):
return (
"id: {}, username: {}, gh id: {}, gh username: {}, "
"lf email: {}, emails: {}, ldap id: {}, lf username: {}, "
- "user company id: {}, note: {}, user external id: {}"
+ "user company id: {}, note: {}, user external id: {}, user gitlab id: {}, user gitlab username: {}"
).format(
self.model.user_id,
self.model.user_github_username,
@@ -1556,7 +1592,9 @@ def __str__(self):
self.model.lf_username,
self.model.user_company_id,
self.model.note,
- self.model.user_external_id
+ self.model.user_external_id,
+ self.model.user_gitlab_id,
+ self.model.user_gitlab_username,
)
def to_dict(self):
@@ -1565,6 +1603,8 @@ def to_dict(self):
ret["user_github_id"] = None
if ret["user_ldap_id"] == "null":
ret["user_ldap_id"] = None
+ if ret["user_gitlab_id"] == "null":
+ ret["user_gitlab_id"] = None
return ret
def log_info(self, msg):
@@ -1661,6 +1701,12 @@ def get_user_github_id(self):
def get_github_username(self):
return self.model.user_github_username
+
+ def get_user_gitlab_id(self):
+ return self.model.user_gitlab_id
+
+ def get_user_gitlab_username(self):
+ return self.model.user_gitlab_username
def get_user_github_username(self):
"""
@@ -1716,6 +1762,12 @@ def set_user_github_id(self, user_github_id):
def set_user_github_username(self, user_github_username):
self.model.user_github_username = user_github_username
+
+ def set_user_gitlab_id(self, user_gitlab_id):
+ self.model.user_gitlab_id = user_gitlab_id
+
+ def set_user_gitlab_username(self, user_gitlab_username):
+ self.model.user_gitlab_username = user_gitlab_username
def set_note(self, note):
self.model.note = note
From 51ed1b51d990633271c23c4512ae3ada289d44ff Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 27 Aug 2021 15:36:35 -0700
Subject: [PATCH 0469/1276] Added GitLab Repository Check Lambda (#3209)
- Added lambda logic
- Added Makefile build steps
- Updated serverless deployment
- Updated circle CI/CD build and deployment
Signed-off-by: David Deal
---
.circleci/config.yml | 3 +
cla-backend-go/.gitignore | 31 +--
cla-backend-go/Makefile | 19 +-
.../cmd/gitlab_repository_check/README.md | 21 ++
.../handler/handler.go | 237 ++++++++++++++++++
.../handler/server_aws_lambda.go | 22 ++
.../handler/server_standalone.go | 25 ++
.../cmd/gitlab_repository_check/main.go | 11 +
cla-backend-go/events/event_data.go | 55 ++++
cla-backend-go/events/event_types.go | 1 +
cla-backend-go/utils/context.go | 14 ++
cla-backend-go/utils/lambda.go | 26 ++
cla-backend-go/utils/properties.go | 28 +++
.../v2/gitlab_organizations/handlers.go | 4 +-
.../v2/gitlab_organizations/repository.go | 89 ++++++-
.../v2/gitlab_organizations/service.go | 153 +++++++----
.../v2/repositories/gitlab_services.go | 54 +++-
cla-backend-go/v2/repositories/repository.go | 33 +++
cla-backend-go/v2/repositories/service.go | 10 +-
cla-backend/serverless.yml | 18 ++
20 files changed, 763 insertions(+), 91 deletions(-)
create mode 100644 cla-backend-go/cmd/gitlab_repository_check/README.md
create mode 100644 cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
create mode 100644 cla-backend-go/cmd/gitlab_repository_check/handler/server_aws_lambda.go
create mode 100644 cla-backend-go/cmd/gitlab_repository_check/handler/server_standalone.go
create mode 100644 cla-backend-go/cmd/gitlab_repository_check/main.go
create mode 100644 cla-backend-go/utils/lambda.go
create mode 100644 cla-backend-go/utils/properties.go
diff --git a/.circleci/config.yml b/.circleci/config.yml
index ad3195388..72af5e1cf 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -278,6 +278,7 @@ jobs:
- cla-backend-go/dynamo-events-lambda
- cla-backend-go/zipbuilder-scheduler-lambda
- cla-backend-go/zipbuilder-lambda
+ - cla-backend-go/gitlab-repository-check-lambda
- cla-backend-go/functional-tests
buildGoBackendCommon:
@@ -353,6 +354,7 @@ jobs:
cp ~/cla-backend-go/dynamo-events-lambda ~/project/cla-backend/
cp ~/cla-backend-go/zipbuilder-scheduler-lambda ~/project/cla-backend/
cp ~/cla-backend-go/zipbuilder-lambda ~/project/cla-backend/
+ cp ~/cla-backend-go/gitlab-repository-check-lambda ~/project/cla-backend/
ls -alF ~/project/cla-backend/
pushd ~/project/cla-backend
@@ -366,6 +368,7 @@ jobs:
if [[ ! -f dynamo-events-lambda ]]; then echo "Missing dynamo-events-lambda binary file. Exiting..."; exit 1; fi
if [[ ! -f zipbuilder-lambda ]]; then echo "Missing zipbuilder-lambda binary file. Exiting..."; exit 1; fi
if [[ ! -f zipbuilder-scheduler-lambda ]]; then echo "Missing zipbuilder-scheduler-lambda binary file. Exiting..."; exit 1; fi
+ if [[ ! -f gitlab-repository-check-lambda ]]; then echo "Missing gitlab-repository-check-lambda binary file. Exiting..."; exit 1; fi
if [[ ! -f serverless.yml ]]; then echo "Missing serverless.yml file. Exiting..."; exit 1; fi
if [[ ! -f serverless-authorizer.yml ]]; then echo "Missing serverless-authorizer.yml file. Exiting..."; exit 1; fi
yarn sls deploy --force --stage ${STAGE} --region us-east-1
diff --git a/cla-backend-go/.gitignore b/cla-backend-go/.gitignore
index 9dc1cd0b5..19cb1e7ba 100644
--- a/cla-backend-go/.gitignore
+++ b/cla-backend-go/.gitignore
@@ -4,27 +4,16 @@
cla-backend-go
cla
cla-mac*
-backend-aws-lambda
-backend-aws-lambda-mac
-backend-aws-lambda-linux
-user-subscribe-lambda
-user-subscribe-lambda-mac
-build-user-subscribe-lambda-linux
-build-user-subscribe-lambda-mac
-metrics-aws-lambda
-metrics-aws-lambda-mac
-metrics-report-lambda
-metrics-report-lambda-mac
-functional-tests
-functional-tests-linux
-functional-tests-mac
-dynamo-events-lambda
-dynamo-events-lambda-mac
-dynamo-events-lambda-linux
-zipbuilder-lambda
-zipbuilder-lambda-mac
-zipbuilder-scheduler-lambda-mac
-zipbuilder-scheduler-lambda
+backend-aws-lambda*
+user-subscribe-lambda*
+build-user-subscribe-lambda*
+metrics-aws-lambda*
+metrics-report-lambda*
+functional-tests*
+dynamo-events-lambda*
+zipbuilder-lambda*
+zipbuilder-scheduler-lambda*
+gitlab-repository-check-lambda*
*env.json
db/schema.sql
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 89cfb5ab2..439414290 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -8,6 +8,7 @@ METRICS_REPORT_BIN = metrics-report-lambda
DYNAMO_EVENTS_BIN = dynamo-events-lambda
ZIPBUILDER_SCHEDULER_BIN = zipbuilder-scheduler-lambda
ZIPBUILDER_BIN = zipbuilder-lambda
+GITLAB_REPO_CHECK_BIN = gitlab-repository-check-lambda
FUNCTIONAL_TESTS_BIN = functional-tests
USER_SUBSCRIBE_BIN = user-subscribe-lambda
MAKEFILE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
@@ -27,12 +28,12 @@ GO_FILES=$(shell find . -type f -name '*.go' -not -path './vendor/*')
.PHONY: generate setup tool-setup setup-dev setup-deploy clean-all clean swagger up fmt test run deps build build-mac build-aws-lambda user-subscribe-lambda qc lint
all: all-mac
-all-mac: clean swagger deps fmt build-mac build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac test lint
-all-linux: clean swagger deps fmt build-linux build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux test lint
+all-mac: clean swagger deps fmt build-mac build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac build-gitlab-repository-check-lambda-mac test lint
+all-linux: clean swagger deps fmt build-linux build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux build-gitlab-repository-check-lambda-linux test lint
lambdas-mac: build-aws-lambda-mac
-build-lambdas-mac: build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-metrics-report-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac
+build-lambdas-mac: build-aws-lambda-mac build-user-subscribe-lambda-mac build-metrics-lambda-mac build-metrics-report-lambda-mac build-dynamo-events-lambda-mac build-zipbuilder-scheduler-lambda-mac build-zipbuilder-lambda-mac build-gitlab-repository-check-lambda-mac
lambdas: build-lambdas-linux
-build-lambdas-linux: build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-metrics-report-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux
+build-lambdas-linux: build-aws-lambda-linux build-user-subscribe-lambda-linux build-metrics-lambda-linux build-metrics-report-lambda-linux build-dynamo-events-lambda-linux build-zipbuilder-scheduler-lambda-linux build-zipbuilder-lambda-linux build-gitlab-repository-check-lambda-linux
generate: swagger
@@ -298,6 +299,16 @@ build-zipbuilder-lambda-mac: deps
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(ZIPBUILDER_BIN)-mac cmd/zipbuilder_lambda/main.go
@chmod +x $(ZIPBUILDER_BIN)-mac
+build-gitlab-repository-check-lambda-linux: deps
+ @echo "Building a statically linked Linux OSX amd64 binary..."
+ env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(GITLAB_REPO_CHECK_BIN) cmd/gitlab_repository_check/main.go
+ @chmod +x $(GITLAB_REPO_CHECK_BIN)
+
+build-gitlab-repository-check-lambda-mac: deps
+ @echo "Building a statically linked Mac OSX amd64 binary..."
+ env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(GITLAB_REPO_CHECK_BIN)-mac cmd/gitlab_repository_check/main.go
+ @chmod +x $(GITLAB_REPO_CHECK_BIN)-mac
+
build-functional-tests: build-functional-tests-linux
build-functional-tests-linux: deps
@echo "Building Functional Tests for Linux amd64 binary..."
diff --git a/cla-backend-go/cmd/gitlab_repository_check/README.md b/cla-backend-go/cmd/gitlab_repository_check/README.md
new file mode 100644
index 000000000..5571beeed
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab_repository_check/README.md
@@ -0,0 +1,21 @@
+# GitLab Repository Check Lambda
+
+GitLab (currently) does not support sending callback/webhook events for GitLab project add or delete events. As a
+result, we created a small lambda that runs periodically to check for any new GitLab project
+(repository) add or deletes.
+
+The process/algorithm is:
+
+1. Query our database for registered GitLab Groups - filter by the enabled flag is true and where the Auto Enable flag
+ is true
+1. For each GitLab group in our database...
+ 1. Create a new GitLab API client instance using the authorization token for the Git Group
+ 1. Query the GitLab API for the project list under the group (include sub-groups). This grabs the list of current GitLab projects under the GitLab group.
+ 1. Query for GitLab project in DB matching this GitLab group path
+ 1. Identify deltas - this identifies how many new and deleted GitLap projects we need to process
+ 1. If any new GitLab projects, add to the DB, set enabled, create an event log
+ 1. If any removed/deleted GitLab projects, remove from the DB, create an event log
+
+## References
+
+- [GitLab Feature request discussion thread](https://gitlab.com/gitlab-com/marketing/community-relations/opensource-program/linux-foundation/-/issues/4#note_653255564)
diff --git a/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
new file mode 100644
index 000000000..ca8aabdab
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
@@ -0,0 +1,237 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package handler
+
+import (
+ "context"
+ "strconv"
+
+ v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
+ "github.com/communitybridge/easycla/cla-backend-go/events"
+ "github.com/communitybridge/easycla/cla-backend-go/gerrits"
+ "github.com/communitybridge/easycla/cla-backend-go/github"
+ "github.com/communitybridge/easycla/cla-backend-go/github_organizations"
+ gitLabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ ini "github.com/communitybridge/easycla/cla-backend-go/init"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/project"
+ "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
+ v1Repositories "github.com/communitybridge/easycla/cla-backend-go/repositories"
+ "github.com/communitybridge/easycla/cla-backend-go/users"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
+ v2Repositories "github.com/communitybridge/easycla/cla-backend-go/v2/repositories"
+
+ "strings"
+
+ "github.com/sirupsen/logrus"
+ goGitLab "github.com/xanzy/go-gitlab"
+)
+
+// Init initializes the handler
+func Init() {
+ f := logrus.Fields{
+ "functionName": "cmd.gitlab_repository_check.handler.Init",
+ }
+ ctx := utils.NewContext()
+ f[utils.XREQUESTID] = ctx.Value(utils.XREQUESTID)
+ log.WithFields(f).Debug("initializing...")
+
+ // General initialization
+ ini.Init()
+ ini.GetConfig()
+}
+
+// Handler is invoked each time the lambda is triggered - https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html
+func Handler(ctx context.Context) error {
+ f := logrus.Fields{
+ "functionName": "cmd.update-project-statistics.Handler",
+ }
+
+ // Add the x-request-id to the context
+ ctx = utils.NewContextFromParent(ctx)
+ f[utils.XREQUESTID] = ctx.Value(utils.XREQUESTID)
+ stage := strings.ToLower(utils.GetProperty("STAGE"))
+ f["stage"] = stage
+
+ awsSession, err := ini.GetAWSSession()
+ if err != nil {
+ log.WithFields(f).WithError(err).Panic("Unable to load AWS session")
+ }
+
+ configFile := ini.GetConfig()
+
+ // initialize GitHub
+ github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
+ // initialize GitLab
+ gitLabApp := gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
+
+ // Repository Layer
+ usersRepo := users.NewRepository(awsSession, stage)
+ eventsRepo := events.NewRepository(awsSession, stage)
+ v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
+ gerritRepo := gerrits.NewRepository(awsSession, stage)
+ v1ProjectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
+ gitV1Repository := v1Repositories.NewRepository(awsSession, stage)
+ gitV2Repository := v2Repositories.NewRepository(awsSession, stage)
+ githubOrganizationsRepo := github_organizations.NewRepository(awsSession, stage)
+ gitlabOrganizationRepo := gitlab_organizations.NewRepository(awsSession, stage)
+ v1CLAGroupRepo := project.NewRepository(awsSession, stage, gitV1Repository, gerritRepo, v1ProjectClaGroupRepo)
+
+ // Service Layer
+
+ type combinedRepo struct {
+ users.UserRepository
+ v1Company.IRepository
+ project.ProjectRepository
+ projects_cla_groups.Repository
+ }
+
+ // Our service layer handlers
+ eventsService := events.NewService(eventsRepo, combinedRepo{
+ usersRepo,
+ v1CompanyRepo,
+ v1CLAGroupRepo,
+ v1ProjectClaGroupRepo,
+ })
+
+ v2RepositoriesService := v2Repositories.NewService(gitV1Repository, gitV2Repository, v1ProjectClaGroupRepo, githubOrganizationsRepo, gitlabOrganizationRepo, eventsService)
+ //gitlabOrganizationsService := gitlab_organizations.NewService(gitlabOrganizationRepo, v2RepositoriesService, v1ProjectClaGroupRepo)
+
+ // Query GitLab Groups
+ // for each group
+ // if enabled and auto-enabled = true
+ // load token and client
+ // query for GitLab API repository list
+ // query for GitLab repositories in DB for this group path
+ // identify deltas
+ // if new, add to DB, create event log
+ // if deleted, remove from DB, create event log
+
+ gitLabGroups, err := gitlabOrganizationRepo.GetGitLabOrganizationsEnabledWithAutoEnabled(ctx)
+ if err != nil {
+ log.WithFields(f).WithError(err).Warnf("problem querying for GitLab group/organizations that are enabled with auto-enabled flag set to true")
+ return err
+ }
+
+ log.WithFields(f).Debugf("start - checking %d GitLab projects for add/delete events", len(gitLabGroups.List))
+ for _, gitLabGroup := range gitLabGroups.List {
+ gitLabClient, gitLabClientErr := gitLabApi.NewGitlabOauthClient(gitLabGroup.AuthInfo, gitLabApp)
+ if gitLabClientErr != nil {
+ log.WithFields(f).WithError(gitLabClientErr).Warnf("problem loading GitLab client for group/organization: %s", gitLabGroup.OrganizationURL)
+ return gitLabClientErr
+ }
+
+ gitLabProjects, getGitLabAPIError := gitLabApi.GetGroupProjectListByGroupID(ctx, gitLabClient, int(gitLabGroup.OrganizationExternalID))
+ if getGitLabAPIError != nil {
+ log.WithFields(f).WithError(getGitLabAPIError).Warnf("problem loading GitLab projects for group/organization: %s using the groupID: %d - skipping GitLab Group/Organziation", gitLabGroup.OrganizationFullPath, gitLabGroup.OrganizationExternalID)
+ continue
+ }
+ gitLabDBProjects, getProjectListDBErr := gitV2Repository.GitLabGetRepositoriesByNamePrefix(ctx, gitLabGroup.OrganizationFullPath)
+ if getProjectListDBErr != nil {
+ log.WithFields(f).WithError(getProjectListDBErr).Warnf("problem loading GitLab projects for group/organization: %s from the database - skipping GitLab Group/Organziation", gitLabGroup.OrganizationFullPath)
+ continue
+ }
+
+ newGitLabProjects := getNewProjects(gitLabProjects, gitLabDBProjects)
+ log.WithFields(f).Debugf("Found %d GitLab projects/repositories that were added for GitLab Group: %s", len(newGitLabProjects), gitLabGroup.OrganizationFullPath)
+ if len(newGitLabProjects) > 0 {
+ var gitLabProjectIDList []int64
+
+ // Build a quick list of the GitLab Project/repo ID values - the add repositories takes a list
+ for _, newGitLabProject := range newGitLabProjects {
+ gitLabProjectIDList = append(gitLabProjectIDList, int64(newGitLabProject.ID))
+ }
+
+ // Add the repositories - will generate a log event
+ _, addErr := v2RepositoriesService.GitLabAddRepositoriesWithEnabledFlag(ctx, gitLabGroup.ProjectSfid, &v2Repositories.GitLabAddRepoModel{
+ ClaGroupID: gitLabGroup.AutoEnabledClaGroupID,
+ GroupName: gitLabGroup.OrganizationName,
+ ExternalID: gitLabGroup.OrganizationExternalID,
+ GroupFullPath: gitLabGroup.OrganizationFullPath,
+ ProjectIDList: gitLabProjectIDList,
+ }, true) // set to enabled when adding since this was added as a result of the auto-enable feature
+ if addErr != nil {
+ log.WithFields(f).WithError(addErr).Warnf("problem adding GitLab projects for group/organization: %s to the database", gitLabGroup.OrganizationFullPath)
+ } else {
+ log.WithFields(f).Debugf("added %d GitLab projects for group/organization: %s to the database", len(newGitLabProjects), gitLabGroup.OrganizationFullPath)
+ }
+ }
+
+ deletedGitLabProjects := getDeletedProjects(gitLabProjects, gitLabDBProjects)
+ log.WithFields(f).Debugf("Found %d GitLab projects/repositories that were removed from the GitLab Group: %s", len(newGitLabProjects), gitLabGroup.OrganizationFullPath)
+ if len(deletedGitLabProjects) > 0 {
+ for _, gitLabProjectDBRecord := range deletedGitLabProjects {
+ repositoryExternalID, parseIntErr := strconv.ParseInt(gitLabProjectDBRecord.RepositoryExternalID, 10, 64)
+ if parseIntErr != nil {
+ log.WithFields(f).WithError(parseIntErr).Warnf("problem converting repository %s external ID string value: %s to integer", gitLabProjectDBRecord.RepositoryFullPath, gitLabProjectDBRecord.RepositoryExternalID)
+ } else {
+ deleteErr := v2RepositoriesService.GitLabDeleteRepositoryByExternalID(ctx, repositoryExternalID)
+ if deleteErr != nil {
+ log.WithFields(f).WithError(deleteErr).Warnf("problem deleting repository %s external ID string value: %s to integer", gitLabProjectDBRecord.RepositoryFullPath, gitLabProjectDBRecord.RepositoryExternalID)
+ } else {
+ log.WithFields(f).Debugf("deleted GitLab project %s for group/organization: %s from the database", gitLabProjectDBRecord.RepositoryName, gitLabGroup.OrganizationFullPath)
+ }
+ }
+ }
+ }
+ }
+
+ log.WithFields(f).Debugf("done - checked %d GitLab projects for add/delete events", len(gitLabGroups.List))
+ return nil
+}
+
+// getNewProjects is a helper function to determine if we have any new GitLab projects that are not in our database
+func getNewProjects(gitLabProjects []*goGitLab.Project, gitLabDBProjects []*v1Repositories.RepositoryDBModel) []*goGitLab.Project {
+ var response []*goGitLab.Project
+ // For each GitLab Project/Repo
+ for _, gitLabProject := range gitLabProjects {
+ found := false
+
+ // For each GitLab Project/Repo in the database
+ for _, gitLabDBProject := range gitLabDBProjects {
+ // Compare the full name/path
+ if strings.ToLower(gitLabProject.PathWithNamespace) == strings.ToLower(gitLabDBProject.RepositoryFullPath) {
+ found = true
+ break
+ }
+ }
+
+ // Didn't find the GitLab Project Repo from GitLab defined in our database - must have been added!
+ if !found {
+ // Add to our list
+ response = append(response, gitLabProject)
+ }
+ }
+
+ return response
+}
+
+// getDeletedProjects is a helper function to determine if we have any new GitLab projects that were removed from GitLab but are still in our database
+func getDeletedProjects(gitLabProjects []*goGitLab.Project, gitLabDBProjects []*v1Repositories.RepositoryDBModel) []*v1Repositories.RepositoryDBModel {
+ var response []*v1Repositories.RepositoryDBModel
+ // For each GitLab Project/Repo in the database
+ for _, gitLabDBProject := range gitLabDBProjects {
+ found := false
+
+ // For each GitLab Project/Repo
+ for _, gitLabProject := range gitLabProjects {
+ // Compare the full name/path
+ if strings.ToLower(gitLabProject.PathWithNamespace) == strings.ToLower(gitLabDBProject.RepositoryFullPath) {
+ found = true
+ break
+ }
+
+ }
+
+ // Didn't find the GitLab Project Repo from the database defined in GitLab - must have been removed!
+ if !found {
+ // Add to our list
+ response = append(response, gitLabDBProject)
+ }
+ }
+
+ return response
+}
diff --git a/cla-backend-go/cmd/gitlab_repository_check/handler/server_aws_lambda.go b/cla-backend-go/cmd/gitlab_repository_check/handler/server_aws_lambda.go
new file mode 100644
index 000000000..2300ca676
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab_repository_check/handler/server_aws_lambda.go
@@ -0,0 +1,22 @@
+// +build aws_lambda
+
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package handler
+
+import (
+ "github.com/aws/aws-lambda-go/lambda"
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/sirupsen/logrus"
+)
+
+// RunHandler starts the lambda main handler routine
+func RunHandler() {
+ f := logrus.Fields{
+ "functionName": "cmd.gitlab_repository_check.handler.RunHandler",
+ }
+ log.WithFields(f).Info("lambda server starting...")
+ lambda.Start(Handler)
+ log.WithFields(f).Infof("Lambda shutting down...")
+}
diff --git a/cla-backend-go/cmd/gitlab_repository_check/handler/server_standalone.go b/cla-backend-go/cmd/gitlab_repository_check/handler/server_standalone.go
new file mode 100644
index 000000000..1141d2942
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab_repository_check/handler/server_standalone.go
@@ -0,0 +1,25 @@
+// +build !aws_lambda
+
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package handler
+
+import (
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/communitybridge/easycla/cla-backend-go/utils"
+ "github.com/sirupsen/logrus"
+)
+
+// RunHandler starts the lambda in local testing model by invoking the handler directly
+func RunHandler() {
+ f := logrus.Fields{
+ "functionName": "cmd.gitlab_repository_check.handler.RunHandler",
+ }
+ log.WithFields(f).Debug("creating a new handler")
+ err := Handler(utils.NewContext())
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("error returned from handler")
+ }
+ log.Infof("handler completed")
+}
diff --git a/cla-backend-go/cmd/gitlab_repository_check/main.go b/cla-backend-go/cmd/gitlab_repository_check/main.go
new file mode 100644
index 000000000..5b2723e97
--- /dev/null
+++ b/cla-backend-go/cmd/gitlab_repository_check/main.go
@@ -0,0 +1,11 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package main
+
+import "github.com/communitybridge/easycla/cla-backend-go/cmd/gitlab_repository_check/handler"
+
+func main() {
+ handler.Init()
+ handler.RunHandler()
+}
diff --git a/cla-backend-go/events/event_data.go b/cla-backend-go/events/event_data.go
index 653cc6644..6ef709283 100644
--- a/cla-backend-go/events/event_data.go
+++ b/cla-backend-go/events/event_data.go
@@ -48,6 +48,12 @@ type RepositoryDisabledEventData struct {
RepositoryExternalID int64
}
+// RepositoryDeletedEventData event data model
+type RepositoryDeletedEventData struct {
+ RepositoryName string
+ RepositoryExternalID int64
+}
+
// RepositoryRenamedEventData event data model
type RepositoryRenamedEventData struct {
NewRepositoryName string
@@ -505,6 +511,29 @@ func (ed *RepositoryDisabledEventData) GetEventDetailsString(args *LogEventArgs)
return data, true
}
+// GetEventDetailsString returns the details string for this event
+func (ed *RepositoryDeletedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
+ data := "The GitHub repository " // nolint
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if ed.RepositoryName != "" {
+ data = data + fmt.Sprintf(" with repository name %s", ed.RepositoryName)
+ }
+ if ed.RepositoryExternalID > 0 {
+ data = data + fmt.Sprintf(" with repository external ID %d", ed.RepositoryExternalID)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectSFID)
+ }
+ data = data + " was deleted"
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventDetailsString returns the details string for this event
func (ed *RepositoryRenamedEventData) GetEventDetailsString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository renamed from %s to %s for the project %s", ed.OldRepositoryName, ed.NewRepositoryName, args.ProjectName)
@@ -1503,6 +1532,32 @@ func (ed *RepositoryDisabledEventData) GetEventSummaryString(args *LogEventArgs)
return data, true
}
+// GetEventSummaryString returns the summary string for this event
+func (ed *RepositoryDeletedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
+ data := "The GitHub repository " // nolint
+ if args.CLAGroupName != "" {
+ data = data + fmt.Sprintf(" for the CLA Group %s", args.CLAGroupName)
+ }
+ if ed.RepositoryName != "" {
+ data = data + fmt.Sprintf(" with repository name %s", ed.RepositoryName)
+ }
+ if ed.RepositoryExternalID > 0 {
+ data = data + fmt.Sprintf(" with repository external ID %d", ed.RepositoryExternalID)
+ }
+ if args.ProjectSFID != "" {
+ data = data + fmt.Sprintf(" for the project %s", args.ProjectSFID)
+ }
+ data = data + " was deleted"
+ if args.CompanyName != "" {
+ data = data + fmt.Sprintf(" for the company %s", args.CompanyName)
+ }
+ if args.UserName != "" {
+ data = data + fmt.Sprintf(" by the user %s", args.UserName)
+ }
+ data = data + "."
+ return data, true
+}
+
// GetEventSummaryString returns the summary string for this event
func (ed *RepositoryRenamedEventData) GetEventSummaryString(args *LogEventArgs) (string, bool) {
data := fmt.Sprintf("The GitHub repository was renamed from %s to %s", ed.OldRepositoryName, ed.NewRepositoryName)
diff --git a/cla-backend-go/events/event_types.go b/cla-backend-go/events/event_types.go
index 85cffeea7..9ad8537f7 100644
--- a/cla-backend-go/events/event_types.go
+++ b/cla-backend-go/events/event_types.go
@@ -35,6 +35,7 @@ const (
RepositoryRenamed = "repository.renamed"
RepositoryTransferred = "repository.transferred"
RepositoryDisabled = "repository.disabled"
+ RepositoryDeleted = "repository.deleted"
RepositoryUpdated = "repository.updated"
RepositoryBranchProtectionAdded = "repository.branchprotection.updated"
RepositoryBranchProtectionDisabled = "repository.branchprotection.updated"
diff --git a/cla-backend-go/utils/context.go b/cla-backend-go/utils/context.go
index 49e612a72..f9e092015 100644
--- a/cla-backend-go/utils/context.go
+++ b/cla-backend-go/utils/context.go
@@ -42,6 +42,20 @@ func NewContextWithUser(authUser *auth.User) context.Context {
return context.WithValue(context.WithValue(context.Background(), XREQUESTID, requestID), CtxAuthUser, authUser) // nolint
}
+// NewContextFromParent returns a new context object with a new request ID based on the parent
+func NewContextFromParent(ctx context.Context) context.Context {
+ f := logrus.Fields{
+ "functionName": "utils.NewContext",
+ }
+ requestID, err := uuid.NewV4()
+ if err != nil {
+ log.WithFields(f).WithError(err).Warn("unable to generate a UUID for x-request-id")
+ return context.Background()
+ }
+
+ return context.WithValue(ctx, XREQUESTID, requestID.String()) // nolint
+}
+
// ContextWithRequestAndUser returns a new context with the specified request ID and user
func ContextWithRequestAndUser(ctx context.Context, reqID string, authUser *auth.User) context.Context {
return context.WithValue(context.WithValue(ctx, XREQUESTID, reqID), CtxAuthUser, authUser) // nolint
diff --git a/cla-backend-go/utils/lambda.go b/cla-backend-go/utils/lambda.go
new file mode 100644
index 000000000..c449c9a1a
--- /dev/null
+++ b/cla-backend-go/utils/lambda.go
@@ -0,0 +1,26 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/aws/aws-lambda-go/events"
+)
+
+//GetHTTPOKResponse : return Get HTTP Success Response
+func GetHTTPOKResponse(ctx context.Context) events.APIGatewayProxyResponse {
+ resp := events.APIGatewayProxyResponse{
+ StatusCode: 200,
+ IsBase64Encoded: false,
+ Body: "",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ XREQUESTID: fmt.Sprintf("%+v", ctx.Value(XREQUESTID)),
+ "X-MyCompany-Func-Reply": "hello-handler",
+ },
+ }
+ return resp
+}
diff --git a/cla-backend-go/utils/properties.go b/cla-backend-go/utils/properties.go
new file mode 100644
index 000000000..410759e69
--- /dev/null
+++ b/cla-backend-go/utils/properties.go
@@ -0,0 +1,28 @@
+// Copyright The Linux Foundation and each contributor to CommunityBridge.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ log "github.com/communitybridge/easycla/cla-backend-go/logging"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+)
+
+// GetProperty is a common routine to bind and return the specified environment variable
+func GetProperty(property string) string {
+ f := logrus.Fields{
+ "functionName": "utils.properties.GetProperty",
+ }
+ err := viper.BindEnv(property)
+ if err != nil {
+ log.WithFields(f).WithError(err).Fatalf("unable to load property: %s - value not defined or empty", property)
+ }
+
+ value := viper.GetString(property)
+ if value == "" {
+ log.WithFields(f).WithError(err).Fatalf("property: %s cannot be empty", property)
+ }
+
+ return value
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/handlers.go b/cla-backend-go/v2/gitlab_organizations/handlers.go
index 7d147b366..ebb38e675 100644
--- a/cla-backend-go/v2/gitlab_organizations/handlers.go
+++ b/cla-backend-go/v2/gitlab_organizations/handlers.go
@@ -67,7 +67,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
utils.ErrorResponseForbidden(reqID, msg))
}
- result, err := service.GetGitLabOrganizations(ctx, params.ProjectSFID)
+ result, err := service.GetGitLabOrganizationsByProjectSFID(ctx, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("Gitlab organization with project SFID not found: %s", params.ProjectSFID)
@@ -304,7 +304,7 @@ func Configure(api *operations.EasyclaAPI, service ServiceInterface, eventServic
},
})
- results, err := service.GetGitLabOrganizations(ctx, params.ProjectSFID)
+ results, err := service.GetGitLabOrganizationsByProjectSFID(ctx, params.ProjectSFID)
if err != nil {
if strings.ContainsAny(err.Error(), "getProjectNotFound") {
msg := fmt.Sprintf("Gitlab organization with project SFID not found: %s", params.ProjectSFID)
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index b12b893ef..d8ca8abf8 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -43,7 +43,10 @@ const (
// RepositoryInterface is interface for gitlab org data model
type RepositoryInterface interface {
AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) (*v2Models.GitlabOrganization, error)
- GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganizations(ctx context.Context) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganizationsEnabled(ctx context.Context) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganizationsEnabledWithAutoEnabled(ctx context.Context) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganizationsByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
@@ -196,10 +199,31 @@ func (repo *Repository) AddGitLabOrganization(ctx context.Context, input *common
return common.ToModel(gitlabOrg), nil
}
-// GetGitLabOrganizations get GitLab organizations based on the project SFID or parent project SFID
-func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error) {
+// GetGitLabOrganizations returns the complete list of GitLab groups/organizations
+func (repo *Repository) GetGitLabOrganizations(ctx context.Context) (*v2Models.GitlabOrganizations, error) {
+ // No filter, return all
+ return repo.getScanResults(ctx, nil)
+}
+
+// GetGitLabOrganizationsEnabled returns the list of GitLab groups/organizations that are enabled
+func (repo *Repository) GetGitLabOrganizationsEnabled(ctx context.Context) (*v2Models.GitlabOrganizations, error) {
+ // Build the scan/query expression
+ filter := expression.Name(GitLabOrganizationsEnabledColumn).Equal(expression.Value(true))
+ return repo.getScanResults(ctx, &filter)
+}
+
+// GetGitLabOrganizationsEnabledWithAutoEnabled returns the list of GitLab groups/organizations that are enabled with the auto enabled flag set to true
+func (repo *Repository) GetGitLabOrganizationsEnabledWithAutoEnabled(ctx context.Context) (*v2Models.GitlabOrganizations, error) {
+ // Build the scan/query expression
+ filter := expression.Name(GitLabOrganizationsEnabledColumn).Equal(expression.Value(true)).
+ And(expression.Name(GitLabOrganizationsAutoEnabledColumn).Equal(expression.Value(true)))
+ return repo.getScanResults(ctx, &filter)
+}
+
+// GetGitLabOrganizationsByProjectSFID get GitLab organizations based on the project SFID or parent project SFID
+func (repo *Repository) GetGitLabOrganizationsByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizations",
+ "functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizationsByProjectSFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -214,7 +238,7 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
// Search the project SFID column
go func(ctx context.Context, projectSFID string) {
condition := expression.Key(GitLabOrganizationsProjectSFIDColumn).Equal(expression.Value(projectSFID))
- filter := expression.Name("enabled").Equal(expression.Value(true))
+ filter := expression.Name(GitLabOrganizationsEnabledColumn).Equal(expression.Value(true))
response, err := repo.getOrganizationsWithConditionFilter(ctx, condition, filter, GitLabOrgProjectSFIDIndex)
responseChannel <- &getResponseChannelModel{
Response: response,
@@ -225,7 +249,7 @@ func (repo *Repository) GetGitLabOrganizations(ctx context.Context, projectSFID
// Search the organization SFID (parent sfid) column
go func(ctx context.Context, projectSFID string) {
condition := expression.Key(GitLabOrganizationsOrganizationSFIDColumn).Equal(expression.Value(projectSFID))
- filter := expression.Name("enabled").Equal(expression.Value(true))
+ filter := expression.Name(GitLabOrganizationsEnabledColumn).Equal(expression.Value(true))
response, err := repo.getOrganizationsWithConditionFilter(ctx, condition, filter, GitLabOrgOrganizationSFIDIndex)
responseChannel <- &getResponseChannelModel{
Response: response,
@@ -792,3 +816,56 @@ func updateResponse(fullResponse, response *v2Models.GitlabOrganizations) {
}
}
}
+
+func (repo *Repository) getScanResults(ctx context.Context, filter *expression.ConditionBuilder) (*v2Models.GitlabOrganizations, error) {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.repository.GetGitLabOrganizations",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ builder := expression.NewBuilder()
+
+ // Add the filter if provided
+ if filter != nil {
+ builder.WithFilter(*filter)
+ }
+
+ // Build the scan/query expression
+ expr, builderErr := builder.Build()
+ if builderErr != nil {
+ log.WithFields(f).Warnf("error building expression for %s scan, error: %v", repo.gitlabOrgTableName, builderErr)
+ return nil, builderErr
+ }
+
+ scanInput := &dynamodb.ScanInput{
+ ExpressionAttributeNames: expr.Names(),
+ ExpressionAttributeValues: expr.Values(),
+ FilterExpression: expr.Filter(),
+ ProjectionExpression: expr.Projection(),
+ TableName: aws.String(repo.gitlabOrgTableName),
+ }
+
+ var resultList []map[string]*dynamodb.AttributeValue
+ for {
+ results, scanErr := repo.dynamoDBClient.Scan(scanInput) //nolint
+ if scanErr != nil {
+ log.WithFields(f).Warnf("error retrieving scan results from table %s, error: %v", repo.gitlabOrgTableName, scanErr)
+ return nil, scanErr
+ }
+ resultList = append(resultList, results.Items...)
+ if len(results.LastEvaluatedKey) != 0 {
+ scanInput.ExclusiveStartKey = results.LastEvaluatedKey
+ } else {
+ break
+ }
+ }
+
+ var gitlabOrganizations *v2Models.GitlabOrganizations
+ unmarshalErr := dynamodbattribute.UnmarshalListOfMaps(resultList, &gitlabOrganizations)
+ if unmarshalErr != nil {
+ log.Warnf("error unmarshalling %s from database. error: %v", repo.gitlabOrgTableName, unmarshalErr)
+ return nil, unmarshalErr
+ }
+
+ return gitlabOrganizations, nil
+}
diff --git a/cla-backend-go/v2/gitlab_organizations/service.go b/cla-backend-go/v2/gitlab_organizations/service.go
index ee35c1fdb..cbd1e3031 100644
--- a/cla-backend-go/v2/gitlab_organizations/service.go
+++ b/cla-backend-go/v2/gitlab_organizations/service.go
@@ -22,7 +22,7 @@ import (
projectService "github.com/communitybridge/easycla/cla-backend-go/v2/project-service"
- "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
+ v2Models "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups"
"github.com/communitybridge/easycla/cla-backend-go/utils"
@@ -33,14 +33,17 @@ import (
// ServiceInterface contains functions of GitlabOrganizations service
type ServiceInterface interface {
- AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*models.GitlabProjectOrganizations, error)
- GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*models.GitlabOrganization, error)
+ AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*v2Models.GitlabProjectOrganizations, error)
+ GetGitLabOrganization(ctx context.Context, gitLabOrganizationID string) (*v2Models.GitlabOrganization, error)
GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizationID string) (*common.GitLabOrganization, error)
- GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error)
- GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*models.GitlabOrganization, error)
- GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*models.GitlabOrganization, error)
- GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error)
- GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error)
+ GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*v2Models.GitlabOrganization, error)
+ GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*v2Models.GitlabOrganization, error)
+ GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*v2Models.GitlabOrganization, error)
+ GetGitLabOrganizations(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error)
+ GetGitLabOrganizationsEnabled(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error)
+ GetGitLabOrganizationsEnabledWithAutoEnabled(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error)
+ GetGitLabOrganizationsByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabProjectOrganizations, error)
+ GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*v2Models.GitlabOrganization, error)
UpdateGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) error
UpdateGitLabOrganizationAuth(ctx context.Context, gitLabOrganizationID string, oauthResp *gitlabApi.OauthSuccessResponse) error
DeleteGitLabOrganizationByFullPath(ctx context.Context, projectSFID string, gitlabOrgFullPath string) error
@@ -55,7 +58,7 @@ type Service struct {
}
// NewService creates a new gitlab organization service
-func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceInterface, claGroupRepository projects_cla_groups.Repository) *Service {
+func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceInterface, claGroupRepository projects_cla_groups.Repository) ServiceInterface {
return &Service{
repo: repo,
v2GitRepoService: v2GitRepoService,
@@ -65,7 +68,7 @@ func NewService(repo RepositoryInterface, v2GitRepoService repositories.ServiceI
}
// AddGitLabOrganization adds the specified GitLab organization
-func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*models.GitlabProjectOrganizations, error) {
+func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization) (*v2Models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.AddGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -77,7 +80,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLa
"groupFullPath": input.OrganizationFullPath,
}
- var existingModel *models.GitlabOrganization
+ var existingModel *v2Models.GitlabOrganization
var getErr error
if input.OrganizationFullPath != "" {
existingModel, getErr = s.GetGitLabOrganizationByFullPath(ctx, input.OrganizationFullPath)
@@ -132,7 +135,7 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLa
log.WithFields(f).WithError(updateErr).Warnf("problem updating GitLab group/organization, error: %+v", updateErr)
return nil, getErr
}
- return s.GetGitLabOrganizations(ctx, input.ProjectSFID)
+ return s.GetGitLabOrganizationsByProjectSFID(ctx, input.ProjectSFID)
}
log.WithFields(f).Debug("adding GitLab organization...")
@@ -143,11 +146,11 @@ func (s *Service) AddGitLabOrganization(ctx context.Context, input *common.GitLa
}
log.WithFields(f).Debugf("created GitLab organization with ID: %s", resp.OrganizationID)
- return s.GetGitLabOrganizations(ctx, input.ProjectSFID)
+ return s.GetGitLabOrganizationsByProjectSFID(ctx, input.ProjectSFID)
}
// GetGitLabOrganization returns the GitLab organization based on the specified GitLab Organization ID
-func (s *Service) GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*v2Models.GitlabOrganization, error) {
dbModel, err := s.GetGitLabOrganizationByID(ctx, gitlabOrganizationID)
if err != nil {
return nil, err
@@ -178,7 +181,7 @@ func (s *Service) GetGitLabOrganizationByID(ctx context.Context, gitLabOrganizat
}
// GetGitLabOrganizationByName returns the gitlab organization based on the Group/Org name
-func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByName",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -195,7 +198,7 @@ func (s *Service) GetGitLabOrganizationByName(ctx context.Context, gitLabOrganiz
}
// GetGitLabOrganizationByFullPath returns the GitLab group/organization using the specified full path
-func (s *Service) GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrganizationFullPath string) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByFullPath",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -215,7 +218,7 @@ func (s *Service) GetGitLabOrganizationByFullPath(ctx context.Context, gitLabOrg
}
// GetGitLabOrganizationByGroupID returns the GitLab group/organization using the specified group ID
-func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGroupID int64) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationByGroupID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -235,10 +238,55 @@ func (s *Service) GetGitLabOrganizationByGroupID(ctx context.Context, gitLabGrou
return common.ToModel(dbModel), nil
}
-// GetGitLabOrganizations returns a collection of GitLab organizations based on the specified project SFID value
-func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string) (*models.GitlabProjectOrganizations, error) {
+// GetGitLabOrganizations returns the complete list of GitLab groups/organizations
+func (s *Service) GetGitLabOrganizations(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error) {
+ gitLabOrganizations, err := s.repo.GetGitLabOrganizations(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Our response model
+ out := &v2Models.GitlabProjectOrganizations{
+ List: s.toGitLabProjectOrganizationList(ctx, gitLabOrganizations),
+ }
+
+ return out, nil
+}
+
+// GetGitLabOrganizationsEnabled returns the list of GitLab groups/organizations that are enabled
+func (s *Service) GetGitLabOrganizationsEnabled(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error) {
+ gitLabOrganizations, err := s.repo.GetGitLabOrganizationsEnabled(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Our response model
+ out := &v2Models.GitlabProjectOrganizations{
+ List: s.toGitLabProjectOrganizationList(ctx, gitLabOrganizations),
+ }
+
+ return out, nil
+}
+
+// GetGitLabOrganizationsEnabledWithAutoEnabled returns the list of GitLab groups/organizations that are enabled with the auto enabled flag set to true
+func (s *Service) GetGitLabOrganizationsEnabledWithAutoEnabled(ctx context.Context) (*v2Models.GitlabProjectOrganizations, error) {
+ gitLabOrganizations, err := s.repo.GetGitLabOrganizationsEnabledWithAutoEnabled(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Our response model
+ out := &v2Models.GitlabProjectOrganizations{
+ List: s.toGitLabProjectOrganizationList(ctx, gitLabOrganizations),
+ }
+
+ return out, nil
+}
+
+// GetGitLabOrganizationsByProjectSFID returns a collection of GitLab organizations based on the specified project SFID value
+func (s *Service) GetGitLabOrganizationsByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabProjectOrganizations, error) {
f := logrus.Fields{
- "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizations",
+ "functionName": "v2.gitlab_organizations.service.GetGitLabOrganizationsByProjectSFID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"projectSFID": projectSFID,
}
@@ -262,7 +310,7 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
// Load the GitLab Organization and Repository details - result will be missing CLA Group info and ProjectSFID details
log.WithFields(f).Debugf("loading Gitlab organizations for projectSFID: %s", projectSFID)
- orgList, err := s.repo.GetGitLabOrganizations(ctx, projectSFID)
+ orgList, err := s.repo.GetGitLabOrganizationsByProjectSFID(ctx, projectSFID)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem loading gitlab organizations from the project service")
return nil, err
@@ -270,12 +318,23 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
log.WithFields(f).Debugf("loaded %d Gitlab organizations for projectSFID: %s", len(orgList.List), projectSFID)
// Our response model
- out := &models.GitlabProjectOrganizations{
- List: make([]*models.GitlabProjectOrganization, 0),
+ out := &v2Models.GitlabProjectOrganizations{
+ List: s.toGitLabProjectOrganizationList(ctx, orgList),
}
- orgMap := make(map[string]*models.GitlabProjectOrganization)
- for _, org := range orgList.List {
+ return out, nil
+}
+
+func (s *Service) toGitLabProjectOrganizationList(ctx context.Context, dbModels *v2Models.GitlabOrganizations) []*v2Models.GitlabProjectOrganization {
+ f := logrus.Fields{
+ "functionName": "v2.gitlab_organizations.service.toGitLabProjectOrganizationList",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ }
+
+ var response []*v2Models.GitlabProjectOrganization
+
+ orgMap := make(map[string]*v2Models.GitlabProjectOrganization)
+ for _, org := range dbModels.List {
autoEnabledCLAGroupName := ""
if org.AutoEnabledClaGroupID != "" {
log.WithFields(f).Debugf("loading CLA Group by ID: %s to obtain the name for GitLab auth enabled CLA Group response", org.AutoEnabledClaGroupID)
@@ -298,14 +357,14 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
log.WithFields(f).Debugf("filtering repositories based on group path: %s", org.OrganizationFullPath)
repoList, repoErr := s.v2GitRepoService.GitLabGetRepositoriesByNamePrefix(ctx, fmt.Sprintf("%s/", org.OrganizationFullPath))
if repoErr != nil {
- if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
- log.WithFields(f).WithError(repoErr).Debugf("no GitLab repositories onboarded for project : %s", projectSFID)
+ if _, ok := repoErr.(*utils.GitLabRepositoryNotFound); ok {
+ log.WithFields(f).WithError(repoErr).Debugf("no GitLab repositories onboarded for group/organization : %s", org.OrganizationFullPath)
} else {
- log.WithFields(f).WithError(repoErr).Debugf("unexpected error while fetching GitLab group repositories for project: %s, error type: %T, error: %v", projectSFID, repoErr, repoErr)
+ log.WithFields(f).WithError(repoErr).Debugf("unexpected error while fetching GitLab group repositories for group/organization path: %s, error type: %T, error: %v", org.OrganizationFullPath, repoErr, repoErr)
}
}
- rorg := &models.GitlabProjectOrganization{
+ rorg := &v2Models.GitlabProjectOrganization{
AutoEnabled: org.AutoEnabled,
AutoEnableClaGroupID: org.AutoEnabledClaGroupID,
AutoEnabledClaGroupName: strings.TrimSpace(autoEnabledCLAGroupName),
@@ -317,17 +376,17 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
OrganizationExternalID: org.OrganizationExternalID,
InstallationURL: buildInstallationURL(org.OrganizationID, orgDetailed.AuthState),
BranchProtectionEnabled: org.BranchProtectionEnabled,
- ConnectionStatus: "", // updated below
- Repositories: []*models.GitlabProjectRepository{}, // updated below
+ ConnectionStatus: "", // updated below
+ Repositories: []*v2Models.GitlabProjectRepository{}, // updated below
}
if orgDetailed.AuthInfo == "" {
rorg.ConnectionStatus = utils.NoConnection
} else {
- if err != nil {
- log.WithFields(f).Warnf("initializing gitlab client for gitlab org : %s failed : %v", org.OrganizationID, err)
+ if repoErr != nil {
+ log.WithFields(f).Warnf("initializing gitlab client for gitlab org: %s failed : %v", org.OrganizationID, repoErr)
rorg.ConnectionStatus = utils.ConnectionFailure
- rorg.ConnectionStatusMessage = err.Error()
+ rorg.ConnectionStatusMessage = repoErr.Error()
} else {
// We've been authenticated by the user - great, see if we can determine the list of repos...
glClient, clientErr := gitlabApi.NewGitlabOauthClient(orgDetailed.AuthInfo, s.gitLabApp)
@@ -352,24 +411,24 @@ func (s *Service) GetGitLabOrganizations(ctx context.Context, projectSFID string
}
orgMap[org.OrganizationName] = rorg
- out.List = append(out.List, rorg)
+ response = append(response, rorg)
}
// Sort everything nicely
- sort.Slice(out.List, func(i, j int) bool {
- return strings.ToLower(out.List[i].OrganizationName) < strings.ToLower(out.List[j].OrganizationName)
+ sort.Slice(response, func(i, j int) bool {
+ return strings.ToLower(response[i].OrganizationName) < strings.ToLower(response[j].OrganizationName)
})
- for _, projectOrganization := range out.List {
+ for _, projectOrganization := range response {
sort.Slice(projectOrganization.Repositories, func(i, j int) bool {
return strings.ToLower(projectOrganization.Repositories[i].RepositoryName) < strings.ToLower(projectOrganization.Repositories[j].RepositoryName)
})
}
- return out, nil
+ return response
}
// GetGitLabOrganizationByState returns the GitLab organization by the auth state
-func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*models.GitlabOrganization, error) {
+func (s *Service) GetGitLabOrganizationByState(ctx context.Context, gitLabOrganizationID, authState string) (*v2Models.GitlabOrganization, error) {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.GetGitLabOrganization",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -532,23 +591,23 @@ func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI
return &installationURL
}
-func toGitLabProjectResponse(gitLabListRepos *models.GitlabRepositoriesList) []*models.GitlabProjectRepository {
+func toGitLabProjectResponse(gitLabListRepos *v2Models.GitlabRepositoriesList) []*v2Models.GitlabProjectRepository {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.toGitLabProjectResponse",
}
if gitLabListRepos == nil {
- return []*models.GitlabProjectRepository{}
+ return []*v2Models.GitlabProjectRepository{}
}
- var repoList []*models.GitlabProjectRepository
+ var repoList []*v2Models.GitlabProjectRepository
for _, repo := range gitLabListRepos.List {
parentProjectSFID, err := projectService.GetClient().GetParentProject(repo.RepositoryProjectSfid)
if err != nil {
log.WithFields(f).Warnf("unable to lookup project parent SFID using SFID: %s", repo.RepositoryProjectSfid)
}
- repoList = append(repoList, &models.GitlabProjectRepository{
+ repoList = append(repoList, &v2Models.GitlabProjectRepository{
ClaGroupID: repo.RepositoryClaGroupID,
//ConnectionStatus: "", // set via another function
Enabled: repo.Enabled,
@@ -566,13 +625,13 @@ func toGitLabProjectResponse(gitLabListRepos *models.GitlabRepositoriesList) []*
}
// updateRepositoryStatus is a helper function to set/add the repository connection status
-func (s *Service) updateRepositoryStatus(glClient *goGitLab.Client, gitLabProjectRepos []*models.GitlabProjectRepository) []*models.GitlabProjectRepository {
+func (s *Service) updateRepositoryStatus(glClient *goGitLab.Client, gitLabProjectRepos []*v2Models.GitlabProjectRepository) []*v2Models.GitlabProjectRepository {
f := logrus.Fields{
"functionName": "v2.gitlab_organizations.service.updateRepositoryStatus",
}
if gitLabProjectRepos == nil {
- return []*models.GitlabProjectRepository{}
+ return []*v2Models.GitlabProjectRepository{}
}
type responseChannelModel struct {
@@ -586,7 +645,7 @@ func (s *Service) updateRepositoryStatus(glClient *goGitLab.Client, gitLabProjec
opts := &goGitLab.GetProjectOptions{}
for _, repo := range gitLabProjectRepos {
// Create a go routine to this concurrently
- go func(glClient *goGitLab.Client, repo *models.GitlabProjectRepository) {
+ go func(glClient *goGitLab.Client, repo *v2Models.GitlabProjectRepository) {
projectModel, resp, lookupErr := glClient.Projects.GetProject(int(repo.RepositoryGitlabID), opts) // OK to convert int64 to int as it is the ID and probably should be typed as a int anyway
if lookupErr != nil {
log.WithFields(f).WithError(lookupErr).Warnf("problem loading GitLab project by external ID: %d, error: %v", repo.RepositoryGitlabID, lookupErr)
diff --git a/cla-backend-go/v2/repositories/gitlab_services.go b/cla-backend-go/v2/repositories/gitlab_services.go
index 9eaaf2378..dfaebce45 100644
--- a/cla-backend-go/v2/repositories/gitlab_services.go
+++ b/cla-backend-go/v2/repositories/gitlab_services.go
@@ -17,7 +17,7 @@ import (
v2GitLabOrg "github.com/communitybridge/easycla/cla-backend-go/v2/common"
- gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ gitLabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/sirupsen/logrus"
@@ -26,8 +26,13 @@ import (
repoModels "github.com/communitybridge/easycla/cla-backend-go/repositories"
)
-// GitLabAddRepositories service function
+// GitLabAddRepositories add a lst of GitLab repositories to the collection - default is not enabled/used/active by a CLA Group
func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string, input *GitLabAddRepoModel) (*v2Models.GitlabRepositoriesList, error) {
+ return s.GitLabAddRepositoriesWithEnabledFlag(ctx, projectSFID, input, false)
+}
+
+// GitLabAddRepositoriesWithEnabledFlag add a lst of GitLab repositories to the collection
+func (s *Service) GitLabAddRepositoriesWithEnabledFlag(ctx context.Context, projectSFID string, input *GitLabAddRepoModel, enabled bool) (*v2Models.GitlabRepositoriesList, error) {
f := logrus.Fields{
"functionName": "v2.repositories.gitlab_services.GitLabAddRepositories",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
@@ -65,7 +70,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
log.WithFields(f).Debugf("successfully loaded GitLab group/organization")
// Get the client
- gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitLabApi.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
if err != nil {
return nil, fmt.Errorf("initializing GitLab client : %v", err)
}
@@ -81,7 +86,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
for _, gitLabProjectID := range input.ProjectIDList {
go func(gitLabProjectID int) {
log.WithFields(f).Debugf("loading GitLab project from GitLab using projectID: %d...", gitLabProjectID)
- project, getProjectErr := gitlab_api.GetProjectByID(ctx, gitLabClient, gitLabProjectID)
+ project, getProjectErr := gitLabApi.GetProjectByID(ctx, gitLabClient, gitLabProjectID)
if getProjectErr != nil {
newErr := fmt.Errorf("unable to load GitLab project using ID: %d, error: %v", gitLabProjectID, getProjectErr)
log.WithFields(f).WithError(newErr)
@@ -105,7 +110,7 @@ func (s *Service) GitLabAddRepositories(ctx context.Context, projectSFID string,
RepositoryOrganizationName: input.GroupName,
RepositoryCLAGroupID: input.ClaGroupID,
RepositoryType: utils.GitLabLower, // should always be gitlab
- Enabled: false, // we don't enable by default
+ Enabled: enabled,
}
repoModel, addErr := s.gitV2Repository.GitLabAddRepository(ctx, projectSFID, inputDBModel)
@@ -172,7 +177,7 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
}
// Get the client
- gitLabClient, err := gitlab_api.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
+ gitLabClient, err := gitLabApi.NewGitlabOauthClient(gitLabOrgModel.AuthInfo, s.gitLabApp)
if err != nil {
return nil, fmt.Errorf("initializing gitlab client : %v", err)
}
@@ -184,7 +189,7 @@ func (s *Service) GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel
}
// Query the project list by organization name
- projectList, projectListErr := gitlab_api.GetGroupProjectListByGroupID(ctx, gitLabClient, gitLabOrgModel.ExternalGroupID)
+ projectList, projectListErr := gitLabApi.GetGroupProjectListByGroupID(ctx, gitLabClient, gitLabOrgModel.ExternalGroupID)
if projectListErr != nil {
return nil, projectListErr
}
@@ -399,6 +404,40 @@ func (s *Service) GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath
return s.gitV2Repository.GitLabDeleteRepositories(ctx, gitLabGroupPath)
}
+// GitLabDeleteRepositoryByExternalID deletes the specified repository
+func (s *Service) GitLabDeleteRepositoryByExternalID(ctx context.Context, gitLabExternalID int64) error {
+ // Load the record - needed for the event log after we delete
+ record, getErr := s.gitV2Repository.GitLabGetRepositoryByExternalID(ctx, gitLabExternalID)
+ if getErr != nil {
+ return getErr
+ }
+
+ // Delete the record
+ err := s.gitV2Repository.GitLabDeleteRepositoryByExternalID(ctx, gitLabExternalID)
+ if err != nil {
+ return err
+ }
+
+ // Convert the external ID value
+ repositoryExternalID, parseIntErr := strconv.ParseInt(record.RepositoryExternalID, 10, 64)
+ if err == nil {
+ return parseIntErr
+ }
+
+ // Log the event
+ s.eventService.LogEventWithContext(ctx, &events.LogEventArgs{
+ EventType: events.RepositoryDeleted,
+ ProjectSFID: record.ProjectSFID,
+ CLAGroupID: record.RepositoryCLAGroupID,
+ LfUsername: utils.GetUserNameFromContext(ctx),
+ EventData: &events.RepositoryDeletedEventData{
+ RepositoryName: record.RepositoryFullPath, // give the full path/name
+ RepositoryExternalID: repositoryExternalID,
+ },
+ })
+ return err
+}
+
// dbModelToGitLabRepository converts the database model to a v2 response model
func dbModelToGitLabRepository(dbModel *repoModels.RepositoryDBModel) (*v2Models.GitlabRepository, error) {
@@ -413,6 +452,7 @@ func dbModelToGitLabRepository(dbModel *repoModels.RepositoryDBModel) (*v2Models
RepositoryClaGroupID: dbModel.RepositoryCLAGroupID, // CLA Group ID
RepositoryExternalID: gitLabExternalID, // GitLab unique gitV1Repository ID
RepositoryName: dbModel.RepositoryName, // Short repository name
+ RepositoryFullPath: dbModel.RepositoryFullPath, // Repository full path
RepositoryOrganizationName: dbModel.RepositoryOrganizationName, // Group/Organization name
RepositoryURL: dbModel.RepositoryURL, // full url
RepositoryType: dbModel.RepositoryType, // gitlab
diff --git a/cla-backend-go/v2/repositories/repository.go b/cla-backend-go/v2/repositories/repository.go
index 4bc6f3848..27fa02048 100644
--- a/cla-backend-go/v2/repositories/repository.go
+++ b/cla-backend-go/v2/repositories/repository.go
@@ -38,6 +38,7 @@ type RepositoryInterface interface {
GitLabEnrollRepositoryByID(ctx context.Context, claGroupID string, repositoryID int64, enrollValue bool) error
GitLabEnableCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error
GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error
+ GitLabDeleteRepositoryByExternalID(ctx context.Context, gitLabExternalID int64) error
}
// Repository object/struct
@@ -457,6 +458,38 @@ func (r *Repository) GitLabDeleteRepositories(ctx context.Context, gitLabGroupPa
return lastErr
}
+// GitLabDeleteRepositoryByExternalID deletes the specified repository
+func (r *Repository) GitLabDeleteRepositoryByExternalID(ctx context.Context, gitLabExternalID int64) error {
+ f := logrus.Fields{
+ "functionName": "v2.repositories.repository.GitLabDeleteRepositoryByExternalID",
+ utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
+ "gitLabExternalID": gitLabExternalID,
+ }
+
+ repositoryRecord, err := r.GitLabGetRepositoryByExternalID(ctx, gitLabExternalID)
+ if err != nil {
+ // If nothing to delete...
+ if _, ok := err.(*utils.GitLabRepositoryNotFound); ok {
+ return nil
+ }
+ log.WithFields(f).WithError(err).Warnf("problem loading existing repository by external ID: %d", gitLabExternalID)
+ return err
+ }
+ if repositoryRecord == nil {
+ return nil
+ }
+
+ _, deleteErr := r.dynamoDBClient.DeleteItem(&dynamodb.DeleteItemInput{
+ Key: map[string]*dynamodb.AttributeValue{
+ repoModels.RepositoryIDColumn: {S: aws.String(repositoryRecord.RepositoryID)},
+ },
+ TableName: aws.String(r.repositoryTableName),
+ })
+
+ // Return the error
+ return deleteErr
+}
+
// getRepositoryWithConditionFilter fetches the repository entry based on the specified condition and filter criteria using the provided index
func (r *Repository) getRepositoryWithConditionFilter(ctx context.Context, condition expression.KeyConditionBuilder, filter expression.ConditionBuilder, indexName string) (*repoModels.RepositoryDBModel, error) {
f := logrus.Fields{
diff --git a/cla-backend-go/v2/repositories/service.go b/cla-backend-go/v2/repositories/service.go
index e2411433d..b19e3ac8d 100644
--- a/cla-backend-go/v2/repositories/service.go
+++ b/cla-backend-go/v2/repositories/service.go
@@ -15,7 +15,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/v2/common"
"github.com/communitybridge/easycla/cla-backend-go/config"
- gitlab_api "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
+ gitLabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
"github.com/communitybridge/easycla/cla-backend-go/github/branch_protection"
@@ -61,17 +61,19 @@ type ServiceInterface interface {
GitLabGetRepositoriesByOrganizationName(ctx context.Context, orgName string) (*v2Models.GitlabRepositoriesList, error)
GitLabGetRepositoriesByNamePrefix(ctx context.Context, repositoryNamePrefix string) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositories(ctx context.Context, projectSFID string, input *GitLabAddRepoModel) (*v2Models.GitlabRepositoriesList, error)
+ GitLabAddRepositoriesWithEnabledFlag(ctx context.Context, projectSFID string, input *GitLabAddRepoModel, enabled bool) (*v2Models.GitlabRepositoriesList, error)
GitLabAddRepositoriesByApp(ctx context.Context, gitLabOrgModel *common.GitLabOrganization) ([]*v2Models.GitlabRepository, error)
GitLabEnrollRepositories(ctx context.Context, claGroupID string, repositoryIDList []int64, enrollValue bool) error
GitLabEnrollRepository(ctx context.Context, claGroupID string, repositoryExternalID int64, enrollValue bool) error
GitLabEnrollCLAGroupRepositories(ctx context.Context, claGroupID string, enrollValue bool) error
GitLabDeleteRepositories(ctx context.Context, gitLabGroupPath string) error
+ GitLabDeleteRepositoryByExternalID(ctx context.Context, gitLabExternalID int64) error
}
// GitLabOrgRepo redefine the interface here to avoid circular dependency issues
type GitLabOrgRepo interface {
AddGitLabOrganization(ctx context.Context, input *common.GitLabAddOrganization, enabled bool) (*v2Models.GitlabOrganization, error)
- GetGitLabOrganizations(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
+ GetGitLabOrganizationsByProjectSFID(ctx context.Context, projectSFID string) (*v2Models.GitlabOrganizations, error)
GetGitLabOrganization(ctx context.Context, gitlabOrganizationID string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByName(ctx context.Context, gitLabOrganizationName string) (*common.GitLabOrganization, error)
GetGitLabOrganizationByExternalID(ctx context.Context, gitLabGroupID int64) (*common.GitLabOrganization, error)
@@ -88,7 +90,7 @@ type Service struct {
projectsClaGroupsRepo projects_cla_groups.Repository
ghOrgRepo github_organizations.RepositoryInterface
glOrgRepo GitLabOrgRepo
- gitLabApp *gitlab_api.App
+ gitLabApp *gitLabApi.App
eventService events.Service
}
@@ -107,7 +109,7 @@ func NewService(gitV1Repository *v1Repositories.Repository, gitV2Repository Repo
ghOrgRepo: ghOrgRepo,
glOrgRepo: glOrgRepo,
eventService: eventService,
- gitLabApp: gitlab_api.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
+ gitLabApp: gitLabApi.Init(config.GetConfig().Gitlab.AppClientID, config.GetConfig().Gitlab.AppClientSecret, config.GetConfig().Gitlab.AppPrivateKey),
}
}
diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml
index f3b6e1438..84ef282a0 100644
--- a/cla-backend/serverless.yml
+++ b/cla-backend/serverless.yml
@@ -15,6 +15,7 @@ package:
- ./dynamo-events-lambda
- ./zipbuilder-scheduler-lambda
- ./zipbuilder-lambda
+ - ./gitlab-repository-check-lambda
- ./functional-tests
- dev.sh
- docs/**
@@ -534,6 +535,23 @@ functions:
include:
- ./zipbuilder-lambda
+ gitlab-repository-check-lambda:
+ handler: gitlab-repository-check-lambda
+ name: ${self:service}-${opt:stage, self:provider.stage, 'dev'}-gitlab-repository-check-lambda
+ description: "routine to periodically check the GitLab repository list for auto-enabled GitLab Groups"
+ runtime: go1.x
+ timeout: 900 # maximum time allowed
+ memorySize: 1024
+ events:
+ - schedule:
+ description: 'periodically check the GitLab repository list for auto-enabled GitLab Groups'
+ rate: rate(15 minutes)
+ enabled: true
+ package:
+ individually: true
+ include:
+ - ./gitlab-repository-check-lambda
+
apiv1:
handler: wsgi_handler.handler
description: "EasyCLA Python API handler for the /v1 endpoints"
From 7cab2f8a1407c0560c7bc466ee08b7cf560ef8ba Mon Sep 17 00:00:00 2001
From: David Deal
Date: Fri, 27 Aug 2021 16:43:00 -0700
Subject: [PATCH 0470/1276] =?UTF-8?q?Resolved=20Build=20and=20Deployment?=
=?UTF-8?q?=20Issues=20for=20GitLab=20Repository=20Checker=20La=E2=80=A6?=
=?UTF-8?q?=20(#3210)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cla-backend-go/Makefile | 2 +-
.../handler/handler.go | 82 ++++++++++++++-----
cla-backend-go/github/init.go | 2 +-
cla-backend-go/gitlab_api/client_groups.go | 4 +
cla-backend-go/v2/common/models.go | 1 +
.../v2/gitlab_organizations/repository.go | 8 +-
6 files changed, 73 insertions(+), 26 deletions(-)
diff --git a/cla-backend-go/Makefile b/cla-backend-go/Makefile
index 439414290..700d174d9 100644
--- a/cla-backend-go/Makefile
+++ b/cla-backend-go/Makefile
@@ -301,7 +301,7 @@ build-zipbuilder-lambda-mac: deps
build-gitlab-repository-check-lambda-linux: deps
@echo "Building a statically linked Linux OSX amd64 binary..."
- env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(GITLAB_REPO_CHECK_BIN) cmd/gitlab_repository_check/main.go
+ env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) $(BUILD_TAGS) -o $(GITLAB_REPO_CHECK_BIN) cmd/gitlab_repository_check/main.go
@chmod +x $(GITLAB_REPO_CHECK_BIN)
build-gitlab-repository-check-lambda-mac: deps
diff --git a/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
index ca8aabdab..718c8341d 100644
--- a/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
+++ b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go
@@ -5,12 +5,16 @@ package handler
import (
"context"
+ "os"
"strconv"
+ "github.com/communitybridge/easycla/cla-backend-go/config"
+
+ "github.com/aws/aws-sdk-go/aws/session"
+
v1Company "github.com/communitybridge/easycla/cla-backend-go/company"
"github.com/communitybridge/easycla/cla-backend-go/events"
"github.com/communitybridge/easycla/cla-backend-go/gerrits"
- "github.com/communitybridge/easycla/cla-backend-go/github"
"github.com/communitybridge/easycla/cla-backend-go/github_organizations"
gitLabApi "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
gitlab "github.com/communitybridge/easycla/cla-backend-go/gitlab_api"
@@ -30,6 +34,13 @@ import (
goGitLab "github.com/xanzy/go-gitlab"
)
+var (
+ awsSession *session.Session
+ stage string
+ configFile config.Config
+ gitLabApp *gitLabApi.App
+)
+
// Init initializes the handler
func Init() {
f := logrus.Fields{
@@ -41,7 +52,43 @@ func Init() {
// General initialization
ini.Init()
- ini.GetConfig()
+
+ var awsErr error
+ awsSession, awsErr = ini.GetAWSSession()
+ if awsErr != nil {
+ log.WithFields(f).WithError(awsErr).Panic("unable to load AWS session")
+ }
+
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage = os.Getenv("STAGE")
+ if stage == "" {
+ log.WithFields(f).Panic("unable to determine STAGE - please set in the environment variable: 'STAGE' - expected one of [DEV, STAGING, PROD]")
+ }
+
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ log.WithFields(f).Panic("unable to determine DYNAMODB_AWS_REGION - please set in the environment variable: 'DYNAMODB_AWS_REGION'")
+ }
+
+ var configErr error
+ configFile, configErr = config.LoadConfig("", awsSession, stage)
+ if configErr != nil {
+ log.WithFields(f).WithError(configErr).Panicf("Unable to load config - Error: %v", configErr)
+ }
+
+ if configFile.Gitlab.AppClientID == "" {
+ log.WithFields(f).Panic("unable to determine configFile.Gitlab.AppClientID value - please set the configuration")
+ }
+ if configFile.Gitlab.AppClientSecret == "" {
+ log.WithFields(f).Panic("unable to determine configFile.Gitlab.AppClientSecret value - please set the configuration")
+ }
+ if configFile.Gitlab.AppPrivateKey == "" {
+ log.WithFields(f).Panic("unable to determine configFile.Gitlab.AppPrivateKey value - please set the configuration")
+ }
+
+ // Create a new GitLab App client instance
+ gitLabApp = gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
+
}
// Handler is invoked each time the lambda is triggered - https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html
@@ -53,20 +100,6 @@ func Handler(ctx context.Context) error {
// Add the x-request-id to the context
ctx = utils.NewContextFromParent(ctx)
f[utils.XREQUESTID] = ctx.Value(utils.XREQUESTID)
- stage := strings.ToLower(utils.GetProperty("STAGE"))
- f["stage"] = stage
-
- awsSession, err := ini.GetAWSSession()
- if err != nil {
- log.WithFields(f).WithError(err).Panic("Unable to load AWS session")
- }
-
- configFile := ini.GetConfig()
-
- // initialize GitHub
- github.Init(configFile.GitHub.AppID, configFile.GitHub.AppPrivateKey, configFile.GitHub.AccessToken)
- // initialize GitLab
- gitLabApp := gitlab.Init(configFile.Gitlab.AppClientID, configFile.Gitlab.AppClientSecret, configFile.Gitlab.AppPrivateKey)
// Repository Layer
usersRepo := users.NewRepository(awsSession, stage)
@@ -118,20 +151,27 @@ func Handler(ctx context.Context) error {
log.WithFields(f).Debugf("start - checking %d GitLab projects for add/delete events", len(gitLabGroups.List))
for _, gitLabGroup := range gitLabGroups.List {
+ log.WithFields(f).Debugf("start - processing GitLab group/organization: %s with group ID: %d associated with project SFID: %s", gitLabGroup.OrganizationURL, gitLabGroup.OrganizationExternalID, gitLabGroup.ProjectSfid)
+
+ if gitLabGroup.AuthInfo == "" {
+ log.WithFields(f).Debugf("GitLab group/organization: %s not fully onboarded - missing authentication info - skipping", gitLabGroup.OrganizationURL)
+ continue
+ }
+
gitLabClient, gitLabClientErr := gitLabApi.NewGitlabOauthClient(gitLabGroup.AuthInfo, gitLabApp)
if gitLabClientErr != nil {
- log.WithFields(f).WithError(gitLabClientErr).Warnf("problem loading GitLab client for group/organization: %s", gitLabGroup.OrganizationURL)
- return gitLabClientErr
+ log.WithFields(f).WithError(gitLabClientErr).Warnf("problem loading GitLab client for group/organization: %s - skipping", gitLabGroup.OrganizationURL)
+ continue
}
gitLabProjects, getGitLabAPIError := gitLabApi.GetGroupProjectListByGroupID(ctx, gitLabClient, int(gitLabGroup.OrganizationExternalID))
if getGitLabAPIError != nil {
- log.WithFields(f).WithError(getGitLabAPIError).Warnf("problem loading GitLab projects for group/organization: %s using the groupID: %d - skipping GitLab Group/Organziation", gitLabGroup.OrganizationFullPath, gitLabGroup.OrganizationExternalID)
+ log.WithFields(f).WithError(getGitLabAPIError).Warnf("problem loading GitLab projects for group/organization: %s using the groupID: %d - skipping GitLab Group/Organziation - skipping", gitLabGroup.OrganizationFullPath, gitLabGroup.OrganizationExternalID)
continue
}
gitLabDBProjects, getProjectListDBErr := gitV2Repository.GitLabGetRepositoriesByNamePrefix(ctx, gitLabGroup.OrganizationFullPath)
if getProjectListDBErr != nil {
- log.WithFields(f).WithError(getProjectListDBErr).Warnf("problem loading GitLab projects for group/organization: %s from the database - skipping GitLab Group/Organziation", gitLabGroup.OrganizationFullPath)
+ log.WithFields(f).WithError(getProjectListDBErr).Warnf("problem loading GitLab projects for group/organization: %s from the database - skipping GitLab Group/Organziation - skipping", gitLabGroup.OrganizationFullPath)
continue
}
@@ -177,6 +217,8 @@ func Handler(ctx context.Context) error {
}
}
}
+
+ log.WithFields(f).Debugf("done - processed GitLab group/organization: %s with group ID: %d associated with project SFID: %s", gitLabGroup.OrganizationURL, gitLabGroup.OrganizationExternalID, gitLabGroup.ProjectSfid)
}
log.WithFields(f).Debugf("done - checked %d GitLab projects for add/delete events", len(gitLabGroups.List))
diff --git a/cla-backend-go/github/init.go b/cla-backend-go/github/init.go
index 4dd5a5d0a..3e59e4c9a 100644
--- a/cla-backend-go/github/init.go
+++ b/cla-backend-go/github/init.go
@@ -7,7 +7,7 @@ var githubAppPrivateKey string
var githubAppID int
var secretAccessToken string
-// Init initializes the required github variables
+// Init initializes the required GitHub variables
func Init(ghAppID int, ghAppPrivateKey string, secAccessToken string) {
githubAppPrivateKey = ghAppPrivateKey
githubAppID = ghAppID
diff --git a/cla-backend-go/gitlab_api/client_groups.go b/cla-backend-go/gitlab_api/client_groups.go
index 196b95531..533c29edc 100644
--- a/cla-backend-go/gitlab_api/client_groups.go
+++ b/cla-backend-go/gitlab_api/client_groups.go
@@ -152,6 +152,10 @@ func GetGroupProjectListByGroupID(ctx context.Context, client *goGitLab.Client,
"functionName": "gitlab_api.client_groups.GetGroupProjectListByGroupID",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}
+ if groupID == 0 {
+ return nil, errors.New("invalid groupID value - 0")
+ }
+
opts := &goGitLab.ListGroupProjectsOptions{
ListOptions: goGitLab.ListOptions{
Page: 1, // starts with one: https://docs.gitlab.com/ee/api/#offset-based-pagination
diff --git a/cla-backend-go/v2/common/models.go b/cla-backend-go/v2/common/models.go
index b2fe363e7..d7cec44ab 100644
--- a/cla-backend-go/v2/common/models.go
+++ b/cla-backend-go/v2/common/models.go
@@ -32,6 +32,7 @@ type GitLabOrganization struct {
// ToModel converts to models.GitlabOrganization
func ToModel(in *GitLabOrganization) *models2.GitlabOrganization {
return &models2.GitlabOrganization{
+ AuthInfo: in.AuthInfo,
OrganizationID: in.OrganizationID,
DateCreated: in.DateCreated,
DateModified: in.DateModified,
diff --git a/cla-backend-go/v2/gitlab_organizations/repository.go b/cla-backend-go/v2/gitlab_organizations/repository.go
index d8ca8abf8..ca2aa9749 100644
--- a/cla-backend-go/v2/gitlab_organizations/repository.go
+++ b/cla-backend-go/v2/gitlab_organizations/repository.go
@@ -827,7 +827,7 @@ func (repo *Repository) getScanResults(ctx context.Context, filter *expression.C
// Add the filter if provided
if filter != nil {
- builder.WithFilter(*filter)
+ builder = builder.WithFilter(*filter)
}
// Build the scan/query expression
@@ -860,12 +860,12 @@ func (repo *Repository) getScanResults(ctx context.Context, filter *expression.C
}
}
- var gitlabOrganizations *v2Models.GitlabOrganizations
- unmarshalErr := dynamodbattribute.UnmarshalListOfMaps(resultList, &gitlabOrganizations)
+ var resultOutput []*common.GitLabOrganization
+ unmarshalErr := dynamodbattribute.UnmarshalListOfMaps(resultList, &resultOutput)
if unmarshalErr != nil {
log.Warnf("error unmarshalling %s from database. error: %v", repo.gitlabOrgTableName, unmarshalErr)
return nil, unmarshalErr
}
- return gitlabOrganizations, nil
+ return &v2Models.GitlabOrganizations{List: common.ToModels(resultOutput)}, nil
}
From 89b79f38638865de9b53583d7715ab9124ac2c5d Mon Sep 17 00:00:00 2001
From: Harold Wanyama
Date: Mon, 30 Aug 2021 13:12:12 +0300
Subject: [PATCH 0471/1276] Bug/Individual Signature
- Resolved issue raised by gitlab flow for individual signature
Signed-off-by: Harold Wanyama
---
cla-backend/cla/controllers/signing.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend/cla/controllers/signing.py b/cla-backend/cla/controllers/signing.py
index 78a163239..8dc40dc17 100644
--- a/cla-backend/cla/controllers/signing.py
+++ b/cla-backend/cla/controllers/signing.py
@@ -36,12 +36,12 @@ def request_individual_signature(project_id, user_id, return_url_type, return_ur
signing_service = get_signing_service()
if return_url_type is not None and return_url_type.lower() == "gerrit":
return signing_service.request_individual_signature_gerrit(str(project_id), str(user_id), return_url)
- elif return_url_type is not None and (return_url_type.lower() == "github" or return_url_type.lower == "gitlab"):
+ elif return_url_type is not None and (return_url_type.lower() == "github" or return_url_type.lower() == "gitlab"):
if return_url_type == "github":
# fetching the primary for the account
github = get_repository_service("github")
primary_user_email = github.get_primary_user_email(request)
- elif return_url_type == "gitlab":
+ elif return_url_type.lower() == "gitlab":
try:
cla.log.debug(f"Fetching user details for: {user_id}")
user = User()
From 58161b816c9184bf71241b63fa0bc79dad8c0c8e Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Mon, 30 Aug 2021 09:09:48 -0700
Subject: [PATCH 0472/1276] Bug/Gitlab ICLA Sign (#3214)
---
cla-backend/cla/models/docusign_models.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 5194b1be9..04ee14fcf 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -252,9 +252,9 @@ def request_individual_signature(self, project_id, user_id, return_url=None, ret
signature_callback_url=callback_url)
# Set signature ACL
- if return_url_type == "github":
+ if return_url_type.lower() == "github":
acl = user.get_user_github_id()
- elif return_url_type == "gitlab":
+ elif return_url_type.lower() == "gitlab":
acl = user.get_user_github_id()
cla.log.debug('Individual Signature - setting ACL using user {} id: {}'.format(return_url_type, acl))
signature.set_signature_acl('github:{}'.format(acl))
@@ -705,9 +705,9 @@ def request_employee_signature(self, project_id, company_id, user_id, return_url
cla.log.info(f'{fn} - created new signature document for: {request_info} - signature: {new_signature}')
# Set signature ACL
- if return_url_type == "github":
+ if return_url_type.lower() == "github":
acl_value = f'github:{user.get_user_github_id()}'
- elif return_url_type == "gitlab":
+ elif return_url_type.lower() == "gitlab":
acl_value = f'gitlab:{user.get_user_gitlab_id()}'
cla.log.info(f'{fn} - assigning signature acl with value: {acl_value} for: {request_info}')
new_signature.set_signature_acl(acl_value)
From 472d3fb32471f647236369f212ce13f9b1d53a95 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Mon, 30 Aug 2021 16:14:37 -0700
Subject: [PATCH 0473/1276] Bug/GitLab ICLA (#3216)
---
cla-backend/cla/models/docusign_models.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend/cla/models/docusign_models.py b/cla-backend/cla/models/docusign_models.py
index 04ee14fcf..38a40605f 100644
--- a/cla-backend/cla/models/docusign_models.py
+++ b/cla-backend/cla/models/docusign_models.py
@@ -180,9 +180,9 @@ def request_individual_signature(self, project_id, user_id, return_url=None, ret
cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
cla.log.debug('Individual Signature - get individual signature callback url')
- if return_url_type == "github":
+ if return_url_type.lower() == "github":
callback_url = cla.utils.get_individual_signature_callback_url(user_id, signature_metadata)
- elif return_url_type == "gitlab":
+ elif return_url_type.lower() == "gitlab":
callback_url = cla.utils.get_individual_signature_callback_url_gitlab(user_id, signature_metadata)
cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
From 0d6ad209855d49f080deffb7b6f28174ea5a3fa1 Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Tue, 31 Aug 2021 08:51:18 -0700
Subject: [PATCH 0474/1276] Bug/Gitlab-ICLA Sign (#3218)
---
cla-backend-go/tests/github_v4_test.go | 74 ++---
.../v2/gitlab-activity/service_test.go | 304 +++++++++---------
cla-backend-go/v2/gitlab_sign/service.go | 1 +
cla-backend/cla/models/docusign_models.py | 2 +-
cla-backend/cla/models/dynamo_models.py | 6 +
cla-backend/cla/utils.py | 5 +-
6 files changed, 204 insertions(+), 188 deletions(-)
diff --git a/cla-backend-go/tests/github_v4_test.go b/cla-backend-go/tests/github_v4_test.go
index 29eba9348..217475a66 100644
--- a/cla-backend-go/tests/github_v4_test.go
+++ b/cla-backend-go/tests/github_v4_test.go
@@ -20,42 +20,44 @@ import (
)
func TestGetRepositoryIDFromName(t *testing.T) {
-
- ctx := utils.NewContext()
-
- // Need to initialize the system to load the configuration which contains a number of SSM parameters
- stage := os.Getenv("STAGE")
- if stage == "" {
- assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
- }
- dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
- if dynamodbRegion == "" {
- assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
- }
-
- viper.Set("STAGE", stage)
- viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
- ini.Init()
- _, err := ini.GetAWSSession()
- if err != nil {
- assert.Fail(t, "unable to load AWS session", err)
- }
- ini.ConfigVariable()
- config := ini.GetConfig()
- github.Init(config.GitHub.AppID, config.GitHub.AppPrivateKey, config.GitHub.AccessToken)
- installationID, int64Err := strconv.ParseInt(config.GitHub.TestOrganizationInstallationID, 10, 64)
- if int64Err != nil {
- assert.Fail(t, fmt.Sprintf("unable to convert installation ID to string: %s", config.GitHub.TestOrganizationInstallationID), int64Err)
+ if enabled {
+ ctx := utils.NewContext()
+
+ // Need to initialize the system to load the configuration which contains a number of SSM parameters
+ stage := os.Getenv("STAGE")
+ if stage == "" {
+ assert.Fail(t, "set STAGE environment variable to run unit and functional tests.")
+ }
+ dynamodbRegion := os.Getenv("DYNAMODB_AWS_REGION")
+ if dynamodbRegion == "" {
+ assert.Fail(t, "set DYNAMODB_AWS_REGION environment variable to run unit and functional tests.")
+ }
+
+ viper.Set("STAGE", stage)
+ viper.Set("DYNAMODB_AWS_REGION", dynamodbRegion)
+ ini.Init()
+ _, err := ini.GetAWSSession()
+ if err != nil {
+ assert.Fail(t, "unable to load AWS session", err)
+ }
+ ini.ConfigVariable()
+ config := ini.GetConfig()
+ github.Init(config.GitHub.AppID, config.GitHub.AppPrivateKey, config.GitHub.AccessToken)
+ installationID, int64Err := strconv.ParseInt(config.GitHub.TestOrganizationInstallationID, 10, 64)
+ if int64Err != nil {
+ assert.Fail(t, fmt.Sprintf("unable to convert installation ID to string: %s", config.GitHub.TestOrganizationInstallationID), int64Err)
+ }
+
+ branchProtectionRepoV4, err := branch_protection.NewBranchProtectionRepositoryV4(installationID)
+ if err != nil {
+ assert.Fail(t, fmt.Sprintf("initializing branch protection v4 repo failed : %v", err))
+ }
+ expectedValue := config.GitHub.TestRepositoryID
+ actualValue, err := branchProtectionRepoV4.GetRepositoryIDFromName(ctx, config.GitHub.TestOrganization, config.GitHub.TestRepository)
+ if err != nil {
+ assert.Fail(t, fmt.Sprintf("unable to create GitHub v4 client from installation ID: %d", installationID), err)
+ }
+ assert.Equal(t, expectedValue, actualValue, "CombinedRepository ID Lookup")
}
- branchProtectionRepoV4, err := branch_protection.NewBranchProtectionRepositoryV4(installationID)
- if err != nil {
- assert.Fail(t, fmt.Sprintf("initializing branch protection v4 repo failed : %v", err))
- }
- expectedValue := config.GitHub.TestRepositoryID
- actualValue, err := branchProtectionRepoV4.GetRepositoryIDFromName(ctx, config.GitHub.TestOrganization, config.GitHub.TestRepository)
- if err != nil {
- assert.Fail(t, fmt.Sprintf("unable to create GitHub v4 client from installation ID: %d", installationID), err)
- }
- assert.Equal(t, expectedValue, actualValue, "CombinedRepository ID Lookup")
}
diff --git a/cla-backend-go/v2/gitlab-activity/service_test.go b/cla-backend-go/v2/gitlab-activity/service_test.go
index 14c98dfef..02e37bfe1 100644
--- a/cla-backend-go/v2/gitlab-activity/service_test.go
+++ b/cla-backend-go/v2/gitlab-activity/service_test.go
@@ -14,182 +14,188 @@ import (
"github.com/xanzy/go-gitlab"
)
-func TestIsUserApprovedForSignature(t *testing.T) {
- userModel := &models.User{
- Emails: []string{
- "one@example.com",
- "two@bar.com",
- },
- }
- gitlabUser := &gitlab.User{
- Username: "one",
- }
+const enabled = false //nolint
- testCases := []struct {
- name string
- signature *models.Signature
- expected bool
- }{
- {
- name: "nothing matched",
- signature: &models.Signature{},
- },
- {
- name: "email approval list non empty no match",
- signature: &models.Signature{
- EmailApprovalList: []string{"three@example.com"},
+func TestIsUserApprovedForSignature(t *testing.T) {
+ if enabled {
+ userModel := &models.User{
+ Emails: []string{
+ "one@example.com",
+ "two@bar.com",
},
- },
- {
- name: "email approval list match",
- signature: &models.Signature{
- EmailApprovalList: []string{"one@example.com"},
+ }
+ gitlabUser := &gitlab.User{
+ Username: "one",
+ }
+
+ testCases := []struct {
+ name string
+ signature *models.Signature
+ expected bool
+ }{
+ {
+ name: "nothing matched",
+ signature: &models.Signature{},
},
- expected: true,
- },
- {
- name: "domain approval list match no match",
- signature: &models.Signature{
- DomainApprovalList: []string{"*.foo.com"},
+ {
+ name: "email approval list non empty no match",
+ signature: &models.Signature{
+ EmailApprovalList: []string{"three@example.com"},
+ },
},
- expected: false,
- },
- {
- name: "domain approval list match domain star",
- signature: &models.Signature{
- DomainApprovalList: []string{"*.example.com"},
+ {
+ name: "email approval list match",
+ signature: &models.Signature{
+ EmailApprovalList: []string{"one@example.com"},
+ },
+ expected: true,
},
- expected: true,
- },
- {
- name: "domain approval list match domain star globbing",
- signature: &models.Signature{
- DomainApprovalList: []string{"*example.com"},
+ {
+ name: "domain approval list match no match",
+ signature: &models.Signature{
+ DomainApprovalList: []string{"*.foo.com"},
+ },
+ expected: false,
},
- expected: true,
- },
- {
- name: "domain approval list match domain star dot",
- signature: &models.Signature{
- DomainApprovalList: []string{".example.com"},
+ {
+ name: "domain approval list match domain star",
+ signature: &models.Signature{
+ DomainApprovalList: []string{"*.example.com"},
+ },
+ expected: true,
},
- expected: true,
- },
- {
- name: "gitlab username approval list no match",
- signature: &models.Signature{
- GitlabUsernameApprovalList: []string{"two"},
+ {
+ name: "domain approval list match domain star globbing",
+ signature: &models.Signature{
+ DomainApprovalList: []string{"*example.com"},
+ },
+ expected: true,
},
- expected: false,
- },
- {
- name: "gitlab username approval list match",
- signature: &models.Signature{
- GitlabUsernameApprovalList: []string{"one"},
+ {
+ name: "domain approval list match domain star dot",
+ signature: &models.Signature{
+ DomainApprovalList: []string{".example.com"},
+ },
+ expected: true,
},
- expected: true,
- },
- }
+ {
+ name: "gitlab username approval list no match",
+ signature: &models.Signature{
+ GitlabUsernameApprovalList: []string{"two"},
+ },
+ expected: false,
+ },
+ {
+ name: "gitlab username approval list match",
+ signature: &models.Signature{
+ GitlabUsernameApprovalList: []string{"one"},
+ },
+ expected: true,
+ },
+ }
- for _, tc := range testCases {
- t.Run(tc.name, func(tt *testing.T) {
- result := IsUserApprovedForSignature(logrus.Fields{}, tc.signature, userModel, gitlabUser)
- if tc.expected {
- assert.True(tt, result)
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ result := IsUserApprovedForSignature(logrus.Fields{}, tc.signature, userModel, gitlabUser)
+ if tc.expected {
+ assert.True(tt, result)
- } else {
- assert.False(tt, result)
- }
- })
+ } else {
+ assert.False(tt, result)
+ }
+ })
+ }
}
}
func TestPrepareMrCommentContent(t *testing.T) {
-
- signedContains := ":white_check_mark: %s"
- missingUserContains := ":x: The commit associated with %s is missing the User's ID"
- missingAffiliationContains := "%s is authorized, but they must confirm their affiliation"
- missingApprovalContains := "%s's commit is not authorized under a signed CLA"
-
- testCases := []struct {
- name string
- signed []*gitlab.User
- missing []*gatedGitlabUser
- expectedMsgs []string
- expectedBadge string
- }{
- {
- name: "all signed",
- signed: []*gitlab.User{
- {ID: 1, Username: "neo"},
- {ID: 2, Username: "oracle"},
- },
- expectedMsgs: []string{signedContains, signedContains},
- expectedBadge: "cla-signed.svg",
- },
- {
- name: "missing id",
- signed: []*gitlab.User{
- {ID: 1, Username: "neo"},
- },
- missing: []*gatedGitlabUser{
- {err: missingID, User: &gitlab.User{ID: 3, Username: "missing"}},
- },
- expectedMsgs: []string{signedContains, missingUserContains},
- expectedBadge: "cla-missing-id.svg",
- },
- {
- name: "missing affiliation",
- signed: []*gitlab.User{
- {ID: 1, Username: "neo"},
+ if enabled {
+ signedContains := ":white_check_mark: %s"
+ missingUserContains := ":x: The commit associated with %s is missing the User's ID"
+ missingAffiliationContains := "%s is authorized, but they must confirm their affiliation"
+ missingApprovalContains := "%s's commit is not authorized under a signed CLA"
+
+ testCases := []struct {
+ name string
+ signed []*gitlab.User
+ missing []*gatedGitlabUser
+ expectedMsgs []string
+ expectedBadge string
+ }{
+ {
+ name: "all signed",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ {ID: 2, Username: "oracle"},
+ },
+ expectedMsgs: []string{signedContains, signedContains},
+ expectedBadge: "cla-signed.svg",
},
- missing: []*gatedGitlabUser{
- {err: missingCompanyAffiliation, User: &gitlab.User{ID: 4, Username: "affiliationUser"}},
+ {
+ name: "missing id",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingID, User: &gitlab.User{ID: 3, Username: "missing"}},
+ },
+ expectedMsgs: []string{signedContains, missingUserContains},
+ expectedBadge: "cla-missing-id.svg",
},
- expectedMsgs: []string{signedContains, missingAffiliationContains},
- expectedBadge: "cla-confirmation-needed.svg",
- },
- {
- name: "missing approval",
- signed: []*gitlab.User{
- {ID: 1, Username: "neo"},
+ {
+ name: "missing affiliation",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingCompanyAffiliation, User: &gitlab.User{ID: 4, Username: "affiliationUser"}},
+ },
+ expectedMsgs: []string{signedContains, missingAffiliationContains},
+ expectedBadge: "cla-confirmation-needed.svg",
},
- missing: []*gatedGitlabUser{
- {err: missingCompanyApproval, User: &gitlab.User{ID: 5, Username: "approvalUser"}},
+ {
+ name: "missing approval",
+ signed: []*gitlab.User{
+ {ID: 1, Username: "neo"},
+ },
+ missing: []*gatedGitlabUser{
+ {err: missingCompanyApproval, User: &gitlab.User{ID: 5, Username: "approvalUser"}},
+ },
+ expectedMsgs: []string{signedContains, missingApprovalContains},
+ expectedBadge: "cla-not-signed.svg",
},
- expectedMsgs: []string{signedContains, missingApprovalContains},
- expectedBadge: "cla-not-signed.svg",
- },
- }
+ }
- for _, tc := range testCases {
- t.Run(tc.name, func(tt *testing.T) {
- result := PrepareMrCommentContent(tc.missing, tc.signed, "https://sign.com")
- tt.Logf("the result is : %s", result)
- parts := strings.Split(result, "
")
- assert.Len(tt, parts, len(tc.expectedMsgs)+1)
+ for _, tc := range testCases {
+ t.Run(tc.name, func(tt *testing.T) {
+ result := PrepareMrCommentContent(tc.missing, tc.signed, "https://sign.com")
+ tt.Logf("the result is : %s", result)
+ parts := strings.Split(result, "
%s is authorized, but they must confirm their affiliation with their company.
Start the authorization process
- by clicking here, click "Corporate",
+ by clicking here, click "Corporate",
select the appropriate company from the list, then confirm
your affiliation on the page that appears.
For further assistance with EasyCLA,
@@ -275,7 +275,7 @@ func PrepareMrCommentContent(missingUsers []*gatedGitlabUser, signedUsers []*git
result += msg
body = confirmationNeededBadge
} else {
- msg := fmt.Sprintf(`
"
+ for author_info, user_commit_summaries in committers.items():
+ # build a quick list of just the commit hash values
+ commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
+ committers_comment += f'
"
if num_missing > 0:
support_url = "https://jira.linuxfoundation.org/servicedesk/customer/portal/4"
# Group commits by author.
committers = {}
- # Consider the case where github Id does not exist
- for commit, author in missing:
- name = "Unknown"
- if author and author[0]:
- name = author[1]
- if name not in committers:
- committers[name] = []
- committers[name].append(commit)
- # Check case for whitelisted unsigned user
- if len(author) == 5:
- committers[name].append(True)
+ for user_commit_summary in missing:
+ if user_commit_summary.is_valid_user():
+ author_info = user_commit_summary.get_user_info()
+ else:
+ author_info = "Unknown"
+
+ if author_info not in committers:
+ committers[author_info] = []
+
+ # user commit summary includes the author information and the corresponding commit hash
+ committers[author_info].append(user_commit_summary)
# Print author commit information.
committers_comment += "
"
github_help_url = "https://help.github.com/en/github/committing-changes-to-your-project/why-are-my-commits-linked-to-the-wrong-user"
- for author, commit_hashes in committers.items():
- if author == "Unknown":
+ for author_info, user_commit_summaries in committers.items():
+ if author_info == "Unknown":
+ # build a quick list of just the commit hash values
+ commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({' ,'.join(commit_hashes)}) "
+ f"
{failed} The commit ({' ,'.join(commit_shas)}) "
+ f"is missing the User's ID, preventing the EasyCLA check. "
+ f"Consult GitHub Help to resolve."
+ f"For further assistance with EasyCLA, "
@@ -1024,30 +1033,34 @@ def get_comment_body(repository_type, sign_url, signed, missing):
+ "
"
)
else:
- if True in commit_hashes:
- cla.log.info(f"{fn} filter True in commit hash listing: {commit_hashes}")
- filtered_commits = [commit_hash for commit_hash in commit_hashes if commit_hash != True]
+ missing_affiliation = [user_commit_summary.affiliation for user_commit_summary in user_commit_summaries
+ if not user_commit_summary.affiliation]
+ if len(missing_affiliation) > 0:
+ # build a quick list of just the commit hash values for users missing company affiliations
+ commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries
+ if not user_commit_summary.affiliation]
+ cla.log.info(f"{fn} SHAs for users with missing company affiliations: {commit_shas}")
committers_comment += (
- f"
{author} ({' ,'.join(filtered_commits)}) "
- + f"is authorized, but they must confirm their affiliation with their company. "
- + f"Start the authorization process "
- + f" by clicking here, click \"Corporate\","
- + f"select the appropriate company from the list, then confirm "
- + f"your affiliation on the page that appears. "
- + f"For further assistance with EasyCLA, "
- + f"please submit a support request ticket."
- + "
"
- )
+ f'
{author_info} ({" ,".join(commit_shas)}) '
+ f'is authorized, but they must confirm their affiliation with their company. '
+ f'Start the authorization process '
+ f" by clicking here, click \"Corporate\","
+ f'select the appropriate company from the list, then confirm '
+ f'your affiliation on the page that appears. '
+ f'For further assistance with EasyCLA, '
+ f"please submit a support request ticket."
+ '
')
else:
+ # build a quick list of just the commit hash values
+ commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
'
for author_info, user_commit_summaries in committers.items():
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += f'
'
github_help_url = "https://help.github.com/en/github/committing-changes-to-your-project/why-are-my-commits-linked-to-the-wrong-user"
for author_info, user_commit_summaries in committers.items():
- if author_info == "Unknown":
+ if author_info == 'Unknown':
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
f"
{failed} The commit ({' ,'.join(commit_shas)}) "
- + f"is missing the User's ID, preventing the EasyCLA check. "
- + f"Consult GitHub Help to resolve."
- + f"For further assistance with EasyCLA, "
- + f"please submit a support request ticket."
- + "
')
else:
- missing_affiliation = [user_commit_summary.affiliation for user_commit_summary in user_commit_summaries
- if not user_commit_summary.affiliation]
- if len(missing_affiliation) > 0:
+ missing_affiliations = [user_commit_summary.affiliated for user_commit_summary in user_commit_summaries
+ if not user_commit_summary.affiliated]
+ if len(missing_affiliations) > 0:
# build a quick list of just the commit hash values for users missing company affiliations
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries
- if not user_commit_summary.affiliation]
- cla.log.info(f"{fn} SHAs for users with missing company affiliations: {commit_shas}")
+ if not user_commit_summary.affiliated]
+ cla.log.info(f'{fn} SHAs for users with missing company affiliations: {commit_shas}')
committers_comment += (
- f'
{author_info} ({" ,".join(commit_shas)}) '
+ f'
{failed} {author_info} ({" ,".join(commit_shas)}) '
f'is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
f" by clicking here, click \"Corporate\","
From 49aab180c9917453a4e026842c202c0d5a5dfcb4 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 1 Mar 2022 16:11:06 -0800
Subject: [PATCH 0597/1276] Working to Resolve Type Error When Generating
GitHub Body Text (#3468)
---
cla-backend/cla/tests/unit/test_user_commit_summary.py | 8 ++++++--
cla-backend/cla/utils.py | 5 +++--
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/cla-backend/cla/tests/unit/test_user_commit_summary.py b/cla-backend/cla/tests/unit/test_user_commit_summary.py
index b191272b5..eb823b927 100644
--- a/cla-backend/cla/tests/unit/test_user_commit_summary.py
+++ b/cla-backend/cla/tests/unit/test_user_commit_summary.py
@@ -29,10 +29,14 @@ def test_user_commit_summary_is_valid(self) -> None:
self.assertFalse(t.is_valid_user())
def test_user_commit_summary_get_comment_body(self) -> None:
- s = UserCommitSummary("some_sha", 1234, 'login_value', 'author name', 'foo@bar.com', True, True)
- signed = [s]
+ s1 = UserCommitSummary("abc1234xyz-123", 1234, 'login_value', 'author name', 'foo@bar.com', True, True)
+ s2 = UserCommitSummary("abc1234xyz-456", 1234, 'login_value', 'author name', 'foo@bar.com', True, True)
+ signed = [s1, s2]
+
m = UserCommitSummary("some_other_sha", 123456, 'login_value2', 'author name2', 'foo2@bar.com', False, False)
missing = [m]
+
body = get_comment_body('github', 'https://foo.com', signed, missing)
+ print(f'body: {body}')
self.assertTrue(':white_check_mark:' in body)
self.assertTrue(':x:' in body)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index a0678038a..f5fefc658 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -985,7 +985,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
if user_commit_summary.is_valid_user():
author_info = user_commit_summary.get_user_info()
else:
- author_info = "Unknown"
+ author_info = 'Unknown'
if author_info not in committers:
committers[author_info] = []
@@ -998,7 +998,8 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
for author_info, user_commit_summaries in committers.items():
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
- committers_comment += f'
'
if num_missing > 0:
From c0893b1ff8d40ff7f809754625c4e7c6993598ae Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 1 Mar 2022 17:11:20 -0800
Subject: [PATCH 0598/1276] Resolved Typo in PR Checks Debug Statement (#3469)
---
cla-backend/cla/models/github_models.py | 2 +-
cla-backend/cla/utils.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index e2ec5425b..14e9b537d 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -1076,7 +1076,7 @@ def update_pull_request(installation_id, github_repository_id, pull_request, rep
if previously_failed:
cla.log.debug(f'{fn} - Found previously failed checks - updating CLA comment in PR.')
comment.edit(body)
- cla.log.debug(f'{fn} - EasyCLA App checks pass for PR: {pull_request.numbe} with authors: {signed}')
+ cla.log.debug(f'{fn} - EasyCLA App checks pass for PR: {pull_request.number} with authors: {signed}')
else:
# Per Issue #167, only add a comment if check fails
# update_cla_comment(pull_request, body)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index f5fefc658..6fddcae45 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1026,7 +1026,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({' ,'.join(commit_shas)}) "
+ f"
{failed} The commit ({' ,'.join(str(commit_shas))}) "
f"is missing the User's ID, preventing the EasyCLA check. "
f"Consult GitHub Help to resolve."
f'For further assistance with EasyCLA, '
@@ -1041,7 +1041,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
if not user_commit_summary.affiliated]
cla.log.info(f'{fn} SHAs for users with missing company affiliations: {commit_shas}')
committers_comment += (
- f'
{failed} {author_info} ({" ,".join(commit_shas)}) '
+ f'
{failed} {author_info} ({" ,".join(str(commit_shas))}) '
f'is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
f" by clicking here, click \"Corporate\","
From eb2dbe1f8d6e729359615de132185dc2a55cef92 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 1 Mar 2022 17:36:37 -0800
Subject: [PATCH 0599/1276] Resolved Commit SHA Issue with User Commit Summary
Object (#3470)
---
cla-backend/cla/models/github_models.py | 10 ++++++----
cla-backend/cla/utils.py | 6 +++---
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index 14e9b537d..008d4f023 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -793,6 +793,7 @@ def handle_commit_from_user(project, user_commit_summary: UserCommitSummary, sig
# Does this user have a signed signature for this project? If so, add to the signed list and return,
# no reason to continue looking
if cla.utils.user_signed_project_signature(user, project):
+ user_commit_summary.authorized = True
signed.append(user_commit_summary)
return
@@ -850,6 +851,7 @@ def handle_commit_from_user(project, user_commit_summary: UserCommitSummary, sig
# Does this user have a signed signature for this project? If so, add to the signed list and return,
# no reason to continue looking
if cla.utils.user_signed_project_signature(user, project):
+ user_commit_summary.authorized = True
signed.append(user_commit_summary)
return
@@ -925,7 +927,7 @@ def get_pull_request_commit_authors(pull_request) -> List[UserCommitSummary]:
if commit.author:
try:
commit_author_summary = UserCommitSummary(
- pull_request.number,
+ commit.sha,
commit.author.id,
commit.author.login,
commit.author.name,
@@ -941,7 +943,7 @@ def get_pull_request_commit_authors(pull_request) -> List[UserCommitSummary]:
# only has date, name and email attributes - no ID attribute/value
# https://pygithub.readthedocs.io/en/latest/github_objects/GitAuthor.html
commit_author_summary = UserCommitSummary(
- pull_request.number,
+ commit.sha,
None,
None,
commit.commit.author.name,
@@ -955,7 +957,7 @@ def get_pull_request_commit_authors(pull_request) -> List[UserCommitSummary]:
commit_authors.append(commit_author_summary)
except (GithubException, IncompletableObject):
commit_author_summary = UserCommitSummary(
- pull_request.number,
+ commit.sha,
None,
None,
None,
@@ -967,7 +969,7 @@ def get_pull_request_commit_authors(pull_request) -> List[UserCommitSummary]:
commit_authors.append(commit_author_summary)
else:
commit_author_summary = UserCommitSummary(
- pull_request.number,
+ commit.sha,
None,
None,
None,
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 6fddcae45..a2138ed2d 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -999,7 +999,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
cla.log.info(f'{fn} SHAs for signed users: {commit_shas}')
- committers_comment += f'
'
committers_comment += ''
if num_missing > 0:
@@ -1026,7 +1026,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({' ,'.join(str(commit_shas))}) "
+ f"
{failed} The commit ({' ,'.join(commit_shas)}) "
f"is missing the User's ID, preventing the EasyCLA check. "
f"Consult GitHub Help to resolve."
f'For further assistance with EasyCLA, '
@@ -1041,7 +1041,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
if not user_commit_summary.affiliated]
cla.log.info(f'{fn} SHAs for users with missing company affiliations: {commit_shas}')
committers_comment += (
- f'
{failed} {author_info} ({" ,".join(str(commit_shas))}) '
+ f'
{failed} {author_info} ({" ,".join(commit_shas)}) '
f'is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
f" by clicking here, click \"Corporate\","
From 19d9c0e41fab54959ac7912df3098affeff5518f Mon Sep 17 00:00:00 2001
From: Harold Wanyama <81645226+nickmango@users.noreply.github.com>
Date: Wed, 2 Mar 2022 18:30:32 +0300
Subject: [PATCH 0600/1276] Bug/User sign update (#3472)
Co-authored-by: Harold Wanyama
---
cla-backend/cla/utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index a2138ed2d..dc2000e96 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1033,9 +1033,9 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
f"please submit a support request ticket."
'
')
else:
- missing_affiliations = [user_commit_summary.affiliated for user_commit_summary in user_commit_summaries
+ missing_affiliations = [user_commit_summary.affiliated and user_commit_summary.authorized for user_commit_summary in user_commit_summaries
if not user_commit_summary.affiliated]
- if len(missing_affiliations) > 0:
+ if len(missing_affiliations) > 0 :
# build a quick list of just the commit hash values for users missing company affiliations
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries
if not user_commit_summary.affiliated]
From 6a008d7a496a5075bf866a68b3b8cb73cc7b73c9 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 2 Mar 2022 08:06:33 -0800
Subject: [PATCH 0601/1276] Reformatted the GitHub comment block text (#3473)
---
cla-backend/cla/user.py | 15 ++++-----------
cla-backend/cla/utils.py | 33 ++++++++++++++++++++-------------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/cla-backend/cla/user.py b/cla-backend/cla/user.py
index 26ed2206b..5cf69f371 100644
--- a/cla-backend/cla/user.py
+++ b/cla-backend/cla/user.py
@@ -91,29 +91,22 @@ def get_user_info(self) -> str:
return re.sub(pattern, '', user_info)
def get_display_text(self) -> str:
- text = ''
if not self.author_id:
return f'{self.author_email} is not linked to this commit.\n'
- # Build up the user text
- if self.author_login:
- text += f'login: {self.author_login} / '
- if self.author_name:
- text += f'name: {self.author_name} / '
- if self.author_email:
- text += f'email: {self.author_email} '
+ text = self.get_user_info()
if not self.is_valid_user():
return 'Invalid author details.\n'
if self.authorized and self.affiliated:
- text += 'is authorized.\n'
+ text += ' is authorized.\n'
return text
if self.affiliated:
- text += 'is associated with a company, but not on an approval list.\n'
+ text += ' is associated with a company, but not on an approval list.\n'
else:
- text += 'is not associated with a company.\n'
+ text += ' is not associated with a company.\n'
return text
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index dc2000e96..da59db862 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -977,6 +977,11 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
committers_comment = ""
num_signed = len(signed)
num_missing = len(missing)
+ text = ''
+
+ # Start of the HTML to render the list of committers
+ if len(signed) > 0 or len(missing) > 0:
+ committers_comment += '
'
if num_signed > 0:
# Group commits by author.
@@ -994,17 +999,16 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
committers[author_info].append(user_commit_summary)
# Print author commit information.
- committers_comment += '
'
for author_info, user_commit_summaries in committers.items():
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
cla.log.info(f'{fn} SHAs for signed users: {commit_shas}')
committers_comment += f'
'
if num_missing > 0:
support_url = "https://jira.linuxfoundation.org/servicedesk/customer/portal/4"
- # Group commits by author.
+
+ # Build a lookup table to group all the commits by author.
committers = {}
for user_commit_summary in missing:
if user_commit_summary.is_valid_user():
@@ -1018,16 +1022,15 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# user commit summary includes the author information and the corresponding commit hash
committers[author_info].append(user_commit_summary)
- # Print author commit information.
- committers_comment += '
'
+ # Print the author commit information.
github_help_url = "https://help.github.com/en/github/committing-changes-to-your-project/why-are-my-commits-linked-to-the-wrong-user"
for author_info, user_commit_summaries in committers.items():
if author_info == 'Unknown':
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({' ,'.join(commit_shas)}) "
- f"is missing the User's ID, preventing the EasyCLA check. "
+ f"
{failed} The commit ({' ,'.join(commit_shas)}). "
+ f"This user is missing the User's ID, preventing the EasyCLA check. "
f"Consult GitHub Help to resolve."
f'For further assistance with EasyCLA, '
f"please submit a support request ticket."
@@ -1041,8 +1044,8 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
if not user_commit_summary.affiliated]
cla.log.info(f'{fn} SHAs for users with missing company affiliations: {commit_shas}')
committers_comment += (
- f'
{failed} {author_info} ({" ,".join(commit_shas)}) '
- f'is authorized, but they must confirm their affiliation with their company. '
+ f'
{failed} {author_info} ({" ,".join(commit_shas)}). '
+ f'This user is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
f" by clicking here, click \"Corporate\","
f'select the appropriate company from the list, then confirm '
@@ -1056,15 +1059,19 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
committers_comment += (
f'
'
f"{failed} - "
- f"{author_info} The commit ({' ,'.join(commit_shas)}) is not authorized under a signed CLA. "
+ f"{author_info}. The commit ({' ,'.join(commit_shas)}) "
+ "is not authorized under a signed CLA. "
f"Please click here to be authorized. "
f"For further assistance with EasyCLA, "
f"please submit a support request ticket."
"
")
- committers_comment += "
"
- return committers_comment
- text = "The committers are authorized under a signed CLA."
+ if len(signed) > 0 or len(missing) > 0:
+ committers_comment += '
'
+
+ if len(signed) > 0 and len(missing) == 0:
+ text = "The committers listed above are authorized under a signed CLA."
+
return text + committers_comment
From 70abf60167a82e35f29490fc38fd660dcd96d9a8 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 2 Mar 2022 09:10:30 -0800
Subject: [PATCH 0602/1276] Fixed Git SHA Printout in GH (#3474)
---
cla-backend/cla/utils.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index da59db862..3f03d7389 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1029,7 +1029,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({' ,'.join(commit_shas)}). "
+ f"
{failed} The commit ({', '.join(commit_shas)}). "
f"This user is missing the User's ID, preventing the EasyCLA check. "
f"Consult GitHub Help to resolve."
f'For further assistance with EasyCLA, '
@@ -1044,7 +1044,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
if not user_commit_summary.affiliated]
cla.log.info(f'{fn} SHAs for users with missing company affiliations: {commit_shas}')
committers_comment += (
- f'
{failed} {author_info} ({" ,".join(commit_shas)}). '
+ f'
{failed} {author_info} ({", ".join(commit_shas)}). '
f'This user is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
f" by clicking here, click \"Corporate\","
@@ -1059,7 +1059,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
committers_comment += (
f'
'
f"{failed} - "
- f"{author_info}. The commit ({' ,'.join(commit_shas)}) "
+ f"{author_info}. The commit ({', '.join(commit_shas)}) "
"is not authorized under a signed CLA. "
f"Please click here to be authorized. "
f"For further assistance with EasyCLA, "
From c5a5462dcc883ba2dccabf8b81659226bbf0c25d Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 2 Mar 2022 10:56:24 -0800
Subject: [PATCH 0603/1276] GitHub Commit - Hide User Public Email (#3476)
---
cla-backend/cla/user.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cla-backend/cla/user.py b/cla-backend/cla/user.py
index 5cf69f371..8bced801d 100644
--- a/cla-backend/cla/user.py
+++ b/cla-backend/cla/user.py
@@ -84,8 +84,8 @@ def get_user_info(self) -> str:
user_info += f'login: {self.author_login} / '
if self.author_name:
user_info += f'name: {self.author_name} / '
- if self.author_email:
- user_info += f'email: {self.author_email}'
+ # if self.author_email:
+ # user_info += f'email: {self.author_email}'
pattern = r'/ $'
return re.sub(pattern, '', user_info)
From b516cb0c1ff53ba1eba754bef4c78caf02637e52 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Tue, 15 Mar 2022 15:13:26 -0700
Subject: [PATCH 0604/1276] Added User Tag to GitHub Comment When Missing
Authorization (#3480)
---
.../tests/unit/test_user_commit_summary.py | 23 ++++++++++++++++++-
cla-backend/cla/user.py | 4 ++--
cla-backend/cla/utils.py | 5 ++--
3 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/cla-backend/cla/tests/unit/test_user_commit_summary.py b/cla-backend/cla/tests/unit/test_user_commit_summary.py
index eb823b927..75f759ed4 100644
--- a/cla-backend/cla/tests/unit/test_user_commit_summary.py
+++ b/cla-backend/cla/tests/unit/test_user_commit_summary.py
@@ -37,6 +37,27 @@ def test_user_commit_summary_get_comment_body(self) -> None:
missing = [m]
body = get_comment_body('github', 'https://foo.com', signed, missing)
- print(f'body: {body}')
self.assertTrue(':white_check_mark:' in body)
self.assertTrue(':x:' in body)
+
+ def test_user_commit_summary_tag_not_in_get_comment_body(self) -> None:
+ s1 = UserCommitSummary("abc1234xyz-123", 1234, 'login_value', 'author name', 'foo@bar.com', True, True)
+ s2 = UserCommitSummary("abc1234xyz-456", 1234, 'login_value', 'author name', 'foo@bar.com', True, True)
+ signed = [s1, s2]
+
+ missing = []
+
+ body = get_comment_body('github', 'https://foo.com', signed, missing)
+ self.assertTrue(':white_check_mark:' in body)
+ self.assertTrue('login_value' in body)
+ self.assertFalse('@login_value' in body) # users should not be tagged in signed use case
+
+ def test_user_commit_summary_tag_in_get_comment_body(self) -> None:
+ signed = []
+
+ m = UserCommitSummary("some_other_sha", 123456, 'login_value2', 'author name2', 'foo2@bar.com', False, False)
+ missing = [m]
+
+ body = get_comment_body('github', 'https://foo.com', signed, missing)
+ self.assertTrue(':x:' in body)
+ self.assertTrue('@login_value2' in body) # users should be tagged in missing use case
diff --git a/cla-backend/cla/user.py b/cla-backend/cla/user.py
index 8bced801d..737332488 100644
--- a/cla-backend/cla/user.py
+++ b/cla-backend/cla/user.py
@@ -78,10 +78,10 @@ def __str__(self) -> str:
def is_valid_user(self) -> bool:
return self.author_id is not None and (self.author_login is not None or self.author_name is not None)
- def get_user_info(self) -> str:
+ def get_user_info(self, tag_user:bool = False) -> str:
user_info = ''
if self.author_login:
- user_info += f'login: {self.author_login} / '
+ user_info += f'login: {"@" if tag_user else ""}{self.author_login} / '
if self.author_name:
user_info += f'name: {self.author_name} / '
# if self.author_email:
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index 3f03d7389..b15fe8a1b 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -1011,8 +1011,9 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# Build a lookup table to group all the commits by author.
committers = {}
for user_commit_summary in missing:
+ tag_user = True
if user_commit_summary.is_valid_user():
- author_info = user_commit_summary.get_user_info()
+ author_info = user_commit_summary.get_user_info(tag_user)
else:
author_info = 'Unknown'
@@ -1047,7 +1048,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
f'
{failed} {author_info} ({", ".join(commit_shas)}). '
f'This user is authorized, but they must confirm their affiliation with their company. '
f'Start the authorization process '
- f" by clicking here, click \"Corporate\","
+ f" by clicking here, click \"Corporate\", "
f'select the appropriate company from the list, then confirm '
f'your affiliation on the page that appears. '
f'For further assistance with EasyCLA, '
From e3d864284d43816f0b869bd222dc7ebb0b4b63a7 Mon Sep 17 00:00:00 2001
From: David Deal
Date: Wed, 16 Mar 2022 07:57:34 -0700
Subject: [PATCH 0605/1276] Added Additional User Tagging Logic for CLA Gating
Failures (#3482)
---
cla-backend/cla/models/github_models.py | 7 +++--
cla-backend/cla/user.py | 20 ++++---------
cla-backend/cla/utils.py | 40 ++++++++++++-------------
3 files changed, 30 insertions(+), 37 deletions(-)
diff --git a/cla-backend/cla/models/github_models.py b/cla-backend/cla/models/github_models.py
index 008d4f023..22f1f76da 100644
--- a/cla-backend/cla/models/github_models.py
+++ b/cla-backend/cla/models/github_models.py
@@ -1036,9 +1036,10 @@ def update_pull_request(installation_id, github_repository_id, pull_request, rep
# knows if it is pass/fail.
# Create check run for users that haven't yet signed and/or affiliated
if missing:
- text = ""
+ text = ''
+ help_url = ''
for user_commit_summary in missing:
- # Check for valid github id
+ # Check for valid GitHub id
# old tuple: (sha, (author_id, author_login_or_name, author_email, optionalTrue))
if not user_commit_summary.is_valid_user():
help_url = "https://help.github.com/en/github/committing-changes-to-your-project/why-are-my-commits-linked-to-the-wrong-user"
@@ -1051,7 +1052,7 @@ def update_pull_request(installation_id, github_repository_id, pull_request, rep
if user_commit_summary.commit_sha != last_commit.sha:
continue
- text += user_commit_summary.get_display_text()
+ text += user_commit_summary.get_display_text(tag_user=True)
payload = {
"name": "CLA check",
diff --git a/cla-backend/cla/user.py b/cla-backend/cla/user.py
index 737332488..93ff2df50 100644
--- a/cla-backend/cla/user.py
+++ b/cla-backend/cla/user.py
@@ -78,35 +78,27 @@ def __str__(self) -> str:
def is_valid_user(self) -> bool:
return self.author_id is not None and (self.author_login is not None or self.author_name is not None)
- def get_user_info(self, tag_user:bool = False) -> str:
+ def get_user_info(self, tag_user: bool = False) -> str:
user_info = ''
if self.author_login:
user_info += f'login: {"@" if tag_user else ""}{self.author_login} / '
if self.author_name:
user_info += f'name: {self.author_name} / '
- # if self.author_email:
- # user_info += f'email: {self.author_email}'
- pattern = r'/ $'
- return re.sub(pattern, '', user_info)
+ return re.sub(r'/ $', '', user_info)
- def get_display_text(self) -> str:
+ def get_display_text(self, tag_user: bool = False) -> str:
if not self.author_id:
return f'{self.author_email} is not linked to this commit.\n'
- text = self.get_user_info()
-
if not self.is_valid_user():
return 'Invalid author details.\n'
if self.authorized and self.affiliated:
- text += ' is authorized.\n'
- return text
+ return self.get_user_info(tag_user) + ' is authorized.\n'
if self.affiliated:
- text += ' is associated with a company, but not on an approval list.\n'
+ return self.get_user_info(tag_user) + ' is associated with a company, but not on an approval list.\n'
else:
- text += ' is not associated with a company.\n'
-
- return text
+ return self.get_user_info(tag_user) + ' is not associated with a company.\n'
diff --git a/cla-backend/cla/utils.py b/cla-backend/cla/utils.py
index b15fe8a1b..be37125c6 100644
--- a/cla-backend/cla/utils.py
+++ b/cla-backend/cla/utils.py
@@ -988,7 +988,7 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
committers = {}
for user_commit_summary in signed:
if user_commit_summary.is_valid_user():
- author_info = user_commit_summary.get_user_info()
+ author_info = user_commit_summary.get_user_info(tag_user=False)
else:
author_info = 'Unknown'
@@ -1011,9 +1011,8 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# Build a lookup table to group all the commits by author.
committers = {}
for user_commit_summary in missing:
- tag_user = True
if user_commit_summary.is_valid_user():
- author_info = user_commit_summary.get_user_info(tag_user)
+ author_info = user_commit_summary.get_user_info(tag_user=True)
else:
author_info = 'Unknown'
@@ -1030,16 +1029,17 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f"
{failed} The commit ({', '.join(commit_shas)}). "
- f"This user is missing the User's ID, preventing the EasyCLA check. "
- f"Consult GitHub Help to resolve."
- f'For further assistance with EasyCLA, '
- f"please submit a support request ticket."
- '
')
+ f"
{failed} The commit ({', '.join(commit_shas)}). "
+ f"This user is missing the User's ID, preventing the EasyCLA check. "
+ f"Consult GitHub Help to resolve."
+ f'For further assistance with EasyCLA, '
+ f"please submit a support request ticket."
+ '
')
else:
- missing_affiliations = [user_commit_summary.affiliated and user_commit_summary.authorized for user_commit_summary in user_commit_summaries
- if not user_commit_summary.affiliated]
- if len(missing_affiliations) > 0 :
+ missing_affiliations = [user_commit_summary.affiliated and user_commit_summary.authorized for
+ user_commit_summary in user_commit_summaries
+ if not user_commit_summary.affiliated]
+ if len(missing_affiliations) > 0:
# build a quick list of just the commit hash values for users missing company affiliations
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries
if not user_commit_summary.affiliated]
@@ -1058,14 +1058,14 @@ def get_comment_body(repository_type, sign_url, signed: List[UserCommitSummary],
# build a quick list of just the commit hash values
commit_shas = [user_commit_summary.commit_sha for user_commit_summary in user_commit_summaries]
committers_comment += (
- f'