diff --git a/cla-backend-go/cmd/s3_upload/main.go b/cla-backend-go/cmd/s3_upload/main.go new file mode 100644 index 000000000..981eb4733 --- /dev/null +++ b/cla-backend-go/cmd/s3_upload/main.go @@ -0,0 +1,374 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +package main + +import ( + "context" + "encoding/csv" + "encoding/xml" + "strings" + "sync" + + "flag" + + "os" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + + "github.com/communitybridge/easycla/cla-backend-go/company" + "github.com/communitybridge/easycla/cla-backend-go/config" + "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/signatures" + "github.com/communitybridge/easycla/cla-backend-go/users" + "github.com/communitybridge/easycla/cla-backend-go/v2/sign" + + "github.com/communitybridge/easycla/cla-backend-go/utils" + + "github.com/sirupsen/logrus" +) + +var stage string +var signatureRepo signatures.SignatureRepository +var awsSession = session.Must(session.NewSession(&aws.Config{})) +var companyRepo company.IRepository +var usersRepo users.UserRepository + +var signService sign.Service +var githubOrgService github_organizations.Service +var report []ReportData +var failed int = 0 +var success int = 0 + +func init() { + stage = os.Getenv("STAGE") + if stage == "" { + log.Fatal("STAGE environment variable not set") + } + + companyRepo = company.NewRepository(awsSession, stage) + usersRepo = users.NewRepository(awsSession, stage) + signatureRepo = signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, nil, nil, nil, nil, nil) + githubOrgService = github_organizations.Service{} + configFile, err := config.LoadConfig("", awsSession, stage) + if err != nil { + log.Fatal(err) + } + signService = sign.NewService("", "", companyRepo, nil, nil, nil, nil, configFile.DocuSignPrivateKey, nil, nil, nil, nil, githubOrgService, nil, "", "", nil, nil, nil, nil, nil) + // projectRepo = repository.NewRepository(awsSession, stage, nil, nil, nil) + utils.SetS3Storage(awsSession, configFile.SignatureFilesBucket) +} + +const ( + // Approved Flag + Approved = true + // Signed Flag + Signed = true + + Failed = "failed" + Success = "success" + DocumentUploaded = "Document uploaded successfully" +) + +type ReportData struct { + SignatureID string + ProjectID string + ReferenceID string + ReferenceName string + EnvelopeID string + DocumentID string + Comment string + Status string +} + +type APIErrorResponse struct { + ErrorCode string `json:"errorCode"` + Message string `json:"message"` +} + +func main() { // nolint + ctx := context.Background() + f := logrus.Fields{ + "functionName": "main", + } + // var toUpdate []*signatures.ItemSignature + + dryRun := flag.Bool("dry-run", false, "dry run mode") + folder := flag.String("folder", "", "folder to upload the s3 documents") + meta := flag.String("meta", "", "meta data to upload the s3 documents") + + flag.Parse() + + // Fetch all the signatures from 2024-02-01T00:00:00.000Z + startDate := "2024-02-01T00:00:00.000Z" + + if dryRun != nil && *dryRun { + log.WithFields(f).Debug("dry-run mode enabled") + } + + if folder != nil && *folder != "" && meta != nil && *meta != "" { + log.WithFields(f).Debugf("folder: %s, meta: %s", *folder, *meta) + // var metaMap map[string]string + + // Read csv file + file, err := os.Open(*meta) + if err != nil { + log.WithFields(f).WithError(err).Warn("problem opening meta file") + return + } + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + + count := len(records) + log.WithFields(f).Debugf("processing %d records", count) + + passed := 0 + + if err != nil { + log.WithFields(f).WithError(err).Warn("problem reading meta file") + return + } + + var wg sync.WaitGroup + + // Limit the number of concurrent uploads + semaphore := make(chan struct{}, 5) + + for _, record := range records { + wg.Add(1) + semaphore <- struct{}{} + go func(record []string) { + defer wg.Done() + defer func() { <-semaphore }() + fileName := record[0] + envelopeID := record[1] + signatureID := record[2] + projectID := record[3] + referenceID := record[4] + log.WithFields(f).Debugf("uploading file: %s, envelopeID: %s, signatureID: %s, projectID: %s, referenceID: %s", fileName, envelopeID, signatureID, projectID, referenceID) + // Upload the file + file, err := os.Open(*folder + "/" + fileName) // nolint + if err != nil { + log.WithFields(f).WithError(err).Warn("problem opening file") + failed++ + return + } + + if dryRun != nil && *dryRun { + log.WithFields(f).Debugf("dry-run mode enabled, skipping file upload: %s", fileName) + return + } + + // Upload the document + log.WithFields(f).Debugf("uploading document for signature...: %s", signatureID) + + err = utils.UploadFileToS3(file, projectID, utils.ClaTypeICLA, referenceID, signatureID) + + if err != nil { + log.WithFields(f).WithError(err).Warn("problem uploading file") + failed++ + return + } + passed++ + + log.WithFields(f).Debugf("document uploaded for signature: %s", signatureID) + + }(record) + } + + wg.Wait() + + log.WithFields(f).Debug("completed processing files") + + log.WithFields(f).Debugf("total: %d, passed: %d, failed: %d", count, passed, failed) + return + } + + iclaSignatures, err := signatureRepo.GetICLAByDate(ctx, startDate) + if err != nil { + log.WithFields(f).WithError(err).Warn("problem fetching ICLA signatures") + return + } + + log.WithFields(f).Debugf("processing %d ICLA signatures", len(iclaSignatures)) + toUpload := make([]signatures.ItemSignature, 0) + + var wg sync.WaitGroup + semaphore := make(chan struct{}, 20) + + for _, icla := range iclaSignatures { + wg.Add(1) + semaphore <- struct{}{} + go func(sig signatures.ItemSignature) { + defer wg.Done() + defer func() { <-semaphore }() + key := strings.Join([]string{"contract-group", sig.SignatureProjectID, utils.ClaTypeICLA, sig.SignatureReferenceID, sig.SignatureID}, "/") + ".pdf" + fileExists, fileErr := utils.DocumentExists(key) + if fileErr != nil { + log.WithFields(f).WithError(fileErr).Debugf("unable to check s3 key : %s", key) + return + } + if !fileExists { + log.WithFields(f).Debugf("document is not uploaded for key: %s", key) + toUpload = append(toUpload, sig) + } else { + log.WithFields(f).Debugf("key: %s exists", key) + } + }(icla) + + } + + log.WithFields(f).Debugf("checking icla signatures from :%s", startDate) + wg.Wait() + log.WithFields(f).Debugf("To upload %d icla signatures: ", len(toUpload)) + + // Upload the documents + for _, icla := range toUpload { + wg.Add(1) + semaphore <- struct{}{} + go func(sig signatures.ItemSignature) { + defer wg.Done() + + var documentID string + + reportData := ReportData{ + SignatureID: sig.SignatureID, + ProjectID: sig.SignatureProjectID, + ReferenceID: sig.SignatureReferenceID, + ReferenceName: sig.SignatureReferenceName, + EnvelopeID: sig.SignatureEnvelopeID, + } + + // get the document id + var info sign.DocuSignEnvelopeInformation + + if sig.UserDocusignRawXML == "" { + log.WithFields(f).Debugf("no raw xml found for signature: %s", sig.SignatureID) + reportData.Comment = "No raw xml found" + // Fetch documentID + documents, docErr := signService.GetEnvelopeDocuments(ctx, sig.SignatureEnvelopeID) + if docErr != nil { + log.WithFields(f).WithError(err).Debugf("unable to get documents for signature: %s", sig.SignatureID) + reportData.Comment = docErr.Error() + reportData.Status = Failed + report = append(report, reportData) + failed++ + return + } + if len(documents) == 0 { + log.WithFields(f).Debugf("no documents found for signature: %s", sig.SignatureID) + reportData.Comment = "No documents found" + reportData.Status = Failed + report = append(report, reportData) + failed++ + return + } + documentID = documents[0].DocumentId + log.WithFields(f).Debugf("document id fetched from docusign: %s", documentID) + } else { + err = xml.Unmarshal([]byte(sig.UserDocusignRawXML), &info) + if err != nil { + log.WithFields(f).WithError(err).Debugf("unable to unmarshal xml for signature: %s", sig.SignatureID) + reportData.Comment = err.Error() + reportData.Status = Failed + report = append(report, reportData) + failed++ + return + } + documentID = info.EnvelopeStatus.DocumentStatuses[0].ID + } + + log.WithFields(f).Debugf("document id: %s", documentID) + reportData.DocumentID = documentID + envelopeID := sig.SignatureEnvelopeID + log.WithFields(f).Debugf("envelope id: %s", envelopeID) + + if documentID == "" { + log.WithFields(f).Debugf("no document id found for signature: %s", sig.SignatureID) + reportData.Comment = "No document id found" + reportData.Status = Failed + report = append(report, reportData) + failed++ + return + } + + // get the document + document, docErr := signService.GetSignedDocument(ctx, envelopeID, documentID) + if docErr != nil { + log.WithFields(f).WithError(docErr).Debugf("unable to get document for signature: %s", sig.SignatureID) + reportData.Comment = docErr.Error() + reportData.Status = Failed + report = append(report, reportData) + failed++ + return + } + // upload the document + if dryRun != nil && *dryRun { + log.WithFields(f).Debugf("dry-run mode enabled, skipping document upload for signature: %s", sig.SignatureID) + log.WithFields(f).Debugf("document uploaded for signature: %s", sig.SignatureID) + reportData.Comment = DocumentUploaded + reportData.Status = Success + report = append(report, reportData) + return + } + + log.WithFields(f).Debugf("uploading document for signature...: %s", sig.SignatureID) + err = utils.UploadToS3(document, sig.SignatureProjectID, utils.ClaTypeICLA, sig.SignatureReferenceID, sig.SignatureID) + if err != nil { + log.WithFields(f).WithError(err).Debugf("unable to upload document for signature: %s", sig.SignatureID) + reportData.Comment = err.Error() + reportData.Status = Failed + failed++ + report = append(report, reportData) + return + } + + log.WithFields(f).Debugf("document uploaded for signature: %s", sig.SignatureID) + reportData.Comment = DocumentUploaded + reportData.Status = Success + success++ + + report = append(report, reportData) + + // release the semaphore + <-semaphore + + }(icla) + } + + wg.Wait() + + log.WithFields(f).Debug("completed processing ICLA signatures") + + file, err := os.Create("s3_upload_report.csv") + if err != nil { + log.WithFields(f).WithError(err).Warn("problem creating report file") + return + } + + writer := csv.NewWriter(file) + defer writer.Flush() + + err = writer.Write([]string{"SignatureID", "ProjectID", "ReferenceID", "ReferenceName", "EnvelopeID", "DocumentID", "Comment", "Status"}) + if err != nil { + log.WithFields(f).WithError(err).Warn("problem writing header to report file") + return + } + + for _, data := range report { + // writer.Write([]string{data.SignatureID, data.ProjectID, data.ReferenceID, data.ReferenceName, data.EnvelopeID, data.DocumentID, data.Comment}) + record := []string{data.SignatureID, data.ProjectID, data.ReferenceID, data.ReferenceName, data.EnvelopeID, data.DocumentID, data.Comment, data.Status} + err = writer.Write(record) + if err != nil { + log.WithFields(f).WithError(err).Warn("problem writing record to report file") + } + } + + log.WithFields(f).Debugf("report generated successfully, total: %d, success: %d, failed: %d", len(report), success, failed) + + log.WithFields(f).Debug("report generated successfully") +} diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod index 2105701bc..0b70280d6 100644 --- a/cla-backend-go/go.mod +++ b/cla-backend-go/go.mod @@ -73,6 +73,8 @@ require ( require ( github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 // indirect + github.com/aws/smithy-go v1.20.2 // indirect github.com/cloudflare/circl v1.3.2 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum index 45ec94f36..512da7ea0 100644 --- a/cla-backend-go/go.sum +++ b/cla-backend-go/go.sum @@ -68,6 +68,11 @@ github.com/aws/aws-lambda-go v1.22.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XO github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.36.27 h1:wc3xLJJHog2SwiqlLnrLUuct/n+dBjB45QhuZw2psVE= github.com/aws/aws-sdk-go v1.36.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 h1:6cnno47Me9bRykw9AEv9zkXE+5or7jz8TsskTTccbgc= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1/go.mod h1:qmdkIIAC+GCLASF7R2whgNrJADz0QZPX+Seiw/i4S3o= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= 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/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= diff --git a/cla-backend-go/project/repository/repository.go b/cla-backend-go/project/repository/repository.go index 7fca1f377..8d8848f5d 100644 --- a/cla-backend-go/project/repository/repository.go +++ b/cla-backend-go/project/repository/repository.go @@ -567,7 +567,7 @@ func (repo *repo) GetCLAGroups(ctx context.Context, params *project.GetProjectsP } // Convert the list of DB models to a list of response models - projectList, modelErr := repo.buildCLAGroupModels(ctx, results.Items, LoadRepoDetails) + projectList, modelErr := repo.buildCLAGroupModels(ctx, results.Items, DontLoadRepoDetails) if modelErr != nil { log.WithFields(f).Warnf("error converting project DB model to response model, error: %v", modelErr) diff --git a/cla-backend-go/signatures/repository.go b/cla-backend-go/signatures/repository.go index 28d779730..b98540b44 100644 --- a/cla-backend-go/signatures/repository.go +++ b/cla-backend-go/signatures/repository.go @@ -106,6 +106,7 @@ type SignatureRepository interface { GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, pageSize *int64, nextKey *string, searchTerm *string) (*models.CorporateContributorList, error) EclaAutoCreate(ctx context.Context, signatureID string, autoCreateECLA bool) error ActivateSignature(ctx context.Context, signatureID string) error + GetICLAByDate(ctx context.Context, startDate string) ([]ItemSignature, error) } type iclaSignatureWithDetails struct { @@ -4447,6 +4448,69 @@ func (repo repository) GetClaGroupICLASignatures(ctx context.Context, claGroupID return out, nil } +func (repo repository) GetICLAByDate(ctx context.Context, startDate string) ([]ItemSignature, error) { + f := logrus.Fields{ + "functionName": "v1.signatures.repository.GetICLAs", + utils.XREQUESTID: ctx.Value(utils.XREQUESTID), + "startDate": startDate, + } + + var signatures []ItemSignature + + log.WithFields(f).Debug("querying for icla signatures by date...") + + filter := expression.Name("date_created").GreaterThanEqual(expression.Value(startDate)). + And(expression.Name("signature_type").Equal(expression.Value(utils.SignatureTypeCLA))). + And(expression.Name("signature_signed").Equal(expression.Value(true))). + And(expression.Name("signature_approved").Equal(expression.Value(true))) + + // Use the expression builder to create the expression + expr, err := expression.NewBuilder().WithFilter(filter).Build() + if err != nil { + log.WithFields(f).Warnf("error building expression for query icla signatures by date: %v", err) + return nil, err + } + + var lastEvaluatedKey map[string]*dynamodb.AttributeValue + + for { + scanInput := &dynamodb.ScanInput{ + ExpressionAttributeNames: expr.Names(), + ExpressionAttributeValues: expr.Values(), + FilterExpression: expr.Filter(), + TableName: aws.String(repo.signatureTableName), + ExclusiveStartKey: lastEvaluatedKey, + } + + result, err := repo.dynamoDBClient.Scan(scanInput) + if err != nil { + log.WithFields(f).Warnf("error retrieving icla signatures by date: %v", err) + return nil, err + } + + log.WithFields(f).Debugf("retrieved %d icla signatures by date", len(result.Items)) + + var dbSignatures []ItemSignature + + unmarshallError := dynamodbattribute.UnmarshalListOfMaps(result.Items, &dbSignatures) + if unmarshallError != nil { + log.WithFields(f).Warnf("error unmarshalling icla signatures from database by date: %v", unmarshallError) + return nil, unmarshallError + } + + signatures = append(signatures, dbSignatures...) + + // log.WithFields(f).Debugf("last evaluated key: %+v", result.LastEvaluatedKey) + + if result.LastEvaluatedKey == nil { + break + } + lastEvaluatedKey = result.LastEvaluatedKey + } + + return signatures, nil +} + func (repo repository) getIntermediateICLAResponse(f logrus.Fields, dbSignatures []ItemSignature) []*iclaSignatureWithDetails { var intermediateResponse []*iclaSignatureWithDetails diff --git a/cla-backend-go/utils/s3.go b/cla-backend-go/utils/s3.go index fa173b494..18ed627e8 100644 --- a/cla-backend-go/utils/s3.go +++ b/cla-backend-go/utils/s3.go @@ -7,10 +7,13 @@ import ( "bytes" "errors" "io" + "os" "strings" "time" + "github.com/aws/aws-sdk-go-v2/service/s3/types" log "github.com/communitybridge/easycla/cla-backend-go/logging" + "github.com/sirupsen/logrus" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -23,9 +26,11 @@ const PresignedURLValidity = 15 * time.Minute // S3Storage provides methods to handle s3 storage type S3Storage interface { Upload(fileContent []byte, projectID string, claType string, identifier string, signatureID string) error + UploadFile(file *os.File, projectID string, claType string, identifier string, signatureID string) error Download(filename string) ([]byte, error) Delete(filename string) error GetPresignedURL(filename string) (string, error) + KeyExists(key string) (bool, error) } var s3Storage S3Storage @@ -57,6 +62,16 @@ func (s3c *S3Client) Upload(fileContent []byte, projectID string, claType string return err } +func (s3c *S3Client) UploadFile(file *os.File, projectID string, claType string, identifier string, signatureID string) error { + filename := strings.Join([]string{"contract-group", projectID, claType, identifier, signatureID}, "/") + ".pdf" + _, err := s3c.s3.PutObject(&s3.PutObjectInput{ + Bucket: aws.String(s3c.BucketName), + Key: aws.String(filename), + Body: file, + }) + return err +} + // Download file from s3 func (s3c *S3Client) Download(filename string) ([]byte, error) { ou, err := s3c.s3.GetObject(&s3.GetObjectInput{ @@ -111,6 +126,24 @@ func UploadToS3(body []byte, projectID string, claType string, identifier string return s3Storage.Upload(body, projectID, claType, identifier, signatureID) } +// UploadFileToS3 uploads file to s3 storage at path contract-group////.pdf +// claType should be cla or ccla +// identifier can be user-id or company-id +func UploadFileToS3(file *os.File, projectID string, claType string, identifier string, signatureID string) error { + if s3Storage == nil { + return errors.New("s3Storage not set") + } + + return s3Storage.UploadFile(file, projectID, claType, identifier, signatureID) +} + +func DocumentExists(key string) (bool, error) { + if s3Storage == nil { + return false, errors.New("s3 storage not set") + } + return s3Storage.KeyExists(key) +} + // DownloadFromS3 downloads file from s3 func DownloadFromS3(filename string) ([]byte, error) { if s3Storage == nil { @@ -135,6 +168,35 @@ func GetDownloadLink(filename string) (string, error) { return s3Storage.GetPresignedURL(filename) } +// KeyExists checks if key exists in s3 +func (s3c *S3Client) KeyExists(key string) (bool, error) { + f := logrus.Fields{ + "functionName": "utils.s3.KeyExists", + "bucketName": s3c.BucketName, + "key": key, + } + + log.WithFields(f).Debug("checking for key") + + _, err := s3c.s3.HeadObject(&s3.HeadObjectInput{ + Bucket: aws.String(s3c.BucketName), + Key: aws.String(key), + }) + if err != nil { + // check for NotFound error + var nsk *types.NoSuchKey + if errors.As(err, &nsk) || strings.Contains(err.Error(), "NotFound") { + log.WithFields(f).Debug("key not found") + return false, nil + } + log.WithFields(f).WithError(err).Warn("problem checking for key") + return false, err + } + + log.WithFields(f).Debugf("s3 document exists for key: %s", key) + return true, nil +} + // SignedCLAFilename provide s3 bucket url func SignedCLAFilename(projectID string, claType string, identifier string, signatureID string) string { return strings.Join([]string{"contract-group", projectID, claType, identifier, signatureID}, "/") + ".pdf" diff --git a/cla-backend-go/v2/sign/docusign.go b/cla-backend-go/v2/sign/docusign.go index 080fd9f3d..a621ea504 100644 --- a/cla-backend-go/v2/sign/docusign.go +++ b/cla-backend-go/v2/sign/docusign.go @@ -566,7 +566,7 @@ func (s *service) GetSignURL(email, recipientID, userName, clientUserId, envelop return viewResponse.URL, nil } -func (s service) getSignedDocument(ctx context.Context, envelopeID, documentID string) ([]byte, error) { +func (s service) GetSignedDocument(ctx context.Context, envelopeID, documentID string) ([]byte, error) { f := logrus.Fields{ "functionName": "v2.getSignedDocument", "envelopeID": envelopeID, @@ -623,3 +623,69 @@ func (s service) getSignedDocument(ctx context.Context, envelopeID, documentID s return responsePayload, nil } + +func (s *service) GetEnvelopeDocuments(ctx context.Context, envelopeID string) ([]DocuSignDocument, error) { + f := logrus.Fields{ + "functionName": "v2.GetEnvelopeDocuments", + "envelopeID": envelopeID, + } + + // Get the access token + accessToken, err := s.getAccessToken(ctx) + + if err != nil { + log.WithFields(f).WithError(err).Warnf("problem getting the access token") + return nil, err + } + + // Create the request + url := fmt.Sprintf("%s/accounts/%s/envelopes/%s/documents", utils.GetProperty("DOCUSIGN_ROOT_URL"), utils.GetProperty("DOCUSIGN_ACCOUNT_ID"), envelopeID) + + req, err := http.NewRequest("GET", url, nil) + + if err != nil { + log.WithFields(f).WithError(err).Warnf("problem creating the HTTP request") + return nil, err + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken)) + + // Make the request + client := &http.Client{} + + resp, err := client.Do(req) + + if err != nil { + log.WithFields(f).WithError(err).Warnf("problem making the HTTP request") + return nil, err + } + + defer func() { + if err = resp.Body.Close(); err != nil { + log.WithFields(f).WithError(err).Warnf("problem closing the response body") + } + }() + + responsePayload, err := io.ReadAll(resp.Body) + + if err != nil { + log.WithFields(f).WithError(err).Warnf("problem reading the response body") + return nil, err + } + + if resp.StatusCode != http.StatusOK { + log.WithFields(f).Warnf("problem making the HTTP request - status code: %d - response : %s", resp.StatusCode, string(responsePayload)) + return nil, errors.New("problem making the HTTP request") + } + + var response []DocuSignDocument + + err = json.Unmarshal(responsePayload, &response) + + if err != nil { + log.WithFields(f).WithError(err).Warnf("problem unmarshalling the response body") + return nil, err + } + + return response, nil +} diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go index 33b08a1c9..2438ea8f9 100644 --- a/cla-backend-go/v2/sign/service.go +++ b/cla-backend-go/v2/sign/service.go @@ -80,6 +80,8 @@ type Service interface { GetSignURL(email, recipientID, userName, clientUserId, envelopeID, returnURL string) (string, error) createEnvelope(ctx context.Context, payload *DocuSignEnvelopeRequest) (string, error) addDocumentToEnvelope(ctx context.Context, envelopeID, documentName string, document []byte) error + GetSignedDocument(ctx context.Context, envelopeID, documentID string) ([]byte, error) + GetEnvelopeDocuments(ctx context.Context, envelopeID string) ([]DocuSignDocument, error) RequestCorporateSignature(ctx context.Context, lfUsername string, authorizationHeader string, input *models.CorporateSignatureInput) (*models.CorporateSignatureOutput, error) RequestIndividualSignature(ctx context.Context, input *models.IndividualSignatureInput, preferredEmail string) (*models.IndividualSignatureOutput, error) @@ -493,7 +495,7 @@ func (s *service) SignedIndividualCallbackGithub(ctx context.Context, payload [] //Get signed document log.WithFields(f).Debugf("getting signed document for envelope ID: %s", envelopeID) - signedDocument, err := s.getSignedDocument(ctx, envelopeID, documentID) + signedDocument, err := s.GetSignedDocument(ctx, envelopeID, documentID) if err != nil { log.WithFields(f).WithError(err).Warnf("unable to get signed document for envelope ID: %s", envelopeID) return err @@ -765,7 +767,7 @@ func (s *service) SignedIndividualCallbackGitlab(ctx context.Context, payload [] //Get signed document log.WithFields(f).Debugf("getting signed document for envelope ID: %s", envelopeID) - signedDocument, err := s.getSignedDocument(ctx, envelopeID, documentID) + signedDocument, err := s.GetSignedDocument(ctx, envelopeID, documentID) if err != nil { log.WithFields(f).WithError(err).Warnf("unable to get signed document for envelope ID: %s", envelopeID) return err @@ -925,7 +927,7 @@ func (s *service) SignedIndividualCallbackGerrit(ctx context.Context, payload [] //Get signed document log.WithFields(f).Debugf("getting signed document for envelope ID: %s", envelopeID) - signedDocument, err := s.getSignedDocument(ctx, envelopeID, documentID) + signedDocument, err := s.GetSignedDocument(ctx, envelopeID, documentID) if err != nil { log.WithFields(f).WithError(err).Warnf("unable to get signed document for envelope ID: %s", envelopeID) return err @@ -1161,7 +1163,7 @@ func (s *service) SignedCorporateCallback(ctx context.Context, payload []byte, c // store document on S3 log.WithFields(f).Debugf("storing signed document on S3...") - signedDocument, err := s.getSignedDocument(ctx, envelopeID, info.EnvelopeStatus.DocumentStatuses[0].ID) + signedDocument, err := s.GetSignedDocument(ctx, envelopeID, info.EnvelopeStatus.DocumentStatuses[0].ID) if err != nil { log.WithFields(f).WithError(err).Warnf("unable to get signed document for envelope ID: %s", envelopeID)