Skip to content

Commit

Permalink
Merge pull request #4155 from communitybridge/feature/docusign-go-api
Browse files Browse the repository at this point in the history
Feature/docusign go api
  • Loading branch information
nickmango authored Oct 20, 2023
2 parents 90aeb74 + e0f4ab7 commit 95b4d98
Show file tree
Hide file tree
Showing 21 changed files with 1,653 additions and 38 deletions.
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

0 comments on commit 95b4d98

Please sign in to comment.