Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[#4002]Feature/docusign go api #4155

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cla-backend-go/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ func server(localMode bool) http.Handler {
v2GithubActivityService := v2GithubActivity.NewService(gitV1Repository, githubOrganizationsRepo, eventsService, autoEnableService, emailService)

v2ClaGroupService := cla_groups.NewService(v1ProjectService, templateService, v1ProjectClaGroupRepo, v1ClaManagerService, v1SignaturesService, metricsRepo, gerritService, v1RepositoriesService, eventsService)
v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, v1ProjectClaGroupRepo, v1CompanyService, v2ClaGroupService, configFile.DocuSignPrivateKey)
v2SignService := sign.NewService(configFile.ClaV1ApiURL, v1CompanyRepo, v1CLAGroupRepo, v1ProjectClaGroupRepo, v1CompanyService, v2ClaGroupService, configFile.DocuSignPrivateKey, usersService, v1SignaturesService, storeRepository, v1RepositoriesService, githubOrganizationsService, gitlabOrganizationsService)

sessionStore, err := dynastore.New(dynastore.Path("/"), dynastore.HTTPOnly(), dynastore.TableName(configFile.SessionStoreTableName), dynastore.DynamoDB(dynamodb.New(awsSession)))
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions cla-backend-go/github/github_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,38 @@ func CreateStatus(ctx context.Context, client *github.Client, owner, repo, sha s

return c, resp, nil
}

func GetReturnURL(ctx context.Context, installationID, repositoryID int64, pullRequestID int) (string, error) {
f := logrus.Fields{
"functionName": "github.github_repository.GetReturnURL",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"installationID": installationID,
"repositoryID": repositoryID,
"pullRequestID": pullRequestID,
}

client, err := NewGithubAppClient(installationID)

if err != nil {
log.WithFields(f).WithError(err).Warn("unable to create Github client")
return "", err
}

log.WithFields(f).Debugf("getting github repository by id: %d", repositoryID)
repo, _, err := client.Repositories.GetByID(ctx, repositoryID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get repository by ID: %d", repositoryID)
return "", err
}

log.WithFields(f).Debugf("getting pull request by id: %d", pullRequestID)
pullRequest, _, err := client.PullRequests.Get(ctx, *repo.Owner.Login, *repo.Name, pullRequestID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get pull request by ID: %d", pullRequestID)
return "", err
}

log.WithFields(f).Debugf("returning pull request html url: %s", *pullRequest.HTMLURL)

return *pullRequest.HTMLURL, nil
}
27 changes: 20 additions & 7 deletions cla-backend-go/project/common/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ func GetCurrentDocument(ctx context.Context, docs []models.ClaGroupDocument) (mo
continue
}

// No previous, use the first...
if currentDoc == (models.ClaGroupDocument{}) {
currentDoc = doc
currentDocVersion = version
currentDocDateTime = dateTime
continue
}
// // No previous, use the first...
// if currentDoc == (models.ClaGroupDocument{}) {
// currentDoc = doc
// currentDocVersion = version
// currentDocDateTime = dateTime
// continue
// }

// Newer version...
if version > currentDocVersion {
Expand All @@ -127,3 +127,16 @@ func GetCurrentDocument(ctx context.Context, docs []models.ClaGroupDocument) (mo

return currentDoc, nil
}

func AreClaGroupDocumentsEqual(doc1, doc2 models.ClaGroupDocument) bool {
return doc1.DocumentName == doc2.DocumentName &&
doc1.DocumentAuthorName == doc2.DocumentAuthorName &&
doc1.DocumentContentType == doc2.DocumentContentType &&
doc1.DocumentFileID == doc2.DocumentFileID &&
doc1.DocumentLegalEntityName == doc2.DocumentLegalEntityName &&
doc1.DocumentPreamble == doc2.DocumentPreamble &&
doc1.DocumentS3URL == doc2.DocumentS3URL &&
doc1.DocumentMajorVersion == doc2.DocumentMajorVersion &&
doc1.DocumentMinorVersion == doc2.DocumentMinorVersion &&
doc1.DocumentCreationDate == doc2.DocumentCreationDate
}
8 changes: 4 additions & 4 deletions cla-backend-go/project/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ func (s ProjectService) GetCLAGroupCurrentICLATemplateURLByID(ctx context.Contex
}
}

if currentDoc == (models.ClaGroupDocument{}) {
log.WithFields(f).WithError(err).Warn("problem determining current ICLA for this CLA Group")
if common.AreClaGroupDocumentsEqual(currentDoc, models.ClaGroupDocument{}) {
log.WithFields(f).WithError(err).Warn("problem determining current ICLA for this CLA Group - document is empty")
return "", &utils.CLAGroupICLANotConfigured{
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
Expand Down Expand Up @@ -288,8 +288,8 @@ func (s ProjectService) GetCLAGroupCurrentCCLATemplateURLByID(ctx context.Contex
}
}

if currentDoc == (models.ClaGroupDocument{}) {
log.WithFields(f).WithError(err).Warn("problem determining current CCLA for this CLA Group")
if common.AreClaGroupDocumentsEqual(currentDoc, models.ClaGroupDocument{}) {
log.WithFields(f).WithError(err).Warn("problem determining current CCLA for this CLA Group - document is empty")
return "", &utils.CLAGroupCCLANotConfigured{
CLAGroupID: claGroupID,
CLAGroupName: claGroupModel.ProjectName,
Expand Down
1 change: 1 addition & 0 deletions cla-backend-go/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ provider:
DOCUSIGN_INTEGRATOR_KEY: ${file(./env.json):docusign-integrator-key, ssm:/cla-docusign-integrator-key-${opt:stage}}
DOCUSIGN_AUTH_SERVER: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-auth-server-${opt:stage}}
DOCUSIGN_USER_ID: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-user-id-${opt:stage}}
DOCUSIGN_ACCOUNT_ID: ${file(./env.json):docusign-account-id, ssm:/cla-docusign-account-id-${opt:stage}}
CLA_API_BASE: ${file(./env.json):cla-api-base, ssm:/cla-api-base-${opt:stage}}
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}}
Expand Down
5 changes: 5 additions & 0 deletions cla-backend-go/signatures/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func (repo repository) buildProjectSignatureModels(ctx context.Context, results
UserDocusignName: dbSignature.UserDocusignName,
UserDocusignDateSigned: dbSignature.UserDocusignDateSigned,
AutoCreateECLA: dbSignature.AutoCreateECLA,
SignatureSignURL: dbSignature.SignatureSignURL,
SignatureCallbackURL: dbSignature.SignatureCallbackURL,
SignatureReturnURL: dbSignature.SignatureReturnURL,
SignatureReturnURLType: dbSignature.SignatureReturnURLType,
SignatureEnvelopeID: dbSignature.SignatureEnvelopeID,
}

sigs = append(sigs, sig)
Expand Down
5 changes: 5 additions & 0 deletions cla-backend-go/signatures/dbmodels.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ type ItemSignature struct {
SignatureSigned bool `json:"signature_signed"`
SignatureDocumentMajorVersion string `json:"signature_document_major_version"`
SignatureDocumentMinorVersion string `json:"signature_document_minor_version"`
SignatureSignURL string `json:"signature_sign_url"`
SignatureReturnURL string `json:"signature_return_url"`
SignatureReturnURLType string `json:"signature_return_url_type"`
SignatureCallbackURL string `json:"signature_callback_url"`
SignatureReferenceID string `json:"signature_reference_id"`
SignatureReferenceName string `json:"signature_reference_name"`
SignatureReferenceNameLower string `json:"signature_reference_name_lower"`
SignatureProjectID string `json:"signature_project_id"`
SignatureReferenceType string `json:"signature_reference_type"`
SignatureType string `json:"signature_type"`
SignatureEnvelopeID string `json:"signature_envelope_id"`
SignatureUserCompanyID string `json:"signature_user_ccla_company_id"`
EmailApprovalList []string `json:"email_whitelist"`
EmailDomainApprovalList []string `json:"domain_whitelist"`
Expand Down
151 changes: 151 additions & 0 deletions cla-backend-go/signatures/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ type SignatureRepository interface {
DeleteGithubOrganizationFromApprovalList(ctx context.Context, signatureID, githubOrganizationID string) ([]models.GithubOrg, error)
ValidateProjectRecord(ctx context.Context, signatureID, note string) error
InvalidateProjectRecord(ctx context.Context, signatureID, note string) error
CreateOrUpdateSignature(ctx context.Context, signature *models.Signature) (*models.Signature, error)

GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
GetActivePullRequestMetadata(ctx context.Context, gitHubAuthorUsername, gitHubAuthorEmail string) (*ActivePullRequest, error)
GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error)
GetIndividualSignatures(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)
Expand Down Expand Up @@ -443,6 +445,40 @@ func (repo repository) GetSignature(ctx context.Context, signatureID string) (*m
return signatureList[0], nil
}

// CreateOrUpdateSignature either creates or updates the signature record
func (repo repository) CreateOrUpdateSignature(ctx context.Context, signature *models.Signature) (*models.Signature, error) {
f := logrus.Fields{
"functionName": "v1.signatures.repository.CreateOrUpdateSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}

// Check if we have an existing signature record
existingSignature, sigErr := repo.GetSignature(ctx, signature.SignatureID)
if sigErr != nil {
log.WithFields(f).Warnf("error retrieving signature by ID: %s, error: %v", signature.SignatureID, sigErr)
return nil, sigErr
}

// If we have an existing signature record, we need to update it
if existingSignature != nil {
log.WithFields(f).Debugf("updating existing signature record for signature ID: %s", signature.SignatureID)
return repo.updateSignature(ctx, signature)
}

return nil, nil
}

// updateSignature updates the specified signature record
func (repo repository) updateSignature(ctx context.Context, signature *models.Signature) (*models.Signature, error) {
// f := logrus.Fields{
// "functionName": "v1.signatures.repository.updateSignature",
// utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
// }

// // Update the record in the database
return nil, nil
}

// GetIndividualSignature returns the signature record for the specified CLA Group and User
func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error) {
f := logrus.Fields{
Expand Down Expand Up @@ -558,6 +594,121 @@ func (repo repository) GetIndividualSignature(ctx context.Context, claGroupID, u
return sigs[0], nil // nolint G602: Potentially accessing slice out of bounds (gosec)
}

// GetIndividualSignature returns the signature record for the specified CLA Group and User
func (repo repository) GetIndividualSignatures(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),
"tableName": repo.signatureTableName,
"claGroupID": claGroupID,
"userID": userID,
"signatureType": utils.SignatureTypeCLA,
"signatureReferenceType": utils.SignatureReferenceTypeUser,
"signatureApproved": "true",
"signatureSigned": "true",
}

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)))
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
searchTermExpression := expression.Name("signature_approved").Equal(expression.Value(aws.BoolValue(approved)))
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 = 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 = 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().
WithKeyCondition(condition).
WithFilter(filter).
WithProjection(buildProjection())

// Use the nice builder to create the expression
expr, err := builder.Build()
if err != nil {
log.WithFields(f).Warnf("error building expression for project ICLA signature query, 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.signatureTableName),
Limit: aws.Int64(100), // The maximum number of items to evaluate (not necessarily the number of matching items)
IndexName: aws.String(SignatureProjectReferenceIndex), // Name of a secondary index to scan
}

sigs := make([]*models.Signature, 0)
var lastEvaluatedKey string

// Loop until we have all the records
for ok := true; ok; ok = lastEvaluatedKey != "" {
// Make the DynamoDB Query API call
results, errQuery := repo.dynamoDBClient.Query(queryInput)
//log.WithFields(f).Debugf("Ran signature project query, results: %+v, error: %+v", results, errQuery)
if errQuery != nil {
log.WithFields(f).Warnf("error retrieving project ICLA signature ID, error: %v", errQuery)
return nil, errQuery
}

// Convert the list of DB models to a list of response models
//log.WithFields(f).Debug("Building response models...")
signatureList, modelErr := repo.buildProjectSignatureModels(ctx, results, claGroupID, LoadACLDetails)
if modelErr != nil {
log.WithFields(f).Warnf("error converting DB model to response model for signatures, error: %v",
modelErr)
return nil, modelErr
}

// 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
} else {
lastEvaluatedKey = ""
}
}

// Didn't find a matching record
if len(sigs) == 0 {
return nil, nil
}

if len(sigs) > 1 {
log.WithFields(f).Warnf("found multiple matching ICLA signatures - found %d total", len(sigs))
}

return sigs, nil
}

// GetCorporateSignature returns the signature record for the specified CLA Group and Company ID
func (repo repository) GetCorporateSignature(ctx context.Context, claGroupID, companyID string, approved, signed *bool) (*models.Signature, error) {
f := logrus.Fields{
Expand Down
12 changes: 12 additions & 0 deletions cla-backend-go/signatures/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
type SignatureService interface {
GetSignature(ctx context.Context, signatureID string) (*models.Signature, error)
GetIndividualSignature(ctx context.Context, claGroupID, userID string, approved, signed *bool) (*models.Signature, error)
GetIndividualSignatures(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)
Expand All @@ -67,6 +68,7 @@ type SignatureService interface {

createOrGetEmployeeModels(ctx context.Context, claGroupModel *models.ClaGroup, companyModel *models.Company, corporateSignatureModel *models.Signature) ([]*models.User, error)
CreateOrUpdateEmployeeSignature(ctx context.Context, claGroupModel *models.ClaGroup, companyModel *models.Company, corporateSignatureModel *models.Signature) ([]*models.User, error)
CreateOrUpdateSignature(ctx context.Context, signature *models.Signature) (*models.Signature, error)
handleGitHubStatusUpdate(ctx context.Context, employeeUserModel *models.User) error
}

Expand Down Expand Up @@ -113,6 +115,11 @@ func (s service) GetIndividualSignature(ctx context.Context, claGroupID, userID
return s.repo.GetIndividualSignature(ctx, claGroupID, userID, approved, signed)
}

// GetIndividualSignatures returns the list of signatures associated with the specified CLA Group and User ID
func (s service) GetIndividualSignatures(ctx context.Context, claGroupID, userID string, approved, signed *bool) ([]*models.Signature, error) {
return s.repo.GetIndividualSignatures(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, approved, signed *bool) (*models.Signature, error) {
return s.repo.GetCorporateSignature(ctx, claGroupID, companyID, approved, signed)
Expand All @@ -129,6 +136,11 @@ func (s service) GetProjectSignatures(ctx context.Context, params signatures.Get
return projectSignatures, nil
}

// CreateOrUpdateEmployeeSignature creates or updates the specified signature
func (s service) CreateOrUpdateSignature(ctx context.Context, signature *models.Signature) (*models.Signature, error) {
return s.repo.CreateOrUpdateSignature(ctx, signature)
}

// CreateProjectSummaryReport generates a project summary report based on the specified input
func (s service) CreateProjectSummaryReport(ctx context.Context, params signatures.CreateProjectSummaryReportParams) (*models.SignatureReport, error) {

Expand Down
3 changes: 3 additions & 0 deletions cla-backend-go/swagger/cla.v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2854,6 +2854,9 @@ definitions:

cla-group-document:
$ref: './common/cla-group-document.yaml'

document-tab:
$ref: './common/document-tab.yaml'

create-cla-group-template:
$ref: './common/create-cla-group-template.yaml'
Expand Down
9 changes: 9 additions & 0 deletions cla-backend-go/swagger/cla.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5605,6 +5605,12 @@ definitions:
sign_url:
type: string
description: signing url
user_id:
type: string
description: easyCLA user identification
project_id:
type: string
description: clagroup ID

signed_document:
type: object
Expand Down Expand Up @@ -5674,6 +5680,9 @@ definitions:

cla-group-summary:
$ref: './common/cla-group-summary.yaml'

document-tab:
$ref: './common/document-tab.yaml'

cla-group-project:
type: object
Expand Down
Loading
Loading