Skip to content

Commit f6fa12e

Browse files
arg-resolve-auction-price-macro
1 parent 3c194cc commit f6fa12e

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

src/main/java/org/prebid/server/bidder/thetradedesk/TheTradeDeskBidder.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.prebid.server.util.BidderUtil;
3030
import org.prebid.server.util.HttpUtil;
3131

32+
import java.math.BigDecimal;
3233
import java.util.ArrayList;
3334
import java.util.Collection;
3435
import java.util.Collections;
@@ -43,6 +44,7 @@ public class TheTradeDeskBidder implements Bidder<BidRequest> {
4344
};
4445

4546
private static final String SUPPLY_ID_MACRO = "{{SupplyId}}";
47+
private static final String PRICE_MACRO = "${AUCTION_PRICE}";
4648
private static final Pattern SUPPLY_ID_PATTERN = Pattern.compile("([a-z]+)$");
4749

4850
private final String endpointUrl;
@@ -196,6 +198,8 @@ private static List<BidderBid> extractBids(BidResponse bidResponse) {
196198
.map(SeatBid::getBid).filter(Objects::nonNull)
197199
.flatMap(Collection::stream)
198200
.filter(Objects::nonNull)
201+
.map(bid -> resolveBidderBid(bidResponse.getCur(), bidRequest.getImp(), bid, errors))
202+
.filter(Objects::nonNull)
199203
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
200204
.toList();
201205
}
@@ -208,4 +212,23 @@ private static BidType getBidType(Bid bid) {
208212
case null, default -> throw new PreBidException("unsupported mtype: %s".formatted(bid.getMtype()));
209213
};
210214
}
215+
216+
private BidderBid resolveBidderBid(String currency, Bid bid, List<BidderError> errors) {
217+
try {
218+
return BidderBid.of(resolvePriceMacros(bid), getBidType(bid), currency);
219+
} catch (PreBidException e) {
220+
errors.add(BidderError.badServerResponse(e.getMessage()));
221+
return null;
222+
}
223+
}
224+
225+
private static Bid resolvePriceMacros(Bid bid) {
226+
final BigDecimal price = bid.getPrice();
227+
final String priceAsString = price != null ? price.toPlainString() : "0";
228+
229+
return bid.toBuilder()
230+
.nurl(StringUtils.replace(bid.getNurl(), PRICE_MACRO, priceAsString))
231+
.adm(StringUtils.replace(bid.getAdm(), PRICE_MACRO, priceAsString))
232+
.build();
233+
}
211234
}

src/test/java/org/prebid/server/bidder/thetradedesk/TheTradeDeskBidderTest.java

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
2525
import org.prebid.server.proto.openrtb.ext.request.thetradedesk.ExtImpTheTradeDesk;
2626

27+
import java.math.BigDecimal;
2728
import java.util.Arrays;
2829
import java.util.List;
2930
import java.util.Set;
@@ -472,6 +473,205 @@ public void makeBidsShouldThrowErrorWhenMediaTypeIsMissing() throws JsonProcessi
472473
.containsOnly(BidderError.badServerResponse("unsupported mtype: null"));
473474
}
474475

476+
@Test
477+
public void makeBidsShouldReplacePriceMacroInNurlAndAdmWithBidPrice() throws JsonProcessingException {
478+
// given
479+
final BidderCall<BidRequest> httpCall = givenHttpCall(
480+
givenBidResponse(bidBuilder -> bidBuilder
481+
.mtype(1)
482+
.impid("123")
483+
.price(BigDecimal.valueOf(1.23))
484+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}")
485+
.adm("<div>Price: ${AUCTION_PRICE}</div>")));
486+
487+
// when
488+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
489+
490+
// then
491+
assertThat(result.getErrors()).isEmpty();
492+
assertThat(result.getValue()).hasSize(1);
493+
final BidderBid bidderBid = result.getValue().getFirst();
494+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=1.23");
495+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 1.23</div>");
496+
assertThat(bidderBid.getBid().getPrice()).isEqualTo(BigDecimal.valueOf(1.23));
497+
}
498+
499+
@Test
500+
public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsNull() throws JsonProcessingException {
501+
// given
502+
final BidderCall<BidRequest> httpCall = givenHttpCall(
503+
givenBidResponse(bidBuilder -> bidBuilder
504+
.mtype(1)
505+
.impid("123")
506+
.price(null)
507+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}")
508+
.adm("<div>Price: ${AUCTION_PRICE}</div>")));
509+
510+
// when
511+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
512+
513+
// then
514+
assertThat(result.getErrors()).isEmpty();
515+
assertThat(result.getValue()).hasSize(1);
516+
final BidderBid bidderBid = result.getValue().getFirst();
517+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=0");
518+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 0</div>");
519+
}
520+
521+
@Test
522+
public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsZero() throws JsonProcessingException {
523+
// given
524+
final BidderCall<BidRequest> httpCall = givenHttpCall(
525+
givenBidResponse(bidBuilder -> bidBuilder
526+
.mtype(1)
527+
.impid("123")
528+
.price(BigDecimal.ZERO)
529+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}")
530+
.adm("<div>Price: ${AUCTION_PRICE}</div>")));
531+
532+
// when
533+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
534+
535+
// then
536+
assertThat(result.getErrors()).isEmpty();
537+
assertThat(result.getValue()).hasSize(1);
538+
final BidderBid bidderBid = result.getValue().getFirst();
539+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=0");
540+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 0</div>");
541+
}
542+
543+
@Test
544+
public void makeBidsShouldReplacePriceMacroInNurlOnlyWhenAdmDoesNotContainMacro() throws JsonProcessingException {
545+
// given
546+
final BidderCall<BidRequest> httpCall = givenHttpCall(
547+
givenBidResponse(bidBuilder -> bidBuilder
548+
.mtype(1)
549+
.impid("123")
550+
.price(BigDecimal.valueOf(5.67))
551+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}")
552+
.adm("<div>No macro here</div>")));
553+
554+
// when
555+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
556+
557+
// then
558+
assertThat(result.getErrors()).isEmpty();
559+
assertThat(result.getValue()).hasSize(1);
560+
final BidderBid bidderBid = result.getValue().getFirst();
561+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=5.67");
562+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>No macro here</div>");
563+
}
564+
565+
@Test
566+
public void makeBidsShouldReplacePriceMacroInAdmOnlyWhenNurlDoesNotContainMacro() throws JsonProcessingException {
567+
// given
568+
final BidderCall<BidRequest> httpCall = givenHttpCall(
569+
givenBidResponse(bidBuilder -> bidBuilder
570+
.mtype(1)
571+
.impid("123")
572+
.price(BigDecimal.valueOf(8.90))
573+
.nurl("http://example.com/nurl")
574+
.adm("<div>Price: ${AUCTION_PRICE}</div>")));
575+
576+
// when
577+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
578+
579+
// then
580+
assertThat(result.getErrors()).isEmpty();
581+
assertThat(result.getValue()).hasSize(1);
582+
final BidderBid bidderBid = result.getValue().getFirst();
583+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl");
584+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 8.90</div>");
585+
}
586+
587+
@Test
588+
public void makeBidsShouldNotReplacePriceMacroWhenNurlAndAdmDoNotContainMacro() throws JsonProcessingException {
589+
// given
590+
final BidderCall<BidRequest> httpCall = givenHttpCall(
591+
givenBidResponse(bidBuilder -> bidBuilder
592+
.mtype(1)
593+
.impid("123")
594+
.price(BigDecimal.valueOf(12.34))
595+
.nurl("http://example.com/nurl")
596+
.adm("<div>No macro</div>")));
597+
598+
// when
599+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
600+
601+
// then
602+
assertThat(result.getErrors()).isEmpty();
603+
assertThat(result.getValue()).hasSize(1);
604+
final BidderBid bidderBid = result.getValue().getFirst();
605+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl");
606+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>No macro</div>");
607+
}
608+
609+
@Test
610+
public void makeBidsShouldHandleNullNurlAndAdm() throws JsonProcessingException {
611+
// given
612+
final BidderCall<BidRequest> httpCall = givenHttpCall(
613+
givenBidResponse(bidBuilder -> bidBuilder
614+
.mtype(1)
615+
.impid("123")
616+
.price(BigDecimal.valueOf(15.00))
617+
.nurl(null)
618+
.adm(null)));
619+
620+
// when
621+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
622+
623+
// then
624+
assertThat(result.getErrors()).isEmpty();
625+
assertThat(result.getValue()).hasSize(1);
626+
final BidderBid bidderBid = result.getValue().getFirst();
627+
assertThat(bidderBid.getBid().getNurl()).isNull();
628+
assertThat(bidderBid.getBid().getAdm()).isNull();
629+
}
630+
631+
@Test
632+
public void makeBidsShouldReplaceMultiplePriceMacrosInSameField() throws JsonProcessingException {
633+
// given
634+
final BidderCall<BidRequest> httpCall = givenHttpCall(
635+
givenBidResponse(bidBuilder -> bidBuilder
636+
.mtype(1)
637+
.impid("123")
638+
.price(BigDecimal.valueOf(9.99))
639+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}&backup_price=${AUCTION_PRICE}")
640+
.adm("<div>Price: ${AUCTION_PRICE}, Fallback: ${AUCTION_PRICE}</div>")));
641+
642+
// when
643+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
644+
645+
// then
646+
assertThat(result.getErrors()).isEmpty();
647+
assertThat(result.getValue()).hasSize(1);
648+
final BidderBid bidderBid = result.getValue().getFirst();
649+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=9.99&backup_price=9.99");
650+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 9.99, Fallback: 9.99</div>");
651+
}
652+
653+
@Test
654+
public void makeBidsShouldHandleLargeDecimalPrices() throws JsonProcessingException {
655+
// given
656+
final BidderCall<BidRequest> httpCall = givenHttpCall(
657+
givenBidResponse(bidBuilder -> bidBuilder
658+
.mtype(1)
659+
.impid("123")
660+
.price(new BigDecimal("123456789.123456789"))
661+
.nurl("http://example.com/nurl?price=${AUCTION_PRICE}")
662+
.adm("<div>Price: ${AUCTION_PRICE}</div>")));
663+
664+
// when
665+
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
666+
667+
// then
668+
assertThat(result.getErrors()).isEmpty();
669+
assertThat(result.getValue()).hasSize(1);
670+
final BidderBid bidderBid = result.getValue().getFirst();
671+
assertThat(bidderBid.getBid().getNurl()).isEqualTo("http://example.com/nurl?price=123456789.123456789");
672+
assertThat(bidderBid.getBid().getAdm()).isEqualTo("<div>Price: 123456789.123456789</div>");
673+
}
674+
475675
private String givenBidResponse(UnaryOperator<Bid.BidBuilder> bidCustomizer) throws JsonProcessingException {
476676
return mapper.writeValueAsString(BidResponse.builder()
477677
.cur("USD")

0 commit comments

Comments
 (0)