From bc5c9ef1e39dcf02113dc436fc162f6ecf804da4 Mon Sep 17 00:00:00 2001 From: JBP <2850825+jybp@users.noreply.github.com> Date: Thu, 15 Aug 2019 22:05:50 +0200 Subject: [PATCH] Add tests and continue integration test --- Makefile | 2 +- browse.go | 392 +++++++++++++--------------------- browse_test.go | 30 ++- ebay.go | 15 +- go.mod | 1 + go.sum | 2 + offer.go | 33 +++ offer_test.go | 15 ++ test/integration/ebay_test.go | 57 +++-- 9 files changed, 267 insertions(+), 280 deletions(-) create mode 100644 offer.go create mode 100644 offer_test.go diff --git a/Makefile b/Makefile index 3467c8d..5587572 100644 --- a/Makefile +++ b/Makefile @@ -4,4 +4,4 @@ test: .PHONY: integration integration: - go test -tags integration -count=1 -v -run "GetItemByLegacyID" ./... \ No newline at end of file + go test -count=1 -v -run "Auction" ./test/integration -integration=true \ No newline at end of file diff --git a/browse.go b/browse.go index cc8a36d..3c0da4b 100644 --- a/browse.go +++ b/browse.go @@ -13,12 +13,19 @@ import ( // eBay API docs: https://developer.ebay.com/api-docs/buy/browse/overview.html type BrowseService service +// Valid values of the "buyingOptions" array for items. +const ( + BrowseBuyingOptionAuction = "AUCTION" + BrowseBuyingOptionFixedPrice = "FIXED_PRICE" +) + // OptBrowseContextualLocation adds the header containing contextualLocation. // It is strongly recommended that you use it when submitting Browse API methods. // // eBay API docs: https://developer.ebay.com/api-docs/buy/static/api-browse.html#Headers func OptBrowseContextualLocation(country, zip string) func(*http.Request) { return func(req *http.Request) { + const headerEndUserCtx = "X-EBAY-C-ENDUSERCTX" v := req.Header.Get(headerEndUserCtx) if len(v) > 0 { v += "," @@ -193,278 +200,167 @@ func (s *BrowseService) GetCompactItem(ctx context.Context, itemID string, opts // Item represents an eBay item. type Item struct { - AdditionalImages []struct { - Height string `json:"height"` + ItemID string `json:"itemId"` + SellerItemRevision string `json:"sellerItemRevision"` + Title string `json:"title"` + Subtitle string `json:"subtitle"` + ShortDescription string `json:"shortDescription"` + Price struct { + Value string `json:"value"` + Currency string `json:"currency"` + } `json:"price"` + CategoryPath string `json:"categoryPath"` + Condition string `json:"condition"` + ConditionID string `json:"conditionId"` + ItemLocation struct { + City string `json:"city"` + Country string `json:"country"` + } `json:"itemLocation"` + Image struct { + ImageURL string `json:"imageUrl"` + } `json:"image"` + AdditionalImages []struct { ImageURL string `json:"imageUrl"` - Width string `json:"width"` } `json:"additionalImages"` - AdultOnly string `json:"adultOnly"` - AgeGroup string `json:"ageGroup"` - BidCount string `json:"bidCount"` - Brand string `json:"brand"` - BuyingOptions []string `json:"buyingOptions"` - CategoryID string `json:"categoryId"` - CategoryPath string `json:"categoryPath"` - Color string `json:"color"` - Condition string `json:"condition"` - ConditionID string `json:"conditionId"` - CurrentBidPrice struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"currentBidPrice"` - Description string `json:"description"` - EnabledForGuestCheckout string `json:"enabledForGuestCheckout"` - EnergyEfficiencyClass string `json:"energyEfficiencyClass"` + MarketingPrice struct { + OriginalPrice struct { + Value string `json:"value"` + Currency string `json:"currency"` + } `json:"originalPrice"` + DiscountPercentage string `json:"discountPercentage"` + DiscountAmount struct { + Value string `json:"value"` + Currency string `json:"currency"` + } `json:"discountAmount"` + } `json:"marketingPrice"` + Color string `json:"color"` + Brand string `json:"brand"` + Seller struct { + Username string `json:"username"` + FeedbackPercentage string `json:"feedbackPercentage"` + FeedbackScore int `json:"feedbackScore"` + } `json:"seller"` + Gtin string `json:"gtin"` + Mpn string `json:"mpn"` Epid string `json:"epid"` EstimatedAvailabilities []struct { - AvailabilityThreshold string `json:"availabilityThreshold"` - AvailabilityThresholdType string `json:"availabilityThresholdType"` DeliveryOptions []string `json:"deliveryOptions"` + AvailabilityThresholdType string `json:"availabilityThresholdType"` + AvailabilityThreshold int `json:"availabilityThreshold"` EstimatedAvailabilityStatus string `json:"estimatedAvailabilityStatus"` - EstimatedAvailableQuantity string `json:"estimatedAvailableQuantity"` - EstimatedSoldQuantity string `json:"estimatedSoldQuantity"` + EstimatedSoldQuantity int `json:"estimatedSoldQuantity"` } `json:"estimatedAvailabilities"` - Gender string `json:"gender"` - Gtin string `json:"gtin"` - Image struct { - Height string `json:"height"` - ImageURL string `json:"imageUrl"` - Width string `json:"width"` - } `json:"image"` - InferredEpid string `json:"inferredEpid"` - ItemAffiliateWebURL string `json:"itemAffiliateWebUrl"` - ItemEndDate string `json:"itemEndDate"` - ItemID string `json:"itemId"` - ItemLocation struct { - AddressLine1 string `json:"addressLine1"` - AddressLine2 string `json:"addressLine2"` - City string `json:"city"` - Country string `json:"country"` - County string `json:"county"` - PostalCode string `json:"postalCode"` - StateOrProvince string `json:"stateOrProvince"` - } `json:"itemLocation"` - ItemWebURL string `json:"itemWebUrl"` - LocalizedAspects []struct { - Name string `json:"name"` - Type string `json:"type"` - Value string `json:"value"` - } `json:"localizedAspects"` - MarketingPrice struct { - DiscountAmount struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"discountAmount"` - DiscountPercentage string `json:"discountPercentage"` - OriginalPrice struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"originalPrice"` - } `json:"marketingPrice"` - Material string `json:"material"` - MinimumPriceToBid struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"minimumPriceToBid"` - Mpn string `json:"mpn"` - Pattern string `json:"pattern"` - Price struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"price"` - PriceDisplayCondition string `json:"priceDisplayCondition"` - PrimaryItemGroup struct { - ItemGroupAdditionalImages []struct { - Height string `json:"height"` - ImageURL string `json:"imageUrl"` - Width string `json:"width"` - } `json:"itemGroupAdditionalImages"` - ItemGroupHref string `json:"itemGroupHref"` - ItemGroupID string `json:"itemGroupId"` - ItemGroupImage struct { - Height string `json:"height"` - ImageURL string `json:"imageUrl"` - Width string `json:"width"` - } `json:"itemGroupImage"` - ItemGroupTitle string `json:"itemGroupTitle"` - ItemGroupType string `json:"itemGroupType"` - } `json:"primaryItemGroup"` - PrimaryProductReviewRating struct { - AverageRating string `json:"averageRating"` - RatingHistograms []struct { - Count string `json:"count"` - Rating string `json:"rating"` - } `json:"ratingHistograms"` - ReviewCount string `json:"reviewCount"` - } `json:"primaryProductReviewRating"` - Product struct { - AdditionalImages []struct { - Height string `json:"height"` - ImageURL string `json:"imageUrl"` - Width string `json:"width"` - } `json:"additionalImages"` - AdditionalProductIdentities []struct { - ProductIdentity []struct { - IdentifierType string `json:"identifierType"` - IdentifierValue string `json:"identifierValue"` - } `json:"productIdentity"` - } `json:"additionalProductIdentities"` - AspectGroups []struct { - Aspects []struct { - LocalizedName string `json:"localizedName"` - LocalizedValues []string `json:"localizedValues"` - } `json:"aspects"` - LocalizedGroupName string `json:"localizedGroupName"` - } `json:"aspectGroups"` - Brand string `json:"brand"` - Description string `json:"description"` - Gtins []string `json:"gtins"` - Image struct { - Height string `json:"height"` - ImageURL string `json:"imageUrl"` - Width string `json:"width"` - } `json:"image"` - Mpns []string `json:"mpns"` - Title string `json:"title"` - } `json:"product"` - ProductFicheWebURL string `json:"productFicheWebUrl"` - QuantityLimitPerBuyer string `json:"quantityLimitPerBuyer"` - ReservePriceMet string `json:"reservePriceMet"` - ReturnTerms struct { - ExtendedHolidayReturnsOffered string `json:"extendedHolidayReturnsOffered"` - RefundMethod string `json:"refundMethod"` - RestockingFeePercentage string `json:"restockingFeePercentage"` - ReturnInstructions string `json:"returnInstructions"` - ReturnMethod string `json:"returnMethod"` - ReturnPeriod struct { - Unit string `json:"unit"` - Value string `json:"value"` - } `json:"returnPeriod"` - ReturnsAccepted string `json:"returnsAccepted"` - ReturnShippingCostPayer string `json:"returnShippingCostPayer"` - } `json:"returnTerms"` - Seller struct { - FeedbackPercentage string `json:"feedbackPercentage"` - FeedbackScore string `json:"feedbackScore"` - SellerAccountType string `json:"sellerAccountType"` - SellerLegalInfo struct { - Email string `json:"email"` - Fax string `json:"fax"` - Imprint string `json:"imprint"` - LegalContactFirstName string `json:"legalContactFirstName"` - LegalContactLastName string `json:"legalContactLastName"` - Name string `json:"name"` - Phone string `json:"phone"` - RegistrationNumber string `json:"registrationNumber"` - SellerProvidedLegalAddress struct { - AddressLine1 string `json:"addressLine1"` - AddressLine2 string `json:"addressLine2"` - City string `json:"city"` - Country string `json:"country"` - CountryName string `json:"countryName"` - County string `json:"county"` - PostalCode string `json:"postalCode"` - StateOrProvince string `json:"stateOrProvince"` - } `json:"sellerProvidedLegalAddress"` - TermsOfService string `json:"termsOfService"` - VatDetails []struct { - IssuingCountry string `json:"issuingCountry"` - VatID string `json:"vatId"` - } `json:"vatDetails"` - } `json:"sellerLegalInfo"` - Username string `json:"username"` - } `json:"seller"` - SellerItemRevision string `json:"sellerItemRevision"` - ShippingOptions []struct { - AdditionalShippingCostPerUnit struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"additionalShippingCostPerUnit"` - CutOffDateUsedForEstimate string `json:"cutOffDateUsedForEstimate"` - MaxEstimatedDeliveryDate string `json:"maxEstimatedDeliveryDate"` - MinEstimatedDeliveryDate string `json:"minEstimatedDeliveryDate"` - QuantityUsedForEstimate string `json:"quantityUsedForEstimate"` - ShippingCarrierCode string `json:"shippingCarrierCode"` - ShippingCost struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` + ShippingOptions []struct { + ShippingServiceCode string `json:"shippingServiceCode"` + Type string `json:"type"` + ShippingCost struct { + Value string `json:"value"` + Currency string `json:"currency"` } `json:"shippingCost"` - ShippingCostType string `json:"shippingCostType"` - ShippingServiceCode string `json:"shippingServiceCode"` + QuantityUsedForEstimate int `json:"quantityUsedForEstimate"` + MinEstimatedDeliveryDate time.Time `json:"minEstimatedDeliveryDate"` + MaxEstimatedDeliveryDate time.Time `json:"maxEstimatedDeliveryDate"` ShipToLocationUsedForEstimate struct { - Country string `json:"country"` - PostalCode string `json:"postalCode"` + Country string `json:"country"` } `json:"shipToLocationUsedForEstimate"` - TrademarkSymbol string `json:"trademarkSymbol"` - Type string `json:"type"` + AdditionalShippingCostPerUnit struct { + Value string `json:"value"` + Currency string `json:"currency"` + } `json:"additionalShippingCostPerUnit"` + ShippingCostType string `json:"shippingCostType"` } `json:"shippingOptions"` ShipToLocations struct { - RegionExcluded []struct { - RegionName string `json:"regionName"` - RegionType string `json:"regionType"` - } `json:"regionExcluded"` RegionIncluded []struct { RegionName string `json:"regionName"` RegionType string `json:"regionType"` } `json:"regionIncluded"` + RegionExcluded []struct { + RegionName string `json:"regionName"` + RegionType string `json:"regionType"` + } `json:"regionExcluded"` } `json:"shipToLocations"` - ShortDescription string `json:"shortDescription"` - Size string `json:"size"` - SizeSystem string `json:"sizeSystem"` - SizeType string `json:"sizeType"` - Subtitle string `json:"subtitle"` - Taxes []struct { - EbayCollectAndRemitTax string `json:"ebayCollectAndRemitTax"` - IncludedInPrice string `json:"includedInPrice"` - ShippingAndHandlingTaxed string `json:"shippingAndHandlingTaxed"` - TaxJurisdiction struct { + ReturnTerms struct { + ReturnsAccepted bool `json:"returnsAccepted"` + RefundMethod string `json:"refundMethod"` + ReturnMethod string `json:"returnMethod"` + ReturnShippingCostPayer string `json:"returnShippingCostPayer"` + ReturnPeriod struct { + Value int `json:"value"` + Unit string `json:"unit"` + } `json:"returnPeriod"` + ReturnInstructions string `json:"returnInstructions"` + RestockingFeePercentage string `json:"restockingFeePercentage"` + } `json:"returnTerms"` + Taxes []struct { + TaxJurisdiction struct { Region struct { RegionName string `json:"regionName"` RegionType string `json:"regionType"` } `json:"region"` TaxJurisdictionID string `json:"taxJurisdictionId"` } `json:"taxJurisdiction"` - TaxPercentage string `json:"taxPercentage"` - TaxType string `json:"taxType"` + TaxType string `json:"taxType"` + TaxPercentage string `json:"taxPercentage"` + ShippingAndHandlingTaxed bool `json:"shippingAndHandlingTaxed"` + IncludedInPrice bool `json:"includedInPrice"` } `json:"taxes"` - Title string `json:"title"` - TopRatedBuyingExperience string `json:"topRatedBuyingExperience"` - UniqueBidderCount string `json:"uniqueBidderCount"` - UnitPrice struct { - ConvertedFromCurrency string `json:"convertedFromCurrency"` - ConvertedFromValue string `json:"convertedFromValue"` - Currency string `json:"currency"` - Value string `json:"value"` - } `json:"unitPrice"` - UnitPricingMeasure string `json:"unitPricingMeasure"` - Warnings []struct { - Category string `json:"category"` - Domain string `json:"domain"` - ErrorID string `json:"errorId"` - InputRefIds []string `json:"inputRefIds"` - LongMessage string `json:"longMessage"` - Message string `json:"message"` - OutputRefIds []string `json:"outputRefIds"` - Parameters []struct { - Name string `json:"name"` - Value string `json:"value"` - } `json:"parameters"` - Subdomain string `json:"subdomain"` - } `json:"warnings"` + LocalizedAspects []struct { + Type string `json:"type"` + Name string `json:"name"` + Value string `json:"value"` + } `json:"localizedAspects"` + QuantityLimitPerBuyer int `json:"quantityLimitPerBuyer"` + PrimaryProductReviewRating struct { + ReviewCount int `json:"reviewCount"` + AverageRating string `json:"averageRating"` + RatingHistograms []struct { + Rating string `json:"rating"` + Count int `json:"count"` + } `json:"ratingHistograms"` + } `json:"primaryProductReviewRating"` + TopRatedBuyingExperience bool `json:"topRatedBuyingExperience"` + BuyingOptions []string `json:"buyingOptions"` + ItemAffiliateWebURL string `json:"itemAffiliateWebUrl"` + ItemWebURL string `json:"itemWebUrl"` + Description string `json:"description"` + Product struct { + AspectGroups []struct { + LocalizedGroupName string `json:"localizedGroupName"` + Aspects []struct { + LocalizedName string `json:"localizedName"` + LocalizedValues []string `json:"localizedValues"` + } `json:"aspects"` + } `json:"aspectGroups"` + Title string `json:"title"` + Description string `json:"description"` + Image struct { + ImageURL string `json:"imageUrl"` + } `json:"image"` + Gtins []string `json:"gtins"` + Brand string `json:"brand"` + Mpns []string `json:"mpns"` + AdditionalProductIdentities []struct { + ProductIdentity []struct { + IdentifierType string `json:"identifierType"` + IdentifierValue string `json:"identifierValue"` + } `json:"productIdentity"` + } `json:"additionalProductIdentities"` + } `json:"product"` + EnabledForGuestCheckout bool `json:"enabledForGuestCheckout"` + AdultOnly bool `json:"adultOnly"` + CategoryID string `json:"categoryId"` + + // Fields not present in the json sample provided by eBay: + ItemEndDate time.Time `json:"itemEndDate"` + MinimumPriceToBid struct { + Currency string `json:"currency"` + Value string `json:"value"` + } `json:"minimumPriceToBid"` + CurrentBidPrice struct { + Currency string `json:"currency"` + Value string `json:"value"` + } `json:"currentBidPrice"` + UniqueBidderCount int `json:"uniqueBidderCount"` } // GetItem retrieves the details of a specific item. diff --git a/browse_test.go b/browse_test.go index a8f34f0..feedf1a 100644 --- a/browse_test.go +++ b/browse_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestOptBrowseContextualLocationn(t *testing.T) { +func TestOptBrowseContextualLocation(t *testing.T) { r, _ := http.NewRequest("", "", nil) ebay.OptBrowseContextualLocation("US", "19406")(r) assert.Equal(t, "contextualLocation=country%3DUS%2Czip%3D19406", r.Header.Get("X-EBAY-C-ENDUSERCTX")) @@ -23,7 +23,20 @@ func TestOptBrowseContextualLocationExistingHeader(t *testing.T) { assert.Equal(t, "affiliateCampaignId=1,contextualLocation=country%3DUS%2Czip%3D19406", r.Header.Get("X-EBAY-C-ENDUSERCTX")) } -func TestGetItem(t *testing.T) { +func TestGetLegacyItem(t *testing.T) { + client, mux, teardown := setup(t) + defer teardown() + + mux.HandleFunc("/buy/browse/v1/item/get_item_by_legacy_id", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, `{"itemId": "v1|%s|0"}`, r.URL.Query().Get("legacy_item_id")) + }) + + item, err := client.Buy.Browse.GetItemByLegacyID(context.Background(), "202117468662") + assert.Nil(t, err) + assert.Equal(t, "v1|202117468662|0", item.ItemID) +} + +func TestGetCompactItem(t *testing.T) { client, mux, teardown := setup(t) defer teardown() @@ -35,3 +48,16 @@ func TestGetItem(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "COMPACT", item.ItemID) } + +func TestGettItem(t *testing.T) { + client, mux, teardown := setup(t) + defer teardown() + + mux.HandleFunc("/buy/browse/v1/item/v1|202117468662|0", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, `{"itemId": "%s"}`, r.URL.Query().Get("fieldgroups")) + }) + + item, err := client.Buy.Browse.GetItem(context.Background(), "v1|202117468662|0") + assert.Nil(t, err) + assert.Equal(t, "PRODUCT", item.ItemID) +} diff --git a/ebay.go b/ebay.go index 2667787..a0583f7 100644 --- a/ebay.go +++ b/ebay.go @@ -3,18 +3,17 @@ package ebay import ( "context" "encoding/json" - "errors" "fmt" "net/http" "net/url" "strings" + + "github.com/pkg/errors" ) const ( baseURL = "https://api.ebay.com/" sandboxBaseURL = "https://api.sandbox.ebay.com/" - - headerEndUserCtx = "X-EBAY-C-ENDUSERCTX" ) // BuyAPI regroups the eBay Buy APIs. @@ -22,6 +21,7 @@ const ( // eBay API docs: https://developer.ebay.com/api-docs/buy/static/buy-landing.html type BuyAPI struct { Browse *BrowseService + Offer *OfferService } // Client manages communication with the eBay API. @@ -63,6 +63,7 @@ func newClient(httpclient *http.Client, baseURL string) *Client { c := &Client{client: httpclient, baseURL: url} c.Buy = BuyAPI{ Browse: (*BrowseService)(&service{c}), + Offer: (*OfferService)(&service{c}), } return c } @@ -82,11 +83,11 @@ func (c *Client) NewRequest(method, url string, opts ...Opt) (*http.Request, err } u, err := c.baseURL.Parse(url) if err != nil { - return nil, err + return nil, errors.WithStack(err) } req, err := http.NewRequest(method, u.String(), nil) if err != nil { - return nil, err + return nil, errors.WithStack(err) } for _, opt := range opts { opt(req) @@ -98,7 +99,7 @@ func (c *Client) NewRequest(method, url string, opts ...Opt) (*http.Request, err func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) error { resp, err := c.client.Do(req.WithContext(ctx)) if err != nil { - return err + return errors.WithStack(err) } defer resp.Body.Close() if err := CheckResponse(resp); err != nil { @@ -107,7 +108,7 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) error if v == nil { return nil } - return json.NewDecoder(resp.Body).Decode(v) + return errors.WithStack(json.NewDecoder(resp.Body).Decode(v)) } // An ErrorData reports one or more errors caused by an API request. diff --git a/go.mod b/go.mod index 6f3be34..30d922b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/joho/godotenv v1.3.0 + github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0 ) diff --git a/go.sum b/go.sum index 8022a3f..a9f1892 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/offer.go b/offer.go new file mode 100644 index 0000000..87ddfbe --- /dev/null +++ b/offer.go @@ -0,0 +1,33 @@ +package ebay + +import ( + "net/http" +) + +// OfferService handles communication with the Offer API +// +// eBay API docs: https://developer.ebay.com/api-docs/buy/offer/static/overview.html +type OfferService service + +// Valid marketplace IDs +const ( + BuyMarketplaceAustralia = "EBAY_AU" + BuyMarketplaceCanada = "EBAY_CA" + BuyMarketplaceGermany = "EBAY_DE" + BuyMarketplaceSpain = "EBAY_ES" + BuyMarketplaceFrance = "EBAY_FR" + BuyMarketplaceGreatBritain = "EBAY_GB" + BuyMarketplaceHongKong = "EBAY_HK" + BuyMarketplaceItalia = "EBAY_IT" + BuyMarketplaceUSA = "EBAY_US" +) + +// OptBuyMarketplace adds the header containing the marketplace id: +// https://developer.ebay.com/api-docs/buy/static/ref-marketplace-supported.html +// +// eBay API docs: https://developer.ebay.com/api-docs/buy/static/api-browse.html#Headers +func OptBuyMarketplace(marketplaceID string) func(*http.Request) { + return func(req *http.Request) { + req.Header.Set("X-EBAY-C-MARKETPLACE-ID", marketplaceID) + } +} diff --git a/offer_test.go b/offer_test.go new file mode 100644 index 0000000..f27066a --- /dev/null +++ b/offer_test.go @@ -0,0 +1,15 @@ +package ebay_test + +import ( + "net/http" + "testing" + + "github.com/jybp/ebay" + "github.com/stretchr/testify/assert" +) + +func TestOptBuyMarketplace(t *testing.T) { + r, _ := http.NewRequest("", "", nil) + ebay.OptBuyMarketplace("EBAY_US")(r) + assert.Equal(t, "EBAY_US", r.Header.Get("X-EBAY-C-MARKETPLACE-ID")) +} diff --git a/test/integration/ebay_test.go b/test/integration/ebay_test.go index fa96b00..8f339f0 100644 --- a/test/integration/ebay_test.go +++ b/test/integration/ebay_test.go @@ -1,20 +1,25 @@ -// +build integration - package integration import ( "context" + "flag" "os" + "strings" "testing" + "time" _ "github.com/joho/godotenv/autoload" "github.com/jybp/ebay" "github.com/jybp/ebay/clientcredentials" ) -var client *ebay.Client +var ( + integration bool + client *ebay.Client +) func init() { + flag.BoolVar(&integration, "integration", false, "run integration tests") clientID := os.Getenv("SANDBOX_CLIENT_ID") clientSecret := os.Getenv("SANDBOX_CLIENT_SECRET") if clientID == "" || clientSecret == "" { @@ -29,29 +34,37 @@ func init() { client = ebay.NewSandboxClient(conf.Client(context.Background())) } -// https://developer.ebay.com/my/api_test_tool?index=0&api=browse&call=item_summary_search__GET&variation=json -func TestAuthorization(t *testing.T) { - req, err := client.NewRequest("GET", "buy/browse/v1/item_summary/search?q=test") - if err != nil { - t.Fatal(err) +func TestAuction(t *testing.T) { + if !integration { + t.SkipNow() } - into := map[string]interface{}{} - err = client.Do(context.Background(), req, &into) - if err != nil { - t.Fatal(err) - } - if testing.Verbose() { - t.Log(into) - } -} -// https://developer.ebay.com/my/api_test_tool?index=0&api=browse&call=item_summary_search__GET&variation=json -func TestGetItemByLegacyID(t *testing.T) { - it, err := client.Buy.Browse.GetItemByLegacyID(context.Background(), "110436963416") + // Manually create an auction in the sandbox and copy/paste the url: + const url = "https://www.sandbox.ebay.com/itm/110439278158" + + ctx := context.Background() + lit, err := client.Buy.Browse.GetItemByLegacyID(ctx, url[strings.LastIndex(url, "/")+1:]) if err != nil { - t.Fatal(err) + t.Fatalf("%+v", err) + } + it, err := client.Buy.Browse.GetItem(ctx, lit.ItemID) + if err != nil { + t.Fatalf("%+v", err) } if testing.Verbose() { - t.Logf("%+v", it) + t.Logf("item: %+v", it) } + isAuction := false + for _, opt := range it.BuyingOptions { + if opt == ebay.BrowseBuyingOptionAuction { + isAuction = true + } + } + if !isAuction { + t.Fatalf("item %s is not an auction. BuyingOptions are: %+v", it.ItemID, it.BuyingOptions) + } + if time.Now().UTC().After(it.ItemEndDate) { + t.Fatalf("item %s end date has been reached. ItemEndDate is: %s", it.ItemID, it.ItemEndDate.String()) + } + t.Logf("item %s UniqueBidderCount:%d minimumBidPrice: %+v currentPriceToBid: %+v", it.ItemID, it.UniqueBidderCount, it.MinimumPriceToBid, it.CurrentBidPrice) }