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 }