Skip to content

Commit

Permalink
[#4254]Feature/S3 Upload
Browse files Browse the repository at this point in the history
- Added script to handle upload of missing icla documents on s3

Signed-off-by: Harold Wanyama <[email protected]>
  • Loading branch information
nickmango committed Apr 18, 2024
1 parent edf06e1 commit 1feeddf
Show file tree
Hide file tree
Showing 9 changed files with 478 additions and 6 deletions.
293 changes: 293 additions & 0 deletions cla-backend-go/cmd/s3_upload/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
// 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() {
ctx := context.Background()
f := logrus.Fields{
"functionName": "main",
}
// var toUpdate []*signatures.ItemSignature

dryRun := flag.Bool("dry-run", false, "dry run mode")

flag.Parse()

// Fetch all the signatures from 2024-02-01T00:00:00.000Z
startDate := "2024-02-01T00:00:00.000Z"

iclaSignatures, err := signatureRepo.GetICLAByDate(ctx, startDate)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem fetching ICLA signatures")
return
}

if dryRun != nil && *dryRun {
log.WithFields(f).Debug("dry-run mode enabled")
}

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")
}
1 change: 1 addition & 0 deletions cla-backend-go/cmd/s3_upload/s3_upload_report.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SignatureID,ProjectID,ReferenceID,ReferenceName,EnvelopeID,DocumentID,Comment,Status
2 changes: 2 additions & 0 deletions cla-backend-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions cla-backend-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
2 changes: 1 addition & 1 deletion cla-backend-go/project/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 1feeddf

Please sign in to comment.