Skip to content

Commit

Permalink
[chore] Use query for GET/DELETE requests, per HTTP spec (#229)
Browse files Browse the repository at this point in the history
* - Use query for GET/DELETE requests, per HTTP spec
- Denote URL vs JSON key names for parameter serialization
- Re-record all cassettes
  • Loading branch information
nwithan8 committed Aug 7, 2024
1 parent c1308e1 commit 0dede49
Show file tree
Hide file tree
Showing 182 changed files with 5,059 additions and 4,780 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## Next Release

- Update HTTP logic to use query for GET/DELETE requests and body for POST/PUT/PATCH requests

## v4.5.0 (2024-07-24)

- Add new claim-related functions: `CreateClaim`, `GetClaim`, `ListClaims`, `GetNextClaimPage` and `CancelClaim`
Expand Down
88 changes: 44 additions & 44 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,86 +8,86 @@ import (
// AddressVerificationFieldError provides additional information on address
// validation errors.
type AddressVerificationFieldError struct {
Code string `json:"code,omitempty"`
Field string `json:"field,omitempty"`
Message string `json:"message,omitempty"`
Suggestion string `json:"suggestion,omitempty"`
Code string `json:"code,omitempty" url:"code,omitempty"`
Field string `json:"field,omitempty" url:"field,omitempty"`
Message string `json:"message,omitempty" url:"message,omitempty"`
Suggestion string `json:"suggestion,omitempty" url:"suggestion,omitempty"`
}

// AddressVerificationDetails contains extra information related to address
// verification.
type AddressVerificationDetails struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
TimeZone string `json:"time_zone"`
Latitude float64 `json:"latitude,omitempty" url:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty" url:"longitude,omitempty"`
TimeZone string `json:"time_zone,omitempty" url:"time_zone,omitempty"`
}

// AddressVerification holds data relating to address verification status.
type AddressVerification struct {
Success bool `json:"success"`
Errors []*AddressVerificationFieldError `json:"errors"`
Details *AddressVerificationDetails `json:"details"`
Success bool `json:"success,omitempty" url:"success,omitempty"`
Errors []*AddressVerificationFieldError `json:"errors,omitempty" url:"errors,omitempty"`
Details *AddressVerificationDetails `json:"details,omitempty" url:"details,omitempty"`
}

// AddressVerifications contains the result of the requested address
// verifications.
type AddressVerifications struct {
ZIP4 *AddressVerification `json:"zip4"`
Delivery *AddressVerification `json:"delivery"`
ZIP4 *AddressVerification `json:"zip4,omitempty" url:"zip4,omitempty"`
Delivery *AddressVerification `json:"delivery,omitempty" url:"delivery,omitempty"`
}

// Address objects are used to represent people, places, and organizations in a
// number of contexts.
type Address struct {
ID string `json:"id,omitempty"`
Object string `json:"object,omitempty"`
Reference string `json:"reference,omitempty"`
Mode string `json:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty"`
UpdatedAt *DateTime `json:"updated_at,omitempty"`
Street1 string `json:"street1,omitempty"`
Street2 string `json:"street2,omitempty"`
City string `json:"city,omitempty"`
State string `json:"state,omitempty"`
Zip string `json:"zip,omitempty"`
Country string `json:"country,omitempty"`
Name string `json:"name,omitempty"`
Company string `json:"company,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
Residential bool `json:"residential,omitempty"`
CarrierFacility string `json:"carrier_facility,omitempty"`
FederalTaxID string `json:"federal_tax_id,omitempty"`
StateTaxID string `json:"state_tax_id,omitempty"`
Verifications *AddressVerifications `json:"verifications,omitempty"`
ID string `json:"id,omitempty" url:"id,omitempty"`
Object string `json:"object,omitempty" url:"object,omitempty"`
Reference string `json:"reference,omitempty" url:"reference,omitempty"`
Mode string `json:"mode,omitempty" url:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty" url:"created_at,omitempty"`
UpdatedAt *DateTime `json:"updated_at,omitempty" url:"updated_at,omitempty"`
Street1 string `json:"street1,omitempty" url:"street1,omitempty"`
Street2 string `json:"street2,omitempty" url:"street2,omitempty"`
City string `json:"city,omitempty" url:"city,omitempty"`
State string `json:"state,omitempty" url:"state,omitempty"`
Zip string `json:"zip,omitempty" url:"zip,omitempty"`
Country string `json:"country,omitempty" url:"country,omitempty"`
Name string `json:"name,omitempty" url:"name,omitempty"`
Company string `json:"company,omitempty" url:"company,omitempty"`
Phone string `json:"phone,omitempty" url:"phone,omitempty"`
Email string `json:"email,omitempty" url:"email,omitempty"`
Residential bool `json:"residential,omitempty" url:"residential,omitempty"`
CarrierFacility string `json:"carrier_facility,omitempty" url:"carrier_facility,omitempty"`
FederalTaxID string `json:"federal_tax_id,omitempty" url:"federal_tax_id,omitempty"`
StateTaxID string `json:"state_tax_id,omitempty" url:"state_tax_id,omitempty"`
Verifications *AddressVerifications `json:"verifications,omitempty" url:"verifications,omitempty"`
}

// CreateAddressOptions is used to specify verification options for address
// creation.
type CreateAddressOptions struct {
// TODO: These should be booleans, not strings, like the other libs
Verify []string `json:"verify,omitempty"`
VerifyStrict []string `json:"verify_strict,omitempty"`
Verify []string `json:"verify,omitempty" url:"verify,omitempty"`
VerifyStrict []string `json:"verify_strict,omitempty" url:"verify_strict,omitempty"`
}

type createAddressRequest struct {
*CreateAddressOptions
Address *Address `json:"address,omitempty"`
Address *Address `json:"address,omitempty" url:"address,omitempty"`
}

type AddressVerifyResponse struct {
Address *Address `json:"address,omitempty"`
Address *Address `json:"address,omitempty" url:"address,omitempty"`
}

// ListAddressResult holds the results from the list addresses API.
type ListAddressResult struct {
Addresses []*Address `json:"addresses,omitempty"`
Addresses []*Address `json:"addresses,omitempty" url:"addresses,omitempty"`
PaginatedCollection
}

// For some reason, the verify API returns the address in a nested dictionary.
type verifyAddressResponse struct {
Address **Address `json:"address,omitempty"`
Address **Address `json:"address,omitempty" url:"address,omitempty"`
}

// CreateAddress submits a request to create a new address, and returns the
Expand Down Expand Up @@ -115,7 +115,7 @@ func (c *Client) CreateAddress(in *Address, opts *CreateAddressOptions) (out *Ad
// allows specifying a context that can interrupt the request.
func (c *Client) CreateAddressWithContext(ctx context.Context, in *Address, opts *CreateAddressOptions) (out *Address, err error) {
req := &createAddressRequest{CreateAddressOptions: opts, Address: in}
err = c.post(ctx, "addresses", req, &out)
err = c.do(ctx, http.MethodPost, "addresses", req, &out)
return
}

Expand All @@ -127,7 +127,7 @@ func (c *Client) ListAddresses(opts *ListOptions) (out *ListAddressResult, err e
// ListAddressesWithContext performs the same operation as ListAddresses, but
// allows specifying a context that can interrupt the request.
func (c *Client) ListAddressesWithContext(ctx context.Context, opts *ListOptions) (out *ListAddressResult, err error) {
err = c.do(ctx, http.MethodGet, "addresses", c.convertOptsToURLValues(opts), &out)
err = c.do(ctx, http.MethodGet, "addresses", opts, &out)
return
}

Expand Down Expand Up @@ -172,7 +172,7 @@ func (c *Client) VerifyAddress(addressID string) (out *Address, err error) {
func (c *Client) VerifyAddressWithContext(ctx context.Context, addressID string) (out *Address, err error) {
path := "addresses/" + addressID + "/verify"
res := &verifyAddressResponse{Address: &out}
err = c.get(ctx, path, res)
err = c.do(ctx, http.MethodGet, path, nil, res)
return
}

Expand All @@ -184,7 +184,7 @@ func (c *Client) GetAddress(addressID string) (out *Address, err error) {
// GetAddressWithContext performs the same operation as GetAddress, but allows
// specifying a context that can interrupt the request.
func (c *Client) GetAddressWithContext(ctx context.Context, addressID string) (out *Address, err error) {
err = c.get(ctx, "addresses/"+addressID, &out)
err = c.do(ctx, http.MethodGet, "addresses/"+addressID, nil, &out)
return
}

Expand All @@ -198,7 +198,7 @@ func (c *Client) CreateAndVerifyAddress(in *Address, opts *CreateAddressOptions)
func (c *Client) CreateAndVerifyAddressWithContext(ctx context.Context, in *Address, opts *CreateAddressOptions) (out *Address, err error) {
req := &createAddressRequest{CreateAddressOptions: opts, Address: in}
response := AddressVerifyResponse{}
err = c.post(ctx, "addresses/create_and_verify", req, &response)
err = c.do(ctx, http.MethodPost, "addresses/create_and_verify", req, &response)
out = response.Address
return
}
17 changes: 9 additions & 8 deletions api_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ package easypost

import (
"context"
"net/http"
)

// APIKey represents a single API key.
type APIKey struct {
Object string `json:"object,omitempty"`
Mode string `json:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty"`
Key string `json:"key,omitempty"`
Object string `json:"object,omitempty" url:"object,omitempty"`
Mode string `json:"mode,omitempty" url:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty" url:"created_at,omitempty"`
Key string `json:"key,omitempty" url:"key,omitempty"`
}

// APIKeys contains information about a list of API keys for the given user and
// any child users.
type APIKeys struct {
ID string `json:"id,omitempty"`
Children []*APIKeys `json:"children,omitempty"`
Keys []*APIKey `json:"keys,omitempty"`
ID string `json:"id,omitempty" url:"id,omitempty"`
Children []*APIKeys `json:"children,omitempty" url:"children,omitempty"`
Keys []*APIKey `json:"keys,omitempty" url:"keys,omitempty"`
}

// GetAPIKeys returns the list of API keys associated with the current user.
Expand All @@ -28,7 +29,7 @@ func (c *Client) GetAPIKeys() (out *APIKeys, err error) {
// GetAPIKeysWithContext performs the same operation as GetAPIKeys, but allows
// specifying a context that can interrupt the request.
func (c *Client) GetAPIKeysWithContext(ctx context.Context) (out *APIKeys, err error) {
err = c.get(ctx, "api_keys", &out)
err = c.do(ctx, http.MethodGet, "api_keys", nil, &out)
return
}

Expand Down
65 changes: 34 additions & 31 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,45 @@ package easypost
import (
"context"
"net/http"
"net/url"
)

// BatchStatus contains counts of statuses for the shipments in a batch.
type BatchStatus struct {
PostagePurchased int `json:"postage_purchased,omitempty"`
PostagePurchaseFailed int `json:"postage_purchase_failed,omitempty"`
QueuedForPurchase int `json:"queued_for_purchase,omitempty"`
CreationFailed int `json:"creation_failed,omitempty"`
PostagePurchased int `json:"postage_purchased,omitempty" url:"postage_purchased,omitempty"`
PostagePurchaseFailed int `json:"postage_purchase_failed,omitempty" url:"postage_purchase_failed,omitempty"`
QueuedForPurchase int `json:"queued_for_purchase,omitempty" url:"queued_for_purchase,omitempty"`
CreationFailed int `json:"creation_failed,omitempty" url:"creation_failed,omitempty"`
}

// Batch represents a batch of shipments.
type Batch struct {
ID string `json:"id,omitempty"`
Object string `json:"object,omitempty"`
Reference string `json:"reference,omitempty"`
Mode string `json:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty"`
UpdatedAt *DateTime `json:"updated_at,omitempty"`
State string `json:"state,omitempty"`
NumShipments int `json:"num_shipments,omitempty"`
Shipments []*Shipment `json:"shipments,omitempty"`
Status *BatchStatus `json:"status,omitempty"`
LabelURL string `json:"label_url,omitempty"`
ScanForm *ScanForm `json:"scan_form,omitempty"`
Pickup *Pickup `json:"pickup,omitempty"`
ID string `json:"id,omitempty" url:"id,omitempty"`
Object string `json:"object,omitempty" url:"object,omitempty"`
Reference string `json:"reference,omitempty" url:"reference,omitempty"`
Mode string `json:"mode,omitempty" url:"mode,omitempty"`
CreatedAt *DateTime `json:"created_at,omitempty" url:"created_at,omitempty"`
UpdatedAt *DateTime `json:"updated_at,omitempty" url:"updated_at,omitempty"`
State string `json:"state,omitempty" url:"state,omitempty"`
NumShipments int `json:"num_shipments,omitempty" url:"num_shipments,omitempty"`
Shipments []*Shipment `json:"shipments,omitempty" url:"shipments,omitempty"`
Status *BatchStatus `json:"status,omitempty" url:"status,omitempty"`
LabelURL string `json:"label_url,omitempty" url:"label_url,omitempty"`
ScanForm *ScanForm `json:"scan_form,omitempty" url:"scan_form,omitempty"`
Pickup *Pickup `json:"pickup,omitempty" url:"pickup,omitempty"`
}

type batchRequest struct {
Batch *Batch `json:"batch,omitempty"`
Batch *Batch `json:"batch,omitempty" url:"batch,omitempty"`
}

// ListBatchesResult holds the results from the list insurances API.
type ListBatchesResult struct {
Batch []*Batch `json:"batches,omitempty"`
Batch []*Batch `json:"batches,omitempty" url:"batches,omitempty"`
PaginatedCollection
}

type addRemoveShipmentsRequest struct {
Shipments []*Shipment `json:"shipments,omitempty"`
Shipments []*Shipment `json:"shipments,omitempty" url:"shipments,omitempty"`
}

// CreateBatch creates a new batch of shipments. It optionally accepts one or
Expand All @@ -63,7 +62,7 @@ func (c *Client) CreateBatch(in ...*Shipment) (out *Batch, err error) {
// specifying a context that can interrupt the request.
func (c *Client) CreateBatchWithContext(ctx context.Context, in ...*Shipment) (out *Batch, err error) {
req := batchRequest{Batch: &Batch{Shipments: in}}
err = c.post(ctx, "batches", req, &out)
err = c.do(ctx, http.MethodPost, "batches", req, &out)
return
}

Expand All @@ -75,7 +74,7 @@ func (c *Client) ListBatches(opts *ListOptions) (out *ListBatchesResult, err err
// ListBatchesWithContext performs the same operation as ListBatches, but
// allows specifying a context that can interrupt the request.
func (c *Client) ListBatchesWithContext(ctx context.Context, opts *ListOptions) (out *ListBatchesResult, err error) {
err = c.do(ctx, http.MethodGet, "batches", c.convertOptsToURLValues(opts), &out)
err = c.do(ctx, http.MethodGet, "batches", opts, &out)
return
}

Expand All @@ -92,7 +91,7 @@ func (c *Client) AddShipmentsToBatch(batchID string, shipments ...*Shipment) (ou
// request.
func (c *Client) AddShipmentsToBatchWithContext(ctx context.Context, batchID string, shipments ...*Shipment) (out *Batch, err error) {
req := addRemoveShipmentsRequest{Shipments: shipments}
err = c.post(ctx, "batches/"+batchID+"/add_shipments", req, &out)
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/add_shipments", req, &out)
return
}

Expand All @@ -107,7 +106,7 @@ func (c *Client) RemoveShipmentsFromBatch(batchID string, shipments ...*Shipment
// the request.
func (c *Client) RemoveShipmentsFromBatchWithContext(ctx context.Context, batchID string, shipments ...*Shipment) (out *Batch, err error) {
req := addRemoveShipmentsRequest{Shipments: shipments}
err = c.post(ctx, "batches/"+batchID+"/remove_shipments", req, &out)
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/remove_shipments", req, &out)
return
}

Expand All @@ -120,7 +119,7 @@ func (c *Client) BuyBatch(batchID string) (out *Batch, err error) {
// BuyBatchWithContext performs the same operation as BuyBatch, but allows
// specifying a context that can interrupt the request.
func (c *Client) BuyBatchWithContext(ctx context.Context, batchID string) (out *Batch, err error) {
err = c.post(ctx, "batches/"+batchID+"/buy", nil, &out)
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/buy", nil, &out)
return
}

Expand All @@ -132,7 +131,7 @@ func (c *Client) GetBatch(batchID string) (out *Batch, err error) {
// GetBatchWithContext performs the same operation as GetBatch, but allows
// specifying a context that can interrupt the request.
func (c *Client) GetBatchWithContext(ctx context.Context, batchID string) (out *Batch, err error) {
err = c.get(ctx, "batches/"+batchID, &out)
err = c.do(ctx, http.MethodGet, "batches/"+batchID, nil, &out)
return
}

Expand All @@ -145,8 +144,10 @@ func (c *Client) GetBatchLabels(batchID, format string) (out *Batch, err error)
// GetBatchLabelsWithContext performs the same operation as GetBatchLabels, but
// allows specifying a context that can interrupt the request.
func (c *Client) GetBatchLabelsWithContext(ctx context.Context, batchID, format string) (out *Batch, err error) {
params := url.Values{"file_format": []string{format}}
err = c.post(ctx, "batches/"+batchID+"/label", params, &out)
params := struct {
FileFormat string `json:"file_format,omitempty" url:"file_format,omitempty"`
}{FileFormat: format}
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/label", params, &out)
return
}

Expand All @@ -159,7 +160,9 @@ func (c *Client) CreateBatchScanForms(batchID, format string) (out *Batch, err e
// CreateBatchScanForms, but allows specifying a context that can interrupt the
// request.
func (c *Client) CreateBatchScanFormsWithContext(ctx context.Context, batchID, format string) (out *Batch, err error) {
vals := url.Values{"file_format": []string{format}}
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/scan_form", vals, &out)
params := struct {
FileFormat string `json:"file_format,omitempty" url:"file_format,omitempty"`
}{FileFormat: format}
err = c.do(ctx, http.MethodPost, "batches/"+batchID+"/scan_form", params, &out)
return
}
Loading

0 comments on commit 0dede49

Please sign in to comment.