From 1bd8dea190f4e3c5459c70b0462cfa81c9ae8c62 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Fri, 17 Jan 2025 09:40:30 +0100 Subject: [PATCH 1/3] Rubicon Adapter: Set RendererUrl Metadata --- .../server/bidder/rubicon/RubiconBidder.java | 152 ++++++++----- .../RubiconExtPrebidBiddersBidder.java | 4 + .../bidder/rubicon/RubiconBidderTest.java | 211 ++++++++++++++++++ 3 files changed, 313 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java index ebc7a7dccf4..3ecc01a376d 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -182,6 +182,7 @@ public class RubiconBidder implements Bidder { new TypeReference<>() { }; private static final boolean DEFAULT_MULTIFORMAT_VALUE = false; + private static final String VIDEO_OUTSTREAM_APEX_META_RENDERER_URL = "https://video-outstream.rubiconproject.com/apex-2.2.1.js"; private final String bidderName; private final String endpointUrl; @@ -1609,33 +1610,93 @@ private List bidsFromResponse(BidRequest prebidRequest, .collect(Collectors.toMap(Imp::getId, Function.identity())); final Map idToRubiconImp = bidRequest.getImp().stream() .collect(Collectors.toMap(Imp::getId, Function.identity())); - final Float cpmOverrideFromRequest = cpmOverrideFromRequest(prebidRequest); + final RubiconExtPrebidBiddersBidder extPrebidBiddersBidder = extPrebidBiddersRubicon(prebidRequest.getExt()); + final Float cpmOverrideFromRequest = cpmOverrideFromRequest(extPrebidBiddersBidder); + final boolean hasApexRenderer = hasApexRenderer(extPrebidBiddersBidder); final BidType bidType = bidType(bidRequest); return bidResponse.getSeatbid().stream() .filter(Objects::nonNull) - .map(seatBid -> updateSeatBids(seatBid, errors)) - .map(RubiconSeatBid::getBid) - .filter(Objects::nonNull) + .map(seatBid -> seatBid.getBid().stream() + .filter(Objects::nonNull) + .map(bid -> updateBid( + bid, + seatBid, + idToImp.get(bid.getImpid()), + bidType, + cpmOverrideFromRequest, + hasApexRenderer, + bidResponse, + errors)) + .filter(Objects::nonNull) + .map(bid -> createBidderBid( + bid, + idToRubiconImp.get(bid.getImpid()), + bidType, + bidResponse.getCur())) + .toList()) .flatMap(Collection::stream) - .map(bid -> updateBid(bid, idToImp.get(bid.getImpid()), cpmOverrideFromRequest, bidResponse)) - .map(bid -> createBidderBid(bid, idToRubiconImp.get(bid.getImpid()), bidType, bidResponse.getCur())) .toList(); } - private RubiconSeatBid updateSeatBids(RubiconSeatBid seatBid, List errors) { - final Integer networkId = resolveNetworkId(seatBid); - final String seat = seatBid.getSeat(); + private Bid updateBid(RubiconBid bid, + RubiconSeatBid seatBid, + Imp imp, + BidType bidType, + Float cpmOverrideFromRequest, + boolean hasApexRenderer, + RubiconBidResponse bidResponse, + List errors) { - if (networkId == null && seat == null) { - return seatBid; + final ObjectNode updateBidExt; + try { + updateBidExt = prepareBidExt(bid, seatBid, imp, bidType, hasApexRenderer); + } catch (PreBidException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; } - final List updatedBids = seatBid.getBid().stream() - .map(bid -> prepareBidMeta(bid, seat, networkId, errors)) - .filter(Objects::nonNull) - .toList(); - return seatBid.toBuilder().bid(updatedBids).build(); + String bidId = bid.getId(); + if (generateBidId) { + // Since Rubicon XAPI returns openrtb_response.seatbid.bid.id not unique enough + // generate new value for it + bidId = UUID.randomUUID().toString(); + } else if (Objects.equals(bid.getId(), "0")) { + // Since Rubicon XAPI returns only one bid per response + // copy bidResponse.bidid to openrtb_response.seatbid.bid.id + bidId = bidResponse.getBidid(); + } + + // Unconditionally set price if coming from CPM override + final Float cpmOverride = ObjectUtils.defaultIfNull(cpmOverrideFromImp(imp), cpmOverrideFromRequest); + final BigDecimal bidPrice = cpmOverride != null + ? new BigDecimal(String.valueOf(cpmOverride)) + : bid.getPrice(); + + final RubiconBid updatedRubiconBid = bid.toBuilder() + .id(bidId) + .adm(resolveAdm(bid.getAdm(), bid.getAdmNative())) + .price(bidPrice) + .ext(updateBidExt) + .build(); + + return bidFromRubiconBid(updatedRubiconBid); + } + + private ObjectNode prepareBidExt(RubiconBid bid, + RubiconSeatBid seatBid, + Imp imp, + BidType bidType, + boolean hasApexRenderer) { + + final ObjectNode originalBidExt = bid.getExt(); + final Integer networkId = resolveNetworkId(seatBid); + final String seat = seatBid.getSeat(); + final String rendererUrl = resolveRendererUrl(imp, bidType, hasApexRenderer); + + return ObjectUtils.allNull(networkId, rendererUrl, seat) + ? originalBidExt + : prepareBidMeta(bid, seat, networkId, rendererUrl); } private static Integer resolveNetworkId(RubiconSeatBid seatBid) { @@ -1644,15 +1705,22 @@ private static Integer resolveNetworkId(RubiconSeatBid seatBid) { return networkId <= 0 ? null : networkId; } - private RubiconBid prepareBidMeta(RubiconBid bid, String seat, Integer networkId, List errors) { - final ObjectNode bidExt = bid.getExt(); - final ExtPrebid extPrebid; - try { - extPrebid = getExtPrebid(bidExt, bid.getId()); - } catch (PreBidException e) { - errors.add(BidderError.badServerResponse(e.getMessage())); + private String resolveRendererUrl(Imp imp, BidType bidType, boolean hasApexRenderer) { + if (imp == null) { return null; } + + final Video video = imp.getVideo(); + return hasApexRenderer + && bidType == BidType.video + && (!Objects.equals(video.getPlacement(), 1) && !Objects.equals(video.getPlcmt(), 1)) + ? VIDEO_OUTSTREAM_APEX_META_RENDERER_URL + : null; + } + + private ObjectNode prepareBidMeta(RubiconBid bid, String seat, Integer networkId, String rendererUrl) { + final ObjectNode bidExt = bid.getExt(); + final ExtPrebid extPrebid = getExtPrebid(bidExt, bid.getId()); final ExtBidPrebid extBidPrebid = extPrebid != null ? extPrebid.getPrebid() : null; final ExtBidPrebidMeta meta = extBidPrebid != null ? extBidPrebid.getMeta() : null; @@ -1661,6 +1729,7 @@ private RubiconBid prepareBidMeta(RubiconBid bid, String seat, Integer networkId .orElseGet(ExtBidPrebidMeta::builder) .networkId(networkId) .seat(seat) + .rendererUrl(rendererUrl) .build(); final ExtBidPrebid modifiedExtBidPrebid = extBidPrebid != null @@ -1670,7 +1739,7 @@ private RubiconBid prepareBidMeta(RubiconBid bid, String seat, Integer networkId final ObjectNode updatedBidExt = bidExt != null ? bidExt : mapper.mapper().createObjectNode(); updatedBidExt.set(PREBID_EXT, mapper.mapper().valueToTree(modifiedExtBidPrebid)); - return bid.toBuilder().ext(updatedBidExt).build(); + return updatedBidExt; } private ExtPrebid getExtPrebid(ObjectNode bidExt, String bidId) { @@ -1681,33 +1750,6 @@ private ExtPrebid getExtPrebid(ObjectNode bidExt, Stri } } - private Bid updateBid(RubiconBid bid, Imp imp, Float cpmOverrideFromRequest, RubiconBidResponse bidResponse) { - String bidId = bid.getId(); - if (generateBidId) { - // Since Rubicon XAPI returns openrtb_response.seatbid.bid.id not unique enough - // generate new value for it - bidId = UUID.randomUUID().toString(); - } else if (Objects.equals(bid.getId(), "0")) { - // Since Rubicon XAPI returns only one bid per response - // copy bidResponse.bidid to openrtb_response.seatbid.bid.id - bidId = bidResponse.getBidid(); - } - - // Unconditionally set price if coming from CPM override - final Float cpmOverride = ObjectUtils.defaultIfNull(cpmOverrideFromImp(imp), cpmOverrideFromRequest); - final BigDecimal bidPrice = cpmOverride != null - ? new BigDecimal(String.valueOf(cpmOverride)) - : bid.getPrice(); - - final RubiconBid updatedRubiconBid = bid.toBuilder() - .id(bidId) - .adm(resolveAdm(bid.getAdm(), bid.getAdmNative())) - .price(bidPrice) - .build(); - - return bidFromRubiconBid(updatedRubiconBid); - } - private String resolveAdm(String bidAdm, ObjectNode admobject) { if (StringUtils.isNotBlank(bidAdm)) { return bidAdm; @@ -1725,7 +1767,6 @@ private Bid bidFromRubiconBid(RubiconBid rubiconBid) { } private static BidderBid createBidderBid(Bid bid, Imp imp, BidType bidType, String currency) { - return BidderBid.builder() .bid(bid) .type(bidType) @@ -1734,8 +1775,7 @@ private static BidderBid createBidderBid(Bid bid, Imp imp, BidType bidType, Stri .build(); } - private Float cpmOverrideFromRequest(BidRequest bidRequest) { - final RubiconExtPrebidBiddersBidder bidder = extPrebidBiddersRubicon(bidRequest.getExt()); + private static Float cpmOverrideFromRequest(RubiconExtPrebidBiddersBidder bidder) { final RubiconExtPrebidBiddersBidderDebug debug = bidder != null ? bidder.getDebug() : null; return debug != null ? debug.getCpmoverride() : null; } @@ -1748,6 +1788,10 @@ private Float cpmOverrideFromImp(Imp imp) { .orElse(null); } + private static boolean hasApexRenderer(RubiconExtPrebidBiddersBidder bidder) { + return Optional.ofNullable(bidder).map(RubiconExtPrebidBiddersBidder::getApexRenderer).orElse(false); + } + private static BidType bidType(BidRequest bidRequest) { final ImpMediaType impMediaType = impType(bidRequest.getImp().getFirst()); return switch (impMediaType) { diff --git a/src/main/java/org/prebid/server/bidder/rubicon/proto/request/RubiconExtPrebidBiddersBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/proto/request/RubiconExtPrebidBiddersBidder.java index 059c7ac7439..6d4886cedf6 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/proto/request/RubiconExtPrebidBiddersBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/proto/request/RubiconExtPrebidBiddersBidder.java @@ -1,5 +1,6 @@ package org.prebid.server.bidder.rubicon.proto.request; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Value; @@ -10,4 +11,7 @@ public class RubiconExtPrebidBiddersBidder { String integration; RubiconExtPrebidBiddersBidderDebug debug; + + @JsonProperty("apexRenderer") + Boolean apexRenderer; } diff --git a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java index d49f874c0e8..71e67fbbf6b 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; @@ -3305,6 +3306,216 @@ public void makeBidsShouldReplaceNotPresentAdmWithAdmNative() throws JsonProcess .containsExactly("{\"admNativeProperty\":\"admNativeValue\"}"); } + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlForOutputStreamVideoBidResponse() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(Video.builder().placement(2).plcmt(3).build()), + identity()); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + final ObjectNode expectedBidExt = mapper.valueToTree( + ExtPrebid.of(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder() + .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") + .build()) + .build(), null)); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsExactly(expectedBidExt); + } + + @Test + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsNotDefined() + throws JsonProcessingException { + + // given + final BidRequest givenBidRequest = givenBidRequest( + imp -> imp.id("impId").video(Video.builder().placement(2).plcmt(3).build())); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsNull(); + } + + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsDefinedAsFalse() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(false))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(Video.builder().placement(2).plcmt(3).build()), + identity()); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsNull(); + } + + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenBidResponseIsNotVideo() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").banner(Banner.builder().build()), + identity()); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsNull(); + } + + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenVideoPlacementIsOne() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(Video.builder().placement(1).plcmt(3).build()), + identity()); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsNull(); + } + + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenVideoPlcmtIsOne() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(Video.builder().placement(2).plcmt(1).build()), + identity()); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> bid.impid("impId").price(ONE)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsNull(); + } + @Test public void makeBidsShouldSetSeatToMetaSeat() throws JsonProcessingException { // given From acb7dd6515eee96192aecc0497904995d674a331 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Fri, 7 Feb 2025 12:08:23 +0100 Subject: [PATCH 2/3] Fix comments --- .../server/bidder/rubicon/RubiconBidder.java | 64 +++++----- .../bidder/rubicon/RubiconBidderTest.java | 111 +++++++++++++++++- 2 files changed, 141 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java index 3ecc01a376d..aac6506f3da 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -1689,41 +1689,19 @@ private ObjectNode prepareBidExt(RubiconBid bid, BidType bidType, boolean hasApexRenderer) { - final ObjectNode originalBidExt = bid.getExt(); - final Integer networkId = resolveNetworkId(seatBid); - final String seat = seatBid.getSeat(); - final String rendererUrl = resolveRendererUrl(imp, bidType, hasApexRenderer); - - return ObjectUtils.allNull(networkId, rendererUrl, seat) - ? originalBidExt - : prepareBidMeta(bid, seat, networkId, rendererUrl); - } - - private static Integer resolveNetworkId(RubiconSeatBid seatBid) { - final String buyer = seatBid.getBuyer(); - final int networkId = NumberUtils.toInt(buyer, 0); - return networkId <= 0 ? null : networkId; - } - - private String resolveRendererUrl(Imp imp, BidType bidType, boolean hasApexRenderer) { - if (imp == null) { - return null; - } - - final Video video = imp.getVideo(); - return hasApexRenderer - && bidType == BidType.video - && (!Objects.equals(video.getPlacement(), 1) && !Objects.equals(video.getPlcmt(), 1)) - ? VIDEO_OUTSTREAM_APEX_META_RENDERER_URL - : null; - } - - private ObjectNode prepareBidMeta(RubiconBid bid, String seat, Integer networkId, String rendererUrl) { final ObjectNode bidExt = bid.getExt(); final ExtPrebid extPrebid = getExtPrebid(bidExt, bid.getId()); final ExtBidPrebid extBidPrebid = extPrebid != null ? extPrebid.getPrebid() : null; final ExtBidPrebidMeta meta = extBidPrebid != null ? extBidPrebid.getMeta() : null; + final Integer networkId = resolveNetworkId(seatBid); + final String seat = seatBid.getSeat(); + final String rendererUrl = resolveRendererUrl(imp, meta, bidType, hasApexRenderer); + + if (ObjectUtils.allNull(networkId, rendererUrl, seat)) { + return bidExt; + } + final ExtBidPrebidMeta updatedMeta = Optional.ofNullable(meta) .map(ExtBidPrebidMeta::toBuilder) .orElseGet(ExtBidPrebidMeta::builder) @@ -1750,6 +1728,32 @@ private ExtPrebid getExtPrebid(ObjectNode bidExt, Stri } } + private static Integer resolveNetworkId(RubiconSeatBid seatBid) { + final String buyer = seatBid.getBuyer(); + final int networkId = NumberUtils.toInt(buyer, 0); + return networkId <= 0 ? null : networkId; + } + + private String resolveRendererUrl(Imp imp, ExtBidPrebidMeta meta, BidType bidType, boolean hasApexRenderer) { + if (imp == null) { + return null; + } + + final Video video = imp.getVideo(); + return hasApexRenderer + && (bidType == BidType.video || isVideoMetaMediaType(meta)) + && (video != null && !Objects.equals(video.getPlacement(), 1) && !Objects.equals(video.getPlcmt(), 1)) + ? VIDEO_OUTSTREAM_APEX_META_RENDERER_URL + : null; + } + + private static Boolean isVideoMetaMediaType(ExtBidPrebidMeta meta) { + return Optional.ofNullable(meta) + .map(ExtBidPrebidMeta::getMediaType) + .map("video"::equalsIgnoreCase) + .orElse(false); + } + private String resolveAdm(String bidAdm, ObjectNode admobject) { if (StringUtils.isNotBlank(bidAdm)) { return bidAdm; diff --git a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java index 71e67fbbf6b..b097468082b 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -3348,6 +3348,109 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlForOutputStreamVide .containsExactly(expectedBidExt); } + @Test + public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenMetaMediaTypeIsVideoAndVideoIsPresent() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(Video.builder().build()), + identity()); + + final ObjectNode givenBidExt = mapper.valueToTree( + ExtPrebid.of(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder() + .mediaType("video") + .rendererUrl("https://renderer.url.from.bidder") + .build()) + .build(), null)); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> + bid.impid("impId").price(ONE).ext(givenBidExt)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + final ObjectNode expectedBidExt = mapper.valueToTree( + ExtPrebid.of(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder() + .mediaType("video") + .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") + .build()) + .build(), null)); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsExactly(expectedBidExt); + } + + @Test + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenMetaMediaTypeIsVideoAndVideoIsAbsent() + throws JsonProcessingException { + + // given + final ExtRequest extBidRequest = ExtRequest.of(ExtRequestPrebid.builder() + .bidders(mapper.valueToTree(ExtPrebidBidders.of( + mapper.createObjectNode().set("apexRenderer", BooleanNode.valueOf(true))))) + .build()); + + final BidRequest givenBidRequest = givenBidRequest( + builder -> builder.ext(extBidRequest), + imp -> imp.id("impId").video(null), + identity()); + + final ObjectNode givenBidExt = mapper.valueToTree( + ExtPrebid.of(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder() + .mediaType("video") + .rendererUrl("https://renderer.url.from.bidder") + .build()) + .build(), null)); + + final BidderCall httpCall = givenHttpCall( + givenBidRequest, + mapper.writeValueAsString(RubiconBidResponse.builder() + .cur("USD") + .seatbid(singletonList(RubiconSeatBid.builder() + .bid(singletonList(givenRubiconBid(bid -> + bid.impid("impId").price(ONE).ext(givenBidExt)))) + .build())) + .build())); + + // when + final Result> result = target.makeBids(httpCall, givenBidRequest); + + // then + final ObjectNode expectedBidExt = mapper.valueToTree( + ExtPrebid.of(ExtBidPrebid.builder() + .meta(ExtBidPrebidMeta.builder() + .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") + .build()) + .build(), null)); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(BidderBid::getBid) + .extracting(Bid::getExt) + .containsExactly(givenBidExt); + } + @Test public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsNotDefined() throws JsonProcessingException { @@ -3377,7 +3480,7 @@ public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenApexRenderer } @Test - public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsDefinedAsFalse() + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsDefinedAsFalse() throws JsonProcessingException { // given @@ -3412,7 +3515,7 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenApexRendererIsD } @Test - public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenBidResponseIsNotVideo() + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenBidResponseIsNotVideo() throws JsonProcessingException { // given @@ -3447,7 +3550,7 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenBidResponseIsNo } @Test - public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenVideoPlacementIsOne() + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenVideoPlacementIsOne() throws JsonProcessingException { // given @@ -3482,7 +3585,7 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenVideoPlacementI } @Test - public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenVideoPlcmtIsOne() + public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenVideoPlcmtIsOne() throws JsonProcessingException { // given From d4103d4ca430a8a8ebd873c0dc383029fe1f5d9d Mon Sep 17 00:00:00 2001 From: antonbabak Date: Mon, 17 Feb 2025 09:50:56 +0100 Subject: [PATCH 3/3] fix comments --- .../server/bidder/rubicon/RubiconBidder.java | 6 ++++-- .../config/bidder/RubiconConfiguration.java | 4 ++++ src/main/resources/bidder-config/rubicon.yaml | 1 + .../bidder/rubicon/RubiconBidderTest.java | 19 ++++++++----------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java index aac6506f3da..04814a31e6d 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -182,7 +182,6 @@ public class RubiconBidder implements Bidder { new TypeReference<>() { }; private static final boolean DEFAULT_MULTIFORMAT_VALUE = false; - private static final String VIDEO_OUTSTREAM_APEX_META_RENDERER_URL = "https://video-outstream.rubiconproject.com/apex-2.2.1.js"; private final String bidderName; private final String endpointUrl; @@ -190,6 +189,7 @@ public class RubiconBidder implements Bidder { private final String xapiUsername; private final Set supportedVendors; private final boolean generateBidId; + private final String apexRendererUrl; private final CurrencyConversionService currencyConversionService; private final PriceFloorResolver floorResolver; private final PrebidVersionProvider versionProvider; @@ -204,6 +204,7 @@ public RubiconBidder(String bidderName, String xapiPassword, List supportedVendors, boolean generateBidId, + String apexRendererUrl, CurrencyConversionService currencyConversionService, PriceFloorResolver floorResolver, PrebidVersionProvider versionProvider, @@ -215,6 +216,7 @@ public RubiconBidder(String bidderName, this.xapiUsername = Objects.requireNonNull(xapiUsername); this.supportedVendors = Set.copyOf(Objects.requireNonNull(supportedVendors)); this.generateBidId = generateBidId; + this.apexRendererUrl = apexRendererUrl; this.currencyConversionService = Objects.requireNonNull(currencyConversionService); this.floorResolver = Objects.requireNonNull(floorResolver); this.versionProvider = Objects.requireNonNull(versionProvider); @@ -1743,7 +1745,7 @@ private String resolveRendererUrl(Imp imp, ExtBidPrebidMeta meta, BidType bidTyp return hasApexRenderer && (bidType == BidType.video || isVideoMetaMediaType(meta)) && (video != null && !Objects.equals(video.getPlacement(), 1) && !Objects.equals(video.getPlcmt(), 1)) - ? VIDEO_OUTSTREAM_APEX_META_RENDERER_URL + ? apexRendererUrl : null; } diff --git a/src/main/java/org/prebid/server/spring/config/bidder/RubiconConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/RubiconConfiguration.java index 89f80e439aa..0bfc14c84b7 100644 --- a/src/main/java/org/prebid/server/spring/config/bidder/RubiconConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/bidder/RubiconConfiguration.java @@ -56,6 +56,7 @@ BidderDeps rubiconBidderDeps(RubiconConfigurationProperties rubiconConfiguration config.getXapi().getPassword(), config.getMetaInfo().getSupportedVendors(), config.getGenerateBidId(), + config.getApexRendererUrl(), currencyConversionService, floorResolver, versionProvider, @@ -75,6 +76,9 @@ private static class RubiconConfigurationProperties extends BidderConfigurationP @NotNull private Boolean generateBidId; + + @NotNull + private String apexRendererUrl; } @Data diff --git a/src/main/resources/bidder-config/rubicon.yaml b/src/main/resources/bidder-config/rubicon.yaml index 882732f83a5..080ed16c415 100644 --- a/src/main/resources/bidder-config/rubicon.yaml +++ b/src/main/resources/bidder-config/rubicon.yaml @@ -38,6 +38,7 @@ adapters: url: GET_FROM_globalsupport@magnite.com support-cors: false generate-bid-id: false + apex-renderer-url: "https://video-outstream.rubiconproject.com/apex-2.2.1.js" XAPI: Username: GET_FROM_globalsupport@magnite.com Password: GET_FROM_globalsupport@magnite.com diff --git a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java index b097468082b..3cf40747f5f 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -150,6 +150,7 @@ public class RubiconBidderTest extends VertxTest { private static final String BIDDER_NAME = "bidderName"; private static final String ENDPOINT_URL = "http://rubiconproject.com/exchange.json?tk_xint=prebid"; private static final String EXTERNAL_URL = "http://localhost:8080"; + private static final String APEX_RENDERER_URL = "https://video-outstream.rubiconproject.com/apex-2.2.1.js"; private static final String USERNAME = "username"; private static final String PASSWORD = "password"; private static final String PBS_VERSION = "pbs_version"; @@ -177,6 +178,7 @@ public void setUp() { PASSWORD, SUPPORTED_VENDORS, false, + APEX_RENDERER_URL, currencyConversionService, priceFloorResolver, versionProvider, @@ -195,6 +197,7 @@ public void creationShouldFailOnInvalidEndpointUrl() { PASSWORD, SUPPORTED_VENDORS, false, + APEX_RENDERER_URL, currencyConversionService, priceFloorResolver, versionProvider, @@ -874,6 +877,7 @@ public void shouldNotSetSizeIfBidderParamsIsMissingSizeId() { PASSWORD, SUPPORTED_VENDORS, true, + APEX_RENDERER_URL, currencyConversionService, priceFloorResolver, versionProvider, @@ -3337,7 +3341,7 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlForOutputStreamVide final ObjectNode expectedBidExt = mapper.valueToTree( ExtPrebid.of(ExtBidPrebid.builder() .meta(ExtBidPrebidMeta.builder() - .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") + .rendererUrl(APEX_RENDERER_URL) .build()) .build(), null)); @@ -3389,7 +3393,7 @@ public void makeBidsShouldSetApexRendererUrlToMetaRendererUrlWhenMetaMediaTypeIs ExtPrebid.of(ExtBidPrebid.builder() .meta(ExtBidPrebidMeta.builder() .mediaType("video") - .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") + .rendererUrl(APEX_RENDERER_URL) .build()) .build(), null)); @@ -3437,13 +3441,6 @@ public void makeBidsShouldNotSetApexRendererUrlToMetaRendererUrlWhenMetaMediaTyp final Result> result = target.makeBids(httpCall, givenBidRequest); // then - final ObjectNode expectedBidExt = mapper.valueToTree( - ExtPrebid.of(ExtBidPrebid.builder() - .meta(ExtBidPrebidMeta.builder() - .rendererUrl("https://video-outstream.rubiconproject.com/apex-2.2.1.js") - .build()) - .build(), null)); - assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()) .extracting(BidderBid::getBid) @@ -4049,7 +4046,7 @@ public void makeBidsShouldReturnBidWithRandomlyGeneratedId() throws JsonProcessi // given target = new RubiconBidder( BIDDER_NAME, ENDPOINT_URL, ENDPOINT_URL, USERNAME, PASSWORD, SUPPORTED_VENDORS, true, - currencyConversionService, priceFloorResolver, versionProvider, jacksonMapper); + APEX_RENDERER_URL, currencyConversionService, priceFloorResolver, versionProvider, jacksonMapper); final BidderCall httpCall = givenHttpCall(givenBidRequest(identity()), mapper.writeValueAsString(RubiconBidResponse.builder() @@ -4075,7 +4072,7 @@ public void makeBidsShouldReturnBidWithCurrencyFromBidResponse() throws JsonProc // given target = new RubiconBidder( BIDDER_NAME, ENDPOINT_URL, EXTERNAL_URL, USERNAME, PASSWORD, SUPPORTED_VENDORS, true, - currencyConversionService, priceFloorResolver, versionProvider, jacksonMapper); + APEX_RENDERER_URL, currencyConversionService, priceFloorResolver, versionProvider, jacksonMapper); final BidderCall httpCall = givenHttpCall(givenBidRequest(identity()), mapper.writeValueAsString(RubiconBidResponse.builder()