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  }