Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
[Currency support] Take into account request.Cur (prebid#880)
Browse files Browse the repository at this point in the history
This CL introduces the mapping of bid request currencies in the currency
conversion workflow.

CF: prebid#280
  • Loading branch information
benjaminch authored and hhhjort committed Apr 30, 2019
1 parent 9ce3c9c commit 8118c65
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 7 deletions.
21 changes: 16 additions & 5 deletions exchange/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,31 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi
}

if httpInfo.err == nil {

bidResponse, moreErrs := bidder.Bidder.MakeBids(request, httpInfo.request, httpInfo.response)
errs = append(errs, moreErrs...)

if bidResponse != nil {

// Setup default currency as `USD` is not set in bid request nor bid response
if bidResponse.Currency == "" {
// Empty currency means default currency `USD`
bidResponse.Currency = defaultCurrency
}
if len(request.Cur) == 0 {
request.Cur = []string{defaultCurrency}
}

// Try to get a conversion rate
// TODO(#280): try to convert every to element of request.cur, and use the first one which succeeds
if conversionRate, err := conversions.GetRate(bidResponse.Currency, "USD"); err == nil {
// Try to get the first currency from request.cur having a match in the rate converter,
// and use it as currency
var conversionRate float64
var err error
for _, bidReqCur := range request.Cur {
if conversionRate, err = conversions.GetRate(bidResponse.Currency, bidReqCur); err == nil {
seatBid.currency = bidReqCur
break
}
}

if err == nil {
// Conversion rate found, using it for conversion
for i := 0; i < len(bidResponse.Bids); i++ {
if bidResponse.Bids[i].Bid != nil {
Expand Down
176 changes: 174 additions & 2 deletions exchange/bidder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ func TestMultiCurrencies(t *testing.T) {
bidderImpl.httpRequest = make([]*adapters.RequestData, len(tc.bids))

for i, bid := range tc.bids {

mockBidderResponses[i] = &adapters.BidderResponse{
Bids: []*adapters.TypedBid{
{
Expand Down Expand Up @@ -631,7 +630,6 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
bidderImpl.httpRequest = make([]*adapters.RequestData, len(tc.bidCurrency))

for i, cur := range tc.bidCurrency {

mockBidderResponses[i] = &adapters.BidderResponse{
Bids: []*adapters.TypedBid{
{
Expand Down Expand Up @@ -668,6 +666,180 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
}
}

// TestMultiCurrencies_RequestCurrencyPick tests request currencies pick.
func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
// Setup:
respStatus := 200
getRespBody := "{\"wasPost\":false}"
postRespBody := "{\"wasPost\":true}"

testCases := []struct {
bidRequestCurrencies []string
bidResponsesCurrency string
expectedPickedCurrency string
expectedError bool
rates currencies.Rates
description string
}{
{
bidRequestCurrencies: []string{"EUR", "USD", "JPY"},
bidResponsesCurrency: "EUR",
expectedPickedCurrency: "EUR",
expectedError: false,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{
"JPY": {
"USD": 0.0089,
},
"GBP": {
"USD": 1.3050530256,
},
"EUR": {
"USD": 1.1435678764,
},
},
},
description: "Case 1 - Allowed currencies in bid request are known, first one is picked",
},
{
bidRequestCurrencies: []string{"JPY"},
bidResponsesCurrency: "JPY",
expectedPickedCurrency: "JPY",
expectedError: false,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{
"JPY": {
"USD": 0.0089,
},
},
},
description: "Case 2 - There is only one allowed currencies in bid request, it's a known one, it's picked",
},
{
bidRequestCurrencies: []string{"CNY", "USD", "EUR", "JPY"},
bidResponsesCurrency: "USD",
expectedPickedCurrency: "USD",
expectedError: false,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{
"JPY": {
"USD": 0.0089,
},
"GBP": {
"USD": 1.3050530256,
},
"EUR": {
"USD": 1.1435678764,
},
},
},
description: "Case 3 - First allowed currencies in bid request is not known but the others are, second one is picked",
},
{
bidRequestCurrencies: []string{"CNY", "EUR", "JPY"},
bidResponsesCurrency: "",
expectedPickedCurrency: "",
expectedError: true,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{},
},
description: "Case 4 - None allowed currencies in bid request are known, an error is returned",
},
{
bidRequestCurrencies: []string{"CNY", "EUR", "JPY", "USD"},
bidResponsesCurrency: "USD",
expectedPickedCurrency: "USD",
expectedError: false,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{},
},
description: "Case 5 - None allowed currencies in bid request are known but the default one (`USD`), no rates are set but default currency will be picked",
},
{
bidRequestCurrencies: nil,
bidResponsesCurrency: "USD",
expectedPickedCurrency: "USD",
expectedError: false,
rates: currencies.Rates{
DataAsOf: time.Now(),
Conversions: map[string]map[string]float64{},
},
description: "Case 6 - No allowed currencies specified in bid request, default one is picked: `USD`",
},
}

server := httptest.NewServer(mockHandler(respStatus, getRespBody, postRespBody))
defer server.Close()

for _, tc := range testCases {

mockedHTTPServer := httptest.NewServer(http.HandlerFunc(
func(rw http.ResponseWriter, req *http.Request) {
b, err := json.Marshal(tc.rates)
if err == nil {
rw.WriteHeader(http.StatusOK)
rw.Write(b)
} else {
rw.WriteHeader(http.StatusInternalServerError)
}
}),
)

mockBidderResponses := []*adapters.BidderResponse{
{
Bids: []*adapters.TypedBid{
{
Bid: &openrtb.Bid{},
BidType: openrtb_ext.BidTypeBanner,
},
},
Currency: tc.bidResponsesCurrency,
},
}
bidderImpl := &goodMultiHTTPCallsBidder{
bidResponses: mockBidderResponses,
}
bidderImpl.httpRequest = []*adapters.RequestData{
{
Method: "POST",
Uri: server.URL,
Body: []byte("{\"key\":\"val\"}"),
Headers: http.Header{},
},
}

// Execute:
bidder := adaptBidder(bidderImpl, server.Client())
currencyConverter := currencies.NewRateConverter(
&http.Client{},
mockedHTTPServer.URL,
time.Duration(10)*time.Second,
)
seatBid, errs := bidder.requestBid(
context.Background(),
&openrtb.BidRequest{
Cur: tc.bidRequestCurrencies,
},
"test",
1,
currencyConverter.Rates(),
)

// Verify:
if tc.expectedError {
assert.NotNil(t, errs, tc.description)
} else {
assert.Nil(t, errs, tc.description)
assert.Equal(t, tc.expectedPickedCurrency, seatBid.currency, tc.description)
}
}
}

// TestBadResponseLogging makes sure that openrtb_ext works properly on malformed HTTP requests.
func TestBadRequestLogging(t *testing.T) {
info := &httpCallInfo{
Expand Down

0 comments on commit 8118c65

Please sign in to comment.