diff --git a/cla-backend-go/cmd/migrate_approval_list/main.go b/cla-backend-go/cmd/migrate_approval_list/main.go index cbfaf6089..cb940be89 100644 --- a/cla-backend-go/cmd/migrate_approval_list/main.go +++ b/cla-backend-go/cmd/migrate_approval_list/main.go @@ -5,8 +5,10 @@ package main import ( "context" + "flag" "fmt" "os" + "strings" "sync" "time" @@ -55,11 +57,25 @@ type combinedRepo struct { projects_cla_groups.Repository } +var emailDomainSuccessCount int +var emailSuccessCount int +var githubOrgSuccessCount int +var githubUsernameSuccessCount int +var gitlabOrgSuccessCount int +var gitlabUsernameSuccessCount int + +// var emailFailureCount int +// var githubOrgFailureCount int +// var githubUsernameFailureCount int +// var gitlabOrgFailureCount int +// var gitlabUsernameFailureCount int + func init() { stage = os.Getenv("STAGE") if stage == "" { log.Fatal("stage not set") } + log.Infof("STAGE set to %s\n", stage) approvalsTableName = fmt.Sprintf("cla-%s-approvals", stage) approvalRepo = approvals.NewRepository(stage, awsSession, approvalsTableName) @@ -91,11 +107,27 @@ func main() { log.Info("Fetching ccla signatures") signed := true approved := true + // signatureID := flag.String("signature-id", "ALL", "signature ID to migrate") + delete := flag.Bool("delete", false, "delete approval items") + flag.Parse() + + if *delete { + log.Info("Deleting approval items") + err := approvalRepo.DeleteAll() + if err != nil { + log.WithFields(f).WithError(err).Error("error deleting approval items") + return + } + log.Info("Deleted all approval items") + return + } + + log.Info("Fetching all ccla signatures...") cclaSignatures, err := signatureRepo.GetCCLASignatures(context.Background(), &signed, &approved) if err != nil { log.Fatalf("Error fetching ccla signatures : %v", err) } - log.Info("Fetched ccla signatures") + log.Infof("Fetched %d ccla signatures", len(cclaSignatures)) eventFound = 0 eventNotFound = 0 recordExists = 0 @@ -106,6 +138,7 @@ func main() { wg.Add(1) go func(signature *signatures.ItemSignature) { defer wg.Done() + log.WithFields(f).Debugf("Processing company : %s, project : %s", signature.SignatureReferenceName, signature.SignatureProjectID) err := updateApprovalsTable(signature) if err != nil { log.WithFields(f).Warnf("Error updating approvals table for signature : %s, error: %v", signature.SignatureID, err) @@ -114,6 +147,39 @@ func main() { } wg.Wait() log.WithFields(f).Debugf("Events found : %d, Events not found : %d , existing Record count: %d", eventFound, eventNotFound, recordExists) + + + +} + +func counter(listType string) { + switch listType { + case utils.DomainApprovalCriteria: + emailDomainSuccessCount++ + case utils.EmailApprovalCriteria: + emailSuccessCount++ + case utils.GithubOrgApprovalCriteria: + githubOrgSuccessCount++ + case utils.GithubUsernameApprovalCriteria: + githubUsernameSuccessCount++ + case utils.GitlabOrgApprovalCriteria: + gitlabOrgSuccessCount++ + case utils.GitlabUsernameApprovalCriteria: + gitlabUsernameSuccessCount++ + } +} + +func approvalExists(approvalItems []approvals.ApprovalItem, listType, item, projectID, signatureID string) bool { + if len(approvalItems) == 0 { + return false + } + for _, approval := range approvalItems { + if approval.ApprovalCriteria == listType && approval.ApprovalName == item && approval.ProjectID == projectID && approval.SignatureID == signatureID { + return true + } + } + + return false } func updateApprovalsTable(signature *signatures.ItemSignature) error { @@ -126,24 +192,45 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { var errMutex sync.Mutex var err error + approvalItems, err := approvalRepo.GetApprovalListBySignature(signature.SignatureID) + if err != nil { + log.WithFields(f).Warnf("Error fetching approval list items for signature : %s, error: %v", signature.SignatureID, err) + return err + } + + log.WithFields(f).Debugf("Fetched %d approval list items for signature: %s", len(approvalItems), signature.SignatureID) + + company, err := companyRepo.GetCompany(context.Background(), signature.SignatureReferenceID) + + if err != nil { + log.WithFields(f).Warnf("Error fetching company : %s, error: %v", signature.SignatureReferenceID, err) + return err + } + + if company == nil { + log.WithFields(f).Warnf("Company not found for : %s", signature.SignatureReferenceID) + return fmt.Errorf("company not found for : %s", signature.SignatureReferenceID) + } + + searchTerm := "was added to the approval list" + + // Get Company Project list + companyEvents, err := eventsRepo.GetCompanyClaGroupEvents(signature.SignatureProjectID, company.CompanyExternalID, nil, nil, &searchTerm, true) + + if err != nil { + log.WithFields(f).Warnf("Error fetching company events : %s, error: %v", signature.SignatureProjectID, err) + return err + } + + log.WithFields(f).Debugf("Fetched %d company events for project : %s and company: %s", len(companyEvents.Events), signature.SignatureProjectID, company.CompanyName) + update := func(approvalList []string, listType string) { defer wg.Done() for _, item := range approvalList { searchTerm := fmt.Sprintf("%s was added to the approval list", item) - pageSize := int64(1000) eventType := events.ClaApprovalListUpdated - // check if approval item already exists - approvalItems, searchErr := approvalRepo.SearchApprovalList(listType, item, signature.SignatureProjectID, "", signature.SignatureID) - if err != nil { - errMutex.Lock() - err = searchErr - errMutex.Unlock() - log.WithFields(f).Warnf("Error searching approval list for item : %s, error: %v", item, err) - return - } - - if len(approvalItems) > 0 { + if approvalExists(approvalItems, listType, item, signature.SignatureProjectID, signature.SignatureID) { log.WithFields(f).Debugf("Approval item already exists for : %s, %s", listType, item) recordExists++ return @@ -151,13 +238,16 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { log.WithFields(f).Debugf("searching for events with search term : %s, projectID: %s, eventType: %s ", searchTerm, signature.SignatureProjectID, eventType) - events, eventErr := eventsRepo.GetCCLAEvents(signature.SignatureProjectID, signature.SignatureReferenceID, searchTerm, eventType, pageSize) + dateAdded := signature.DateModified - if eventErr != nil { - errMutex.Lock() - err = eventErr - errMutex.Unlock() - return + // search events for the item + for _, event := range companyEvents.Events { + if event.EventType == eventType && event.EventCompanyID == company.CompanyID && event.EventCLAGroupID == signature.SignatureProjectID && strings.Contains(strings.ToLower(event.EventData), strings.ToLower(searchTerm)) { + log.WithFields(f).Debugf("found event with id: %s, event : %+v ", event.EventID, event) + dateAdded = event.EventTime + eventFound++ + break + } } approvalID, approvalErr := uuid.NewV4() @@ -169,6 +259,7 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { return } currentTime := time.Now().UTC().String() + note := fmt.Sprintf("Approval item added by migration script on %s", currentTime) approvalItem := approvals.ApprovalItem{ ApprovalID: approvalID.String(), SignatureID: signature.SignatureID, @@ -179,22 +270,12 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { CompanyID: signature.SignatureReferenceID, ProjectID: signature.SignatureProjectID, ApprovalCompanyName: signature.SignatureReferenceName, + DateAdded: dateAdded, + Note: note, + Active: true, } log.WithFields(f).Debugf("Adding approval item : %+v", approvalItem) - - if len(events) > 0 { - event := getLatestEvent(events) - approvalItem.DateAdded = event.EventTime - log.WithFields(f).Debugf("found event with id: %s , approval: %+v ", event.EventID, approvalItem) - eventFound++ - } else { - log.WithFields(f).Debugf("no events found for %s: %s", listType, item) - approvalItem.DateAdded = signature.DateModified - eventNotFound++ - } - - log.WithFields(f).Debugf("adding approval item : %+v", approvalItem) approvalErr = approvalRepo.AddApprovalList(approvalItem) if err != nil { errMutex.Lock() @@ -203,6 +284,8 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { log.WithFields(f).Warnf("Error adding approval item : %v", err) return } + + counter(listType) } } @@ -226,6 +309,9 @@ func updateApprovalsTable(signature *signatures.ItemSignature) error { wg.Wait() + log.WithFields(f).Debugf("processed for company : %s, project : %s", signature.SignatureReferenceName, signature.SignatureProjectID) + log.WithFields(f).Debugf("Email domain success count : %d, Email success count : %d, Github org success count : %d, Github username success count : %d, Gitlab org success count : %d, Gitlab username success count : %d", emailDomainSuccessCount, emailSuccessCount, githubOrgSuccessCount, githubUsernameSuccessCount, gitlabOrgSuccessCount, gitlabUsernameSuccessCount) + return err } diff --git a/cla-backend-go/events/repository.go b/cla-backend-go/events/repository.go index 187f18b8b..20f23bd6c 100644 --- a/cla-backend-go/events/repository.go +++ b/cla-backend-go/events/repository.go @@ -4,10 +4,12 @@ package events import ( + "crypto/rand" "encoding/base64" "encoding/json" "errors" "fmt" + "math/big" "strconv" "strings" "time" @@ -27,6 +29,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models" eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events" ) @@ -249,6 +252,48 @@ func addTimeExpression(keyCond expression.KeyConditionBuilder, params *eventOps. return keyCond } +func isProvisionedThroughputExceeded(err error) bool { + f := logrus.Fields{ + "functionName": "v1.events.repository.isProvisionedThroughputExceeded", + } + + if err == nil { + return false + } + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "ProvisionedThroughputExceededException" { + log.WithFields(f).WithError(err).Warn("provisioned throughput exceeded") + return true + } + + log.WithFields(f).WithError(err).Warn("error checking for provisioned throughput exceeded") + return false +} + +func exponentialBackoffSleep(retry int) { + // Base delay + baseDelay := 100 + + // Mx backoff time + maxBackoff := 20000 + + // Calculate delay + delay := baseDelay * (1 << retry) + + if delay > maxBackoff { + delay = maxBackoff + } + + // Add jitter + jitter, err := rand.Int(rand.Reader, big.NewInt(int64(delay))) + if err != nil { + log.Warnf("error generating random number: %v", err) + return + } + totalDelay := time.Duration(int64(delay)+jitter.Int64()) * time.Millisecond + + time.Sleep(totalDelay) +} + // GetEvents func (repo *repository) GetCCLAEvents(claGroupId, companyID, searchTerm, eventType string, pageSize int64) ([]*models.Event, error) { f := logrus.Fields{ @@ -290,13 +335,21 @@ func (repo *repository) GetCCLAEvents(claGroupId, companyID, searchTerm, eventTy var results *dynamodb.QueryOutput - for { + maxRetries := 5 + + for retry := 0; retry < maxRetries; retry++ { // Perform the query... + log.WithFields(f).Debugf("retrying query: %d", retry) var errQuery error results, errQuery = repo.dynamoDBClient.Query(queryInput) if errQuery != nil { - log.WithFields(f).WithError(errQuery).Warn("error retrieving events") - return nil, errQuery + if retry == maxRetries || isProvisionedThroughputExceeded(errQuery) { + log.WithFields(f).WithError(errQuery).Warn("error retrieving events") + return nil, errQuery + } + log.WithFields(f).WithError(errQuery).Warn("error retrieving events - retrying...") + exponentialBackoffSleep(retry) + continue } // Build the result models @@ -313,6 +366,7 @@ func (repo *repository) GetCCLAEvents(claGroupId, companyID, searchTerm, eventTy log.WithFields(f).Debugf("last evaluated key %+v", results.LastEvaluatedKey) if len(results.LastEvaluatedKey) > 0 { queryInput.ExclusiveStartKey = results.LastEvaluatedKey + retry = 0 } else { break } diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go index 5723432b9..733c225de 100644 --- a/cla-backend-go/signatures/repository.go +++ b/cla-backend-go/signatures/repository.go @@ -212,6 +212,7 @@ func (repo repository) GetCCLASignatures(ctx context.Context, signed, approved * } var filter expression.ConditionBuilder + pageSize := 1000 filter = expression.Name("signature_type").Equal(expression.Value("ccla")) if signed != nil { @@ -235,25 +236,39 @@ func (repo repository) GetCCLASignatures(ctx context.Context, signed, approved * FilterExpression: expr.Filter(), ExpressionAttributeNames: expr.Names(), ExpressionAttributeValues: expr.Values(), + Limit: aws.Int64(int64(pageSize)), } - results, err := repo.dynamoDBClient.Scan(input) - if err != nil { - log.WithFields(f).Warnf("error retrieving CCLA signatures, error: %v", err) - return nil, err - } + var signatures []*ItemSignature + var lastEvaluatedKey map[string]*dynamodb.AttributeValue - // The scan returns a list of matching records - we need to convert these to a list of models - log.WithFields(f).Debugf("retrieved %d CCLA signatures", len(results.Items)) + for { + results, queryErr := repo.dynamoDBClient.Scan(input) + if queryErr != nil { + log.WithFields(f).Warnf("error retrieving CCLA signatures, error: %v", queryErr) + return nil, queryErr + } - var signatures []*ItemSignature - err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &signatures) - if err != nil { - log.WithFields(f).Warnf("error unmarshalling CCLA signatures from database, error: %v", err) - return nil, err + var items []*ItemSignature + err = dynamodbattribute.UnmarshalListOfMaps(results.Items, &items) + if err != nil { + log.WithFields(f).Warnf("error unmarshalling CCLA signatures from database, error: %v", err) + return nil, err + } + + signatures = append(signatures, items...) + + // If the result set is truncated, we'll need to issue another query to fetch the next page + if results.LastEvaluatedKey == nil { + break + } + + lastEvaluatedKey = results.LastEvaluatedKey + input.ExclusiveStartKey = lastEvaluatedKey } return signatures, nil + } // UpdateSignature updates an existing signature diff --git a/cla-backend-go/v2/approvals/models.go b/cla-backend-go/v2/approvals/models.go index 03efe2d32..74ea117c9 100644 --- a/cla-backend-go/v2/approvals/models.go +++ b/cla-backend-go/v2/approvals/models.go @@ -16,4 +16,5 @@ type ApprovalItem struct { ProjectID string `dynamodbav:"project_id"` ApprovalCompanyName string `dynamodbav:"approval_company_name"` Note string `dynamodbav:"note"` + Active bool `dynamodbav:"active"` } diff --git a/cla-backend-go/v2/approvals/repository.go b/cla-backend-go/v2/approvals/repository.go index 4681cc0af..de44c3755 100644 --- a/cla-backend-go/v2/approvals/repository.go +++ b/cla-backend-go/v2/approvals/repository.go @@ -5,8 +5,11 @@ package approvals import ( "errors" + "fmt" + "time" "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" @@ -17,6 +20,7 @@ import ( type IRepository interface { GetApprovalList(approvalID string) (*ApprovalItem, error) + DeleteAll() error GetApprovalListBySignature(signatureID string) ([]ApprovalItem, error) AddApprovalList(approvalItem ApprovalItem) error DeleteApprovalList(approvalID string) error @@ -37,6 +41,95 @@ func NewRepository(stage string, awsSession *session.Session, tableName string) } } +func (repo *repository) getAll() ([]*ApprovalItem, error) { + f := logrus.Fields{ + "functionName": "getAll", + } + + log.WithFields(f).Debugf("repository.getAll - fetching all approval lists") + + // Get all the records + pageSize := int64(100) + + scanInput := &dynamodb.ScanInput{ + TableName: aws.String(repo.tableName), + Limit: aws.Int64(pageSize), + } + + var results []*ApprovalItem + for { + result, err := repo.dynamoDBClient.Scan(scanInput) + if err != nil { + log.WithFields(f).Warnf("repository.getAll - unable to scan table, error: %+v", err) + return nil, err + } + + var items []*ApprovalItem + err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &items) + if err != nil { + log.WithFields(f).Warnf("repository.getAll - unable to unmarshal data from table, error: %+v", err) + return nil, err + } + + results = append(results, items...) + + if result.LastEvaluatedKey == nil { + break + } + + scanInput.ExclusiveStartKey = result.LastEvaluatedKey + } + + return results, nil +} + +func (repo *repository) DeleteAll() error { + f := logrus.Fields{ + "functionName": "DeleteAll", + } + + log.WithFields(f).Debugf("repository.DeleteAll - deleting all approval lists") + itemsToDelete, err := repo.getAll() + + if err != nil { + log.WithFields(f).Warnf("repository.DeleteAll - unable to fetch data from table, error: %+v", err) + return err + } + + log.WithFields(f).Debugf("repository.DeleteAll - deleting %d approval list items", len(itemsToDelete)) + + // Delete all the records + for _, item := range itemsToDelete { + retry := 0 + for { + log.WithFields(f).Debugf("repository.DeleteAll - deleting approval list item: %+v", item) + deleteRequest := &dynamodb.DeleteItemInput{ + Key: map[string]*dynamodb.AttributeValue{ + "approval_id": { + S: aws.String(item.ApprovalID), + }, + }, + TableName: aws.String(repo.tableName), + } + + _, err = repo.dynamoDBClient.DeleteItem(deleteRequest) + if err != nil { + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == dynamodb.ErrCodeProvisionedThroughputExceededException { + if retry > 5 { + return fmt.Errorf("unable to delete approval list item - retry limit reached, error: %+v", err) + } + retry++ + continue + } + return fmt.Errorf("unable to delete approval list item, error: %+v", err) + } + break + } + } + + return nil +} + func (repo *repository) GetApprovalList(approvalID string) (*ApprovalItem, error) { f := logrus.Fields{ "functionName": "GetApprovalList", @@ -81,28 +174,53 @@ func (repo *repository) GetApprovalListBySignature(signatureID string) ([]Approv log.WithFields(f).Debugf("repository.GetApprovalListBySignature - fetching approval list by signatureID: %s", signatureID) - result, err := repo.dynamoDBClient.Scan(&dynamodb.ScanInput{ - TableName: aws.String(repo.tableName), - FilterExpression: aws.String("signature_id = :signature_id"), - ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ - ":signature_id": { - S: aws.String(signatureID), - }, - }, - }) + condition := expression.Key("signature_id").Equal(expression.Value(signatureID)) + + expr, err := expression.NewBuilder().WithKeyCondition(condition).Build() + if err != nil { - log.WithFields(f).Warnf("repository.GetApprovalListBySignature - unable to read data from table, error: %+v", err) + log.WithFields(f).Warnf("error building expression, error: %+v", err) return nil, err } - approvalItems := make([]ApprovalItem, 0) - err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &approvalItems) - if err != nil { - log.WithFields(f).Warnf("repository.GetApprovalListBySignature - unable to unmarshal data from table, error: %+v", err) - return nil, err + pageSize := int64(100) + + input := &dynamodb.QueryInput{ + TableName: aws.String(repo.tableName), + IndexName: aws.String("signature-id-index"), + KeyConditionExpression: expr.KeyCondition(), + ExpressionAttributeNames: expr.Names(), + ExpressionAttributeValues: expr.Values(), + Limit: aws.Int64(pageSize), + } + + var results []ApprovalItem + + for { + output, err := repo.dynamoDBClient.Query(input) + if err != nil { + log.WithFields(f).Warnf("error retrieving approval list, error: %+v", err) + return nil, err + } + + var items []ApprovalItem + err = dynamodbattribute.UnmarshalListOfMaps(output.Items, &items) + if err != nil { + log.WithFields(f).Warnf("error unmarshalling data, error: %+v", err) + return nil, err + } + + results = append(results, items...) + + if output.LastEvaluatedKey == nil { + break + } + + input.ExclusiveStartKey = output.LastEvaluatedKey } - return approvalItems, nil + return results, nil + } func (repo *repository) AddApprovalList(approvalItem ApprovalItem) error { @@ -121,13 +239,33 @@ func (repo *repository) AddApprovalList(approvalItem ApprovalItem) error { return err } - _, err = repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{ - TableName: aws.String(repo.tableName), - Item: av, - }) - if err != nil { - log.WithFields(f).Warnf("repository.AddApprovalList - unable to add data to table, error: %+v", err) - return err + const maxRetries = 5 + var retryDelay time.Duration = 1 + + for attempt := 0; attempt < maxRetries; attempt++ { + _, err = repo.dynamoDBClient.PutItem(&dynamodb.PutItemInput{ + TableName: aws.String(repo.tableName), + Item: av, + }) + + if err != nil { + awsErr, ok := err.(awserr.Error) + if !ok { + log.WithFields(f).Warnf("repository.AddApprovalList - unable to add data to table, error: %+v", err) + return err + } + + switch awsErr.Code() { + case dynamodb.ErrCodeProvisionedThroughputExceededException: + log.WithFields(f).Warnf("repository.AddApprovalList - provisioned throughput exceeded, retrying in %d seconds", retryDelay) + time.Sleep(retryDelay * time.Second) + retryDelay = retryDelay * 2 // exponential backoff + continue + default: + log.WithFields(f).Warnf("repository.AddApprovalList - unable to add data to table, error: %+v", err) + return err + } + } } return nil diff --git a/cla-backend-go/v2/signatures/converters.go b/cla-backend-go/v2/signatures/converters.go index 93dd5c49f..a33f735f3 100644 --- a/cla-backend-go/v2/signatures/converters.go +++ b/cla-backend-go/v2/signatures/converters.go @@ -12,6 +12,7 @@ import ( "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/communitybridge/easycla/cla-backend-go/v2/approvals" "github.com/jinzhu/copier" "github.com/sirupsen/logrus" ) @@ -52,6 +53,14 @@ func v2SignaturesReplaceCompanyID(src *v1Models.Signatures, internalID, external } func (s *Service) v2SignaturesToCorporateSignatures(src models.Signatures, projectSFID string) (*models.CorporateSignatures, error) { + f := logrus.Fields{ + "functionName": "v2SignaturesToCorporateSignatures", + "projectSFID": projectSFID, + } + + // Convert the signatures + log.WithFields(f).Debugf("converting %d signatures to corporate signatures", len(src.Signatures)) + var dst models.CorporateSignatures err := copier.Copy(&dst, src) if err != nil { @@ -71,6 +80,25 @@ func (s *Service) v2SignaturesToCorporateSignatures(src models.Signatures, proje return &dst, nil } +func searchSignatureApprovals(signatureID, criteria, name string, approvalList []approvals.ApprovalItem) []approvals.ApprovalItem { + f := logrus.Fields{ + "functionName": "searchSignatureApprovals", + "signatureID": signatureID, + "criteria": criteria, + "name": name, + } + + var result = make([]approvals.ApprovalItem, 0) + for _, approval := range approvalList { + if approval.SignatureID == signatureID && approval.ApprovalCriteria == criteria && approval.ApprovalName == name { + log.WithFields(f).Debugf("found approval for %s: %s :%s", criteria, name, approval.DateAdded) + result = append(result, approval) + } + } + + return result +} + // TransformSignatureToCorporateSignature transforms a Signature model into a CorporateSignature model func (s *Service) TransformSignatureToCorporateSignature(signature *models.Signature, corporateSignature *models.CorporateSignature, projectSFID string) error { f := logrus.Fields{ @@ -79,37 +107,35 @@ func (s *Service) TransformSignatureToCorporateSignature(signature *models.Signa } var wg sync.WaitGroup - var errMutex sync.Mutex var err error + // fetch approval list items for signature + approvals, approvalErr := s.approvalsRepos.GetApprovalListBySignature(signature.SignatureID) + if approvalErr != nil { + log.WithFields(f).WithError(approvalErr).Warnf("unable to fetch approval list items for signature") + return approvalErr + } + + log.WithFields(f).Debugf("Fetched %d approval list items for signature", len(approvals)) + transformApprovalList := func(approvalList []string, listType string, destinationList *[]*models.ApprovalItem) { defer wg.Done() for _, item := range approvalList { - approvals, approvalErr := s.approvalsRepos.SearchApprovalList(listType, item, signature.ProjectID, "", signature.SignatureID) - if approvalErr != nil { - errMutex.Lock() - err = approvalErr - errMutex.Unlock() - return - } - + // Default to the signature modified date + // log.WithFields(f).Debugf("searching for approval for %s: %s", listType, item) + foundApprovals := searchSignatureApprovals(signature.SignatureID, listType, item, approvals) // Handle scenarios of records with no attached event logs dateAdded := signature.SignatureModified - if len(approvals) > 0 { - log.WithFields(f).Debugf("approval found: for %s: %s", listType, item) + if len(foundApprovals) > 0 { // ideally this should be one record dateAdded = approvals[0].DateAdded - } else { - log.WithFields(f).Debugf("no approval found for %s: %s", listType, item) } approvalItem := &models.ApprovalItem{ ApprovalItem: item, DateAdded: dateAdded, } - - log.WithFields(f).Debugf("approvalItem: %+v and list type: %s", approvalItem, listType) *destinationList = append(*destinationList, approvalItem) } }