github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/api/client/nib.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"strconv"
     9  
    10  	"github.com/hoffie/larasync/api"
    11  	"github.com/hoffie/larasync/api/common"
    12  	"github.com/hoffie/larasync/helpers/bincontainer"
    13  	"github.com/hoffie/larasync/repository"
    14  )
    15  
    16  // putNIBRequest builds a request for uploading NIB
    17  func (c *Client) putNIBRequest(nibID string, nibReader io.Reader) (*http.Request, error) {
    18  	req, err := http.NewRequest("PUT", c.BaseURL+"/nibs/"+nibID,
    19  		nibReader)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	req.Header.Set("Content-Type", "application/octet-stream")
    24  	common.SignWithKey(req, c.signingPrivateKey)
    25  	return req, nil
    26  }
    27  
    28  // handleNIBPreconditionError tries to get additional data from the precondition failed
    29  // error and returns the extracted error information.
    30  func handleNIBPreconditionError(resp *http.Response) error {
    31  	data, err := ioutil.ReadAll(resp.Body)
    32  	if err != nil {
    33  		return err
    34  	}
    35  	jsonError := &api.ContentIDsJSONError{}
    36  	err = json.Unmarshal(data, jsonError)
    37  	if err != nil {
    38  		return ErrUnexpectedStatus
    39  	}
    40  	return repository.NewErrNIBContentMissing(jsonError.MissingContentIDs)
    41  }
    42  
    43  // PutNIB uploads a NIB to the server
    44  func (c *Client) PutNIB(nibID string, nibReader io.Reader) error {
    45  	req, err := c.putNIBRequest(nibID, nibReader)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	resp, err := c.doRequest(
    50  		req,
    51  		http.StatusCreated, http.StatusOK, http.StatusPreconditionFailed,
    52  	)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	if resp.StatusCode == http.StatusPreconditionFailed {
    58  		err = handleNIBPreconditionError(resp)
    59  	}
    60  	return err
    61  }
    62  
    63  // NIBGetResponse encapsulates the response given from a NIB GET request.
    64  type NIBGetResponse struct {
    65  	NIBData             <-chan []byte
    66  	ServerTransactionID int64
    67  }
    68  
    69  // getNIBsRequest builds a request for getting a NIB list
    70  func (c *Client) getNIBsRequest() (*http.Request, error) {
    71  	return c.getNIBsFromTransactionRequest(0)
    72  }
    73  
    74  // getNIBsFromTransactionRequest builds a request and requests all data
    75  // from the passed Transaction ID.
    76  func (c *Client) getNIBsFromTransactionRequest(lastTransactionID int64) (*http.Request, error) {
    77  	req, err := http.NewRequest("GET", c.BaseURL+"/nibs", nil)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	if lastTransactionID != 0 {
    83  		query := req.URL.Query()
    84  		query.Add("from-transaction-id", strconv.FormatInt(lastTransactionID, 10))
    85  		req.URL.RawQuery = query.Encode()
    86  	}
    87  
    88  	common.SignWithKey(req, c.signingPrivateKey)
    89  	return req, nil
    90  }
    91  
    92  // GetNIBsFromTransactionID returns the list of all nibs from the passed server transaction.
    93  func (c *Client) GetNIBsFromTransactionID(lastTransactionID int64) (*NIBGetResponse, error) {
    94  	req, err := c.getNIBsFromTransactionRequest(lastTransactionID)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	return c.processNibGetRequest(req)
    99  }
   100  
   101  // GetNIBs returns the list of all nib byte representations.
   102  func (c *Client) GetNIBs() (*NIBGetResponse, error) {
   103  	req, err := c.getNIBsRequest()
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return c.processNibGetRequest(req)
   108  }
   109  
   110  // processNibGetRequest takes a request and processes the NIB GET request. Returns the
   111  // parsed bytes from the entry.
   112  func (c *Client) processNibGetRequest(req *http.Request) (*NIBGetResponse, error) {
   113  	resp, err := c.doRequest(req, http.StatusOK)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	bin := bincontainer.NewDecoder(resp.Body)
   118  	res := make(chan []byte, 100)
   119  	go func() {
   120  		for {
   121  			chunk, err := bin.ReadChunk()
   122  			if err == io.EOF {
   123  				close(res)
   124  				return
   125  			}
   126  			if err != nil {
   127  				break
   128  			}
   129  			res <- chunk
   130  		}
   131  		close(res)
   132  	}()
   133  	nibResponse := &NIBGetResponse{
   134  		NIBData:             res,
   135  		ServerTransactionID: parseTransactionID(resp),
   136  	}
   137  	return nibResponse, nil
   138  }
   139  
   140  // parseTransactionID tries to get the current server Transaction ID from the
   141  // passed response. Returns "0" if no transaction ID could be extracted.
   142  func parseTransactionID(resp *http.Response) int64 {
   143  	currentTransactionIDString := resp.Header.Get("X-Current-Transaction-Id")
   144  
   145  	currentTransactionID, err := strconv.ParseInt(currentTransactionIDString, 10, 64)
   146  	if err != nil {
   147  		return 0
   148  	}
   149  	return currentTransactionID
   150  }