diff --git a/CHANGELOG.md b/CHANGELOG.md index e826f97a..892fc3e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.5.1 +# Added +- Add `BlockRaw` method to algod API V2 client. # 1.5.0 # Added - Support for Applications diff --git a/README.md b/README.md index cef3587c..38e253e5 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,7 @@ func main() { if wallet.Name == "testwallet" { fmt.Printf("found wallet '%s' with ID: %s\n", wallet.Name, wallet.ID) exampleWalletID = wallet.ID + break } } // Get a wallet handle diff --git a/client/v2/algod/algod.go b/client/v2/algod/algod.go index b7565eca..2faafc44 100644 --- a/client/v2/algod/algod.go +++ b/client/v2/algod/algod.go @@ -11,20 +11,25 @@ const algodAuthHeader = "X-Algo-API-Token" type Client common.Client // get performs a GET request to the specific path against the server, assumes JSON response -func (c *Client) get(ctx context.Context, response interface{}, path string, request interface{}, headers []*common.Header) error { - return (*common.Client)(c).Get(ctx, response, path, request, headers) +func (c *Client) get(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error { + return (*common.Client)(c).Get(ctx, response, path, body, headers) } // getMsgpack performs a GET request to the specific path against the server, assumes msgpack response -func (c *Client) getMsgpack(ctx context.Context, response interface{}, path string, request interface{}, headers []*common.Header) error { - return (*common.Client)(c).GetRawMsgpack(ctx, response, path, request, headers) +func (c *Client) getMsgpack(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error { + return (*common.Client)(c).GetRawMsgpack(ctx, response, path, body, headers) +} + +// getMsgpack performs a GET request to the specific path against the server, assumes msgpack response +func (c *Client) getRaw(ctx context.Context, path string, body interface{}, headers []*common.Header) ([]byte, error) { + return (*common.Client)(c).GetRaw(ctx, path, body, headers) } // post sends a POST request to the given path with the given request object. // No query parameters will be sent if request is nil. // response must be a pointer to an object as post writes the response there. -func (c *Client) post(ctx context.Context, response interface{}, path string, request interface{}, headers []*common.Header) error { - return (*common.Client)(c).Post(ctx, response, path, request, headers) +func (c *Client) post(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error { + return (*common.Client)(c).Post(ctx, response, path, body, headers) } // MakeClient is the factory for constructing a ClientV2 for a given endpoint. @@ -42,6 +47,10 @@ func (c *Client) Block(round uint64) *Block { return &Block{c: c, round: round} } +func (c *Client) BlockRaw(round uint64) *BlockRaw { + return &BlockRaw{c: c, round: round} +} + func (c *Client) HealthCheck() *HealthCheck { return &HealthCheck{c: c} } diff --git a/client/v2/algod/blockRaw.go b/client/v2/algod/blockRaw.go new file mode 100644 index 00000000..1a16f918 --- /dev/null +++ b/client/v2/algod/blockRaw.go @@ -0,0 +1,22 @@ +package algod + +import ( + "context" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// BlockRaw contains metadata required to execute a BlockRaw query. +type BlockRaw struct { + c *Client + round uint64 + p models.GetBlockParams +} + +// Do executes the BlockRaw query and gets the results. +func (s *BlockRaw) Do(ctx context.Context, headers ...*common.Header) (result []byte, err error) { + s.p.Format = "msgpack" + return s.c.getRaw(ctx, fmt.Sprintf("/v2/blocks/%d", s.round), s.p, headers) +} diff --git a/client/v2/common/common.go b/client/v2/common/common.go index 31fc0b51..710f257c 100644 --- a/client/v2/common/common.go +++ b/client/v2/common/common.go @@ -102,35 +102,35 @@ func mergeRawQueries(q1, q2 string) string { return q1 + "&" + q2 } -// submitForm is a helper used for submitting (ex.) GETs and POSTs to the server -func (client *Client) submitFormRaw(ctx context.Context, path string, request interface{}, requestMethod string, encodeJSON bool, headers []*Header) (resp *http.Response, err error) { +// submitFormRaw is a helper used for submitting (ex.) GETs and POSTs to the server +func (client *Client) submitFormRaw(ctx context.Context, path string, body interface{}, requestMethod string, encodeJSON bool, headers []*Header) (resp *http.Response, err error) { queryURL := client.serverURL queryURL.Path += path var req *http.Request - var body io.Reader - if request != nil { + var bodyReader io.Reader + if body != nil { if requestMethod == "POST" && rawRequestPaths[path] { - reqBytes, ok := request.([]byte) + reqBytes, ok := body.([]byte) if !ok { - return nil, fmt.Errorf("couldn't decode raw request as bytes") + return nil, fmt.Errorf("couldn't decode raw body as bytes") } - body = bytes.NewBuffer(reqBytes) + bodyReader = bytes.NewBuffer(reqBytes) } else { - v, err := query.Values(request) + v, err := query.Values(body) if err != nil { return nil, err } queryURL.RawQuery = mergeRawQueries(queryURL.RawQuery, v.Encode()) if encodeJSON { - jsonValue, _ := json.Marshal(request) - body = bytes.NewBuffer(jsonValue) + jsonValue, _ := json.Marshal(body) + bodyReader = bytes.NewBuffer(jsonValue) } } } - req, err = http.NewRequest(requestMethod, queryURL.String(), body) + req, err = http.NewRequest(requestMethod, queryURL.String(), bodyReader) if err != nil { return nil, err } @@ -166,8 +166,8 @@ func (client *Client) submitFormRaw(ctx context.Context, path string, request in return resp, nil } -func (client *Client) submitForm(ctx context.Context, response interface{}, path string, request interface{}, requestMethod string, encodeJSON bool, headers []*Header) error { - resp, err := client.submitFormRaw(ctx, path, request, requestMethod, encodeJSON, headers) +func (client *Client) submitForm(ctx context.Context, response interface{}, path string, body interface{}, requestMethod string, encodeJSON bool, headers []*Header) error { + resp, err := client.submitFormRaw(ctx, path, body, requestMethod, encodeJSON, headers) if err != nil { return err } @@ -178,24 +178,38 @@ func (client *Client) submitForm(ctx context.Context, response interface{}, path } // Get performs a GET request to the specific path against the server -func (client *Client) Get(ctx context.Context, response interface{}, path string, request interface{}, headers []*Header) error { - return client.submitForm(ctx, response, path, request, "GET", false /* encodeJSON */, headers) +func (client *Client) Get(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error { + return client.submitForm(ctx, response, path, body, "GET", false /* encodeJSON */, headers) } -func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, path string, request interface{}, headers []*Header) error { - resp, err := client.submitFormRaw(ctx, path, request, "GET", false /* encodeJSON */, headers) +// GetRaw performs a GET request to the specific path against the server and returns the raw body bytes. +func (client *Client) GetRaw(ctx context.Context, path string, body interface{}, headers []*Header) (response []byte, err error) { + var resp *http.Response + resp, err = client.submitFormRaw(ctx, path, body, "GET", false /* encodeJSON */, headers) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + return ioutil.ReadAll(resp.Body) +} + +// GetRawMsgpack performs a GET request to the specific path against the server and returns the decoded messagepack response. +func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error { + resp, err := client.submitFormRaw(ctx, path, body, "GET", false /* encodeJSON */, headers) if err != nil { return err } defer resp.Body.Close() + dec := msgpack.NewDecoder(resp.Body) return dec.Decode(&response) } -// Post sends a POST request to the given path with the given request object. -// No query parameters will be sent if request is nil. +// Post sends a POST request to the given path with the given body object. +// No query parameters will be sent if body is nil. // response must be a pointer to an object as post writes the response there. -func (client *Client) Post(ctx context.Context, response interface{}, path string, request interface{}, headers []*Header) error { - return client.submitForm(ctx, response, path, request, "POST", true /* encodeJSON */, headers) +func (client *Client) Post(ctx context.Context, response interface{}, path string, body interface{}, headers []*Header) error { + return client.submitForm(ctx, response, path, body, "POST", true /* encodeJSON */, headers) } diff --git a/client/v2/indexer/indexer.go b/client/v2/indexer/indexer.go index a3a9aaac..6f01060c 100644 --- a/client/v2/indexer/indexer.go +++ b/client/v2/indexer/indexer.go @@ -11,8 +11,8 @@ const indexerAuthHeader = "X-Indexer-API-Token" type Client common.Client // get performs a GET request to the specific path against the server -func (c *Client) get(ctx context.Context, response interface{}, path string, request interface{}, headers []*common.Header) error { - return (*common.Client)(c).Get(ctx, response, path, request, headers) +func (c *Client) get(ctx context.Context, response interface{}, path string, body interface{}, headers []*common.Header) error { + return (*common.Client)(c).Get(ctx, response, path, body, headers) } // MakeClient is the factory for constructing an IndexerClient for a given endpoint.