Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions adapters/triplelift/triplelift.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v4/adapters"
Expand Down Expand Up @@ -33,7 +34,7 @@ func getBidType(ext TripleliftRespExt) openrtb_ext.BidType {
return openrtb_ext.BidTypeBanner
}

func processImp(imp *openrtb2.Imp) error {
func processImp(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error {
// get the triplelift extension
var ext adapters.ExtImpBidder
var tlext openrtb_ext.ExtImpTriplelift
Expand All @@ -48,15 +49,35 @@ func processImp(imp *openrtb2.Imp) error {
}
imp.TagID = tlext.InvCode
// floor is optional
if tlext.Floor == nil {
return nil
} else {
imp.BidFloor = *tlext.Floor
if tlext.Floor != nil {
imp.BidFloor = *tlext.Floor
}
// Normalize bid floor to USD - TLX expects USD
if err := resolveBidFloorCurrency(imp, reqInfo); err != nil {
return err
}
// no error
return nil
}

// Normalize imp.BidFloor to USD
func resolveBidFloorCurrency(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error {
if imp.BidFloor <= 0 {
return nil
}
if imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
converted, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
if err != nil {
return &errortypes.BadInput{
Message: fmt.Sprintf("Unable to convert bid floor from %s to USD: %s", imp.BidFloorCur, err.Error()),
}
}
imp.BidFloor = converted
}
imp.BidFloorCur = "USD"
return nil
}

func (a *TripleliftAdapter) MakeRequests(request *openrtb2.BidRequest, extra *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
errs := make([]error, 0, len(request.Imp)+1)
reqs := make([]*adapters.RequestData, 0, 1)
Expand All @@ -66,7 +87,7 @@ func (a *TripleliftAdapter) MakeRequests(request *openrtb2.BidRequest, extra *ad
var validImps []openrtb2.Imp
// pre-process the imps
for _, imp := range tlRequest.Imp {
if err := processImp(&imp); err == nil {
if err := processImp(&imp, extra); err == nil {
validImps = append(validImps, imp)
} else {
errs = append(errs, err)
Expand Down Expand Up @@ -126,6 +147,9 @@ func (a *TripleliftAdapter) MakeBids(internalRequest *openrtb2.BidRequest, exter
count := getBidCount(bidResp)
bidResponse := adapters.NewBidderResponseWithBidsCapacity(count)

// Bids are always denominated in USD
bidResponse.Currency = "USD"

for _, sb := range bidResp.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]
Expand Down
91 changes: 91 additions & 0 deletions adapters/triplelift/triplelift_currency_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package triplelift

import (
"errors"
"testing"

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v4/adapters"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

type mockCurrencyConversion struct {
mock.Mock
}

func (m *mockCurrencyConversion) GetRate(from, to string) (float64, error) {
args := m.Called(from, to)
return args.Get(0).(float64), args.Error(1)
}

func (m *mockCurrencyConversion) GetRates() *map[string]map[string]float64 {
args := m.Called()
return args.Get(0).(*map[string]map[string]float64)
}

var currencyTestCases = map[string]struct {
imp openrtb2.Imp
setMock func(m *mock.Mock)
expectedImp openrtb2.Imp
assertError assert.ErrorAssertionFunc
}{
"no floor, no currency": {
imp: openrtb2.Imp{BidFloor: 0, BidFloorCur: ""},
setMock: func(m *mock.Mock) {},
expectedImp: openrtb2.Imp{BidFloor: 0, BidFloorCur: ""},
assertError: assert.NoError,
},
"floor, empty currency (implicit USD)": {
imp: openrtb2.Imp{BidFloor: 1.25, BidFloorCur: ""},
setMock: func(m *mock.Mock) {},
expectedImp: openrtb2.Imp{BidFloor: 1.25, BidFloorCur: "USD"},
assertError: assert.NoError,
},
"USD floor, no conversion needed": {
imp: openrtb2.Imp{BidFloor: 2.0, BidFloorCur: "USD"},
setMock: func(m *mock.Mock) {},
expectedImp: openrtb2.Imp{BidFloor: 2.0, BidFloorCur: "USD"},
assertError: assert.NoError,
},
"usd lowercase - no conversion needed": {
imp: openrtb2.Imp{BidFloor: 2.0, BidFloorCur: "usd"},
setMock: func(m *mock.Mock) {},
expectedImp: openrtb2.Imp{BidFloor: 2.0, BidFloorCur: "USD"},
assertError: assert.NoError,
},
"EUR floor converts to USD": {
imp: openrtb2.Imp{BidFloor: 1.0, BidFloorCur: "EUR"},
setMock: func(m *mock.Mock) {
m.On("GetRate", "EUR", "USD").Return(1.1, nil)
},
expectedImp: openrtb2.Imp{BidFloor: 1.1, BidFloorCur: "USD"},
assertError: assert.NoError,
},
"unknown currency returns error": {
imp: openrtb2.Imp{BidFloor: 1.0, BidFloorCur: "ABC"},
setMock: func(m *mock.Mock) {
m.On("GetRate", "ABC", "USD").Return(0.0, errors.New("conversion rate not found"))
},
expectedImp: openrtb2.Imp{BidFloor: 1.0, BidFloorCur: "ABC"},
assertError: assert.Error,
},
}

func TestResolveBidFloorCurrency(t *testing.T) {
for name, tc := range currencyTestCases {
t.Run(name, func(t *testing.T) {
imp := tc.imp
mockConversions := &mockCurrencyConversion{}
tc.setMock(&mockConversions.Mock)

reqInfo := adapters.ExtraRequestInfo{CurrencyConversions: mockConversions}
err := resolveBidFloorCurrency(&imp, &reqInfo)

tc.assertError(t, err)
assert.Equal(t, tc.expectedImp.BidFloor, imp.BidFloor)
assert.Equal(t, tc.expectedImp.BidFloorCur, imp.BidFloorCur)
mockConversions.AssertExpectations(t)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
},
"tagid": "foo",
"bidfloor": 20,
"bidfloorcur": "USD",
"ext": {
"bidder": {
"inventoryCode": "foo",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"mockBidRequest": {
"id": "test-request-id",
"cur": ["USD"],
"imp": [
{
"id": "test-imp-id",
"banner": {
"format": [{ "w": 300, "h": 250 }]
},
"bidfloor": 1.0,
"bidfloorcur": "EUR",
"ext": {
"bidder": { "inventoryCode": "aa" }
}
}
],
"ext": {
"prebid": {
"currency": {
"rates": { "EUR": { "USD": 1.1 } },
"usepbsrates": false
}
}
}
},
"httpCalls": [
{
"expectedRequest": {
"uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20",
"body": {
"id": "test-request-id",
"cur": ["USD"],
"imp": [
{
"id": "test-imp-id",
"banner": { "format": [{ "w": 300, "h": 250 }] },
"bidfloor": 1.1,
"bidfloorcur": "USD",
"tagid": "aa",
"ext": { "bidder": { "inventoryCode": "aa" } }
}
],
"ext": {
"prebid": {
"currency": {
"rates": { "EUR": { "USD": 1.1 } },
"usepbsrates": false
}
}
}
},
"impIDs": ["test-imp-id"]
},
"mockResponse": {
"status": 200,
"body": {
"id": "test-request-id",
"seatbid": [{
"seat": "958",
"bid": [{
"id": "b1", "impid": "test-imp-id", "price": 1.5,
"adm": "some-ad", "crid": "cr1", "w": 300, "h": 250,
"ext": { "triplelift_pb": { "format": 2 } }
}]
}],
"bidid": "auction-id"
}
}
}
],
"expectedBidResponses": [{
"currency": "USD",
"bids": [{
"bid": {
"id": "b1", "impid": "test-imp-id", "price": 1.5,
"adm": "some-ad", "crid": "cr1", "w": 300, "h": 250,
"ext": { "triplelift_pb": { "format": 2 } }
},
"type": "banner"
}]
}]
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
{
"tagid": "aaw",
"bidfloor": 0.10,
"bidfloorcur": "USD",
"id": "test-imp-id",
"video": {
"mimes": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{
"tagid": "aaw",
"bidfloor": 0.10,
"bidfloorcur": "USD",
"id": "test-imp-id",
"video": {
"mimes": [ "video/mp4"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{
"tagid": "aaw",
"bidfloor": 0.10,
"bidfloorcur": "USD",
"id": "test-imp-id",
"video": {
"mimes": [ "video/mp4"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{
"tagid": "aaw",
"bidfloor": 0.10,
"bidfloorcur": "USD",
"id": "test-imp-id",
"video": {
"mimes": [ "video/mp4"],
Expand Down
38 changes: 32 additions & 6 deletions adapters/triplelift_native/triplelift_native.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v4/adapters"
Expand Down Expand Up @@ -47,7 +48,7 @@ func getBidType(ext TripleliftRespExt) openrtb_ext.BidType {
return openrtb_ext.BidTypeNative
}

func processImp(imp *openrtb2.Imp, request *openrtb2.BidRequest) error {
func processImp(imp *openrtb2.Imp, request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) error {
// get the triplelift extension
var ext ExtImp
var tlext openrtb_ext.ExtImpTriplelift
Expand All @@ -68,15 +69,37 @@ func processImp(imp *openrtb2.Imp, request *openrtb2.BidRequest) error {
imp.TagID = tlext.InvCode
}

// floor is optional
if tlext.Floor == nil {
return nil
// floor is optional
if tlext.Floor != nil {
imp.BidFloor = *tlext.Floor
}

// Normalize bid floor to USD
if err := resolveBidFloorCurrency(imp, reqInfo); err != nil {
return err
}
imp.BidFloor = *tlext.Floor

return nil
}

// Normalize imp.BidFloor to USD
func resolveBidFloorCurrency(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error {
if imp.BidFloor <= 0 {
return nil
}
if imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {
converted, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
if err != nil {
return &errortypes.BadInput{
Message: fmt.Sprintf("Unable to convert bid floor from %s to USD: %s", imp.BidFloorCur, err.Error()),
}
}
imp.BidFloor = converted
}
imp.BidFloorCur = "USD"
return nil
}

// msnInApp returns whether msn.com is in request.app.publisher.domain
func msnInApp(request *openrtb2.BidRequest) bool {
return request.App != nil && request.App.Publisher != nil && request.App.Publisher.Domain == "msn.com"
Expand Down Expand Up @@ -113,7 +136,7 @@ func (a *TripleliftNativeAdapter) MakeRequests(request *openrtb2.BidRequest, ext
var validImps []openrtb2.Imp
// pre-process the imps
for _, imp := range tlRequest.Imp {
if err := processImp(&imp, request); err == nil {
if err := processImp(&imp, request, extra); err == nil {
validImps = append(validImps, imp)
} else {
errs = append(errs, err)
Expand Down Expand Up @@ -186,6 +209,9 @@ func (a *TripleliftNativeAdapter) MakeBids(internalRequest *openrtb2.BidRequest,
count := getBidCount(bidResp)
bidResponse := adapters.NewBidderResponseWithBidsCapacity(count)

// Bids are always denominated in USD
bidResponse.Currency = "USD"

for _, sb := range bidResp.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]
Expand Down
Loading
Loading