diff --git a/adapters/thetradedesk/thetradedesk.go b/adapters/thetradedesk/thetradedesk.go index 2fdf227d15d..f22744ffdc2 100644 --- a/adapters/thetradedesk/thetradedesk.go +++ b/adapters/thetradedesk/thetradedesk.go @@ -6,6 +6,8 @@ import ( "fmt" "net/http" "regexp" + "strconv" + "strings" "text/template" "github.com/prebid/prebid-server/v3/adapters" @@ -176,6 +178,7 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest for _, bid := range seatBid.Bid { bid := bid + resolveAuctionPriceMacros(&bid) bidType, err := getBidType(bid.MType) if err != nil { @@ -232,3 +235,12 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co templateEndpoint: template, }, nil } + +func resolveAuctionPriceMacros(bid *openrtb2.Bid) { + if bid == nil { + return + } + price := strconv.FormatFloat(bid.Price, 'f', -1, 64) + bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1) + bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1) +} diff --git a/adapters/thetradedesk/thetradedesk_test.go b/adapters/thetradedesk/thetradedesk_test.go index 22fdcebeec4..11ffda30d9f 100644 --- a/adapters/thetradedesk/thetradedesk_test.go +++ b/adapters/thetradedesk/thetradedesk_test.go @@ -451,3 +451,147 @@ func TestTheTradeDeskAdapter_BuildEndpoint(t *testing.T) { }) } } + +func TestResolveAuctionPriceMacros(t *testing.T) { + tests := []struct { + name string + bid *openrtb2.Bid + expectedNURL string + expectedAdM string + }{ + { + name: "nil_bid", + bid: nil, + }, + { + name: "no_macros", + bid: &openrtb2.Bid{ + Price: 1.23, + NURL: "http://example.com/nurl", + AdM: "
Ad content
", + }, + expectedNURL: "http://example.com/nurl", + expectedAdM: "
Ad content
", + }, + { + name: "macros_in_both_nurl_and_adm", + bid: &openrtb2.Bid{ + Price: 1.50, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Ad content with price ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=1.5", + expectedAdM: "
Ad content with price 1.5
", + }, + { + name: "macro_only_in_nurl", + bid: &openrtb2.Bid{ + Price: 2.75, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Ad content
", + }, + expectedNURL: "http://example.com/nurl?price=2.75", + expectedAdM: "
Ad content
", + }, + { + name: "macro_only_in_adm", + bid: &openrtb2.Bid{ + Price: 0.99, + NURL: "http://example.com/nurl", + AdM: "
Price: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl", + expectedAdM: "
Price: 0.99
", + }, + { + name: "multiple_macros_in_same_field", + bid: &openrtb2.Bid{ + Price: 3.14, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}&backup_price=${AUCTION_PRICE}", + AdM: "
Price: ${AUCTION_PRICE}, Backup: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=3.14&backup_price=3.14", + expectedAdM: "
Price: 3.14, Backup: 3.14
", + }, + { + name: "zero_price", + bid: &openrtb2.Bid{ + Price: 0.0, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Price: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=0", + expectedAdM: "
Price: 0
", + }, + { + name: "very_small_price", + bid: &openrtb2.Bid{ + Price: 0.001, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Price: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=0.001", + expectedAdM: "
Price: 0.001
", + }, + { + name: "large_price", + bid: &openrtb2.Bid{ + Price: 999.999, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Price: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=999.999", + expectedAdM: "
Price: 999.999
", + }, + { + name: "integer_price", + bid: &openrtb2.Bid{ + Price: 5.0, + NURL: "http://example.com/nurl?price=${AUCTION_PRICE}", + AdM: "
Price: ${AUCTION_PRICE}
", + }, + expectedNURL: "http://example.com/nurl?price=5", + expectedAdM: "
Price: 5
", + }, + { + name: "empty_nurl_and_adm", + bid: &openrtb2.Bid{ + Price: 1.23, + NURL: "", + AdM: "", + }, + expectedNURL: "", + expectedAdM: "", + }, + { + name: "case_sensitive_macro_not_replaced", + bid: &openrtb2.Bid{ + Price: 1.50, + NURL: "http://example.com/nurl?price=${auction_price}", + AdM: "
Price: ${Auction_Price}
", + }, + expectedNURL: "http://example.com/nurl?price=${auction_price}", + expectedAdM: "
Price: ${Auction_Price}
", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var testBid *openrtb2.Bid + if tt.bid != nil { + bidCopy := *tt.bid + testBid = &bidCopy + } + + resolveAuctionPriceMacros(testBid) + + if tt.bid == nil { + assert.Nil(t, testBid) + return + } + + assert.Equal(t, tt.expectedNURL, testBid.NURL, "NURL should match expected value") + assert.Equal(t, tt.expectedAdM, testBid.AdM, "AdM should match expected value") + }) + } +} diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/auction-price-macro-replacement.json b/adapters/thetradedesk/thetradedesktest/supplemental/auction-price-macro-replacement.json new file mode 100644 index 00000000000..2629242b010 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/auction-price-macro-replacement.json @@ -0,0 +1,142 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 150 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 1.50, + "crid": "creative-123", + "adm": "", + "nurl": "http://win-notification.ttd.com?wp=${AUCTION_PRICE}&id=test-slot-id", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 1.50, + "crid": "creative-123", + "adm": "", + "nurl": "http://win-notification.ttd.com?wp=1.5&id=test-slot-id", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file