github.com/rainforestapp/rainforest-cli@v2.12.0+incompatible/rainforest/files.go (about) 1 package rainforest 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "encoding/hex" 7 "fmt" 8 "io/ioutil" 9 "mime" 10 "mime/multipart" 11 "net/http" 12 "os" 13 "path/filepath" 14 "strconv" 15 ) 16 17 // uploadedFile represents a file that has been uploaded to Rainforest 18 type uploadedFile struct { 19 ID int `json:"id"` 20 Signature string `json:"signature"` 21 Digest string `json:"digest"` 22 MimeType string `json:"mime_type"` 23 Size int64 `json:"size"` 24 Name string `json:"name"` 25 } 26 27 // getUploadedFiles returns information for all all files uploaded to the 28 // given test before. 29 func (c *Client) getUploadedFiles(testID int) ([]uploadedFile, error) { 30 req, err := c.NewRequest("GET", "tests/"+strconv.Itoa(testID)+"/files", nil) 31 if err != nil { 32 return nil, err 33 } 34 35 var fileResp []uploadedFile 36 _, err = c.Do(req, &fileResp) 37 return fileResp, err 38 } 39 40 // awsFileInfo represents the response when uploading new file data to Rainforest. 41 // It contains information used to upload data the file to AWS. 42 type awsFileInfo struct { 43 FileID int `json:"file_id"` 44 FileSignature string `json:"file_signature"` 45 URL string `json:"aws_url"` 46 Key string `json:"aws_key"` 47 AccessID string `json:"aws_access_id"` 48 Policy string `json:"aws_policy"` 49 ACL string `json:"aws_acl"` 50 Signature string `json:"aws_signature"` 51 } 52 53 // multipartFormRequest creates a http.Request containing the required body for 54 // uploading a file to AWS given the values stored in the receiving awsFileInfo struct. 55 func (aws *awsFileInfo) multipartFormRequest(fileName string, fileContents []byte) (*http.Request, error) { 56 var req *http.Request 57 fileExt := filepath.Ext(fileName) 58 59 buffer := new(bytes.Buffer) 60 writer := multipart.NewWriter(buffer) 61 62 writer.WriteField("key", aws.Key) 63 writer.WriteField("AWSAccessKeyId", aws.AccessID) 64 writer.WriteField("acl", aws.ACL) 65 writer.WriteField("policy", aws.Policy) 66 writer.WriteField("signature", aws.Signature) 67 writer.WriteField("Content-Type", mime.TypeByExtension(fileExt)) 68 69 part, err := writer.CreateFormFile("file", fileName) 70 part.Write(fileContents) 71 72 url := aws.URL 73 req, err = http.NewRequest("POST", url, buffer) 74 if err != nil { 75 return req, err 76 } 77 78 req.Header.Set("Content-Type", writer.FormDataContentType()) 79 writer.Close() 80 req.ContentLength = int64(buffer.Len()) 81 82 return req, nil 83 } 84 85 // createTestFile creates a uploadedFile resource by sending file information to 86 // Rainforest. This information is used for uploading the file contents to AWS. 87 func (c *Client) createTestFile(testID int, file *os.File, fileContents []byte) (*awsFileInfo, error) { 88 fileName := file.Name() 89 fileInfo, err := file.Stat() 90 91 if err != nil { 92 return &awsFileInfo{}, err 93 } 94 95 md5CheckSum := md5.Sum(fileContents) 96 hexDigest := hex.EncodeToString(md5CheckSum[:16]) 97 98 body := uploadedFile{ 99 MimeType: mime.TypeByExtension(filepath.Ext(fileName)), 100 Size: fileInfo.Size(), 101 Name: fileName, 102 Digest: hexDigest, 103 } 104 105 url := "tests/" + strconv.Itoa(testID) + "/files" 106 req, err := c.NewRequest("POST", url, body) 107 if err != nil { 108 return &awsFileInfo{}, err 109 } 110 111 awsInfo := &awsFileInfo{} 112 _, err = c.Do(req, awsInfo) 113 return awsInfo, err 114 } 115 116 // uploadEmbeddedFile is a function that uploads the given embedded file's contents to AWS 117 func (c *Client) uploadEmbeddedFile(fileName string, fileContents []byte, awsInfo *awsFileInfo) error { 118 req, err := awsInfo.multipartFormRequest(fileName, fileContents) 119 if err != nil { 120 return err 121 } 122 123 var resp *http.Response 124 resp, err = c.client.Do(req) 125 126 if err != nil { 127 return err 128 } 129 130 status := resp.StatusCode 131 if status >= 300 { 132 var body []byte 133 body, err = ioutil.ReadAll(resp.Body) 134 135 if err != nil { 136 return err 137 } 138 139 return fmt.Errorf("There was an error uploading your file - %v: %v", fileName, string(body)) 140 } 141 142 return nil 143 }