diff --git a/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java b/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java index 4c64992184b..a48c51a1be9 100644 --- a/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java +++ b/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java @@ -7,8 +7,10 @@ import com.iab.openrtb.request.Format; import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Site; +import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.vertx.core.MultiMap; import org.apache.commons.collections4.CollectionUtils; import org.prebid.server.bidder.Bidder; @@ -57,8 +59,11 @@ public Result>> makeHttpRequests(BidRequest request for (Imp imp : request.getImp()) { try { + if (imp.getBanner() == null && imp.getVideo() == null && imp.getXNative() == null && imp.getAudio() == null) { + throw new PreBidException("We need a Banner, Video, Native or Audio Object in the request"); + } final ExtImpConnectAd impExt = parseImpExt(imp); - final Imp updatedImp = updateImp(imp, secure, impExt.getSiteId(), impExt.getBidFloor()); + final Imp updatedImp = updateImp(imp, secure, impExt); processedImps.add(updatedImp); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); @@ -98,20 +103,46 @@ private ExtImpConnectAd parseImpExt(Imp imp) { return extImpConnectAd; } - private Imp updateImp(Imp imp, Integer secure, String siteId, BigDecimal bidFloor) { + private Imp updateImp(Imp imp, Integer secure, ExtImpConnectAd extImpConnectAd) { + final BigDecimal bidFloor = extImpConnectAd.getBidFloor(); final boolean isValidBidFloor = BidderUtil.isValidPrice(bidFloor); return imp.toBuilder() .banner(updateBanner(imp.getBanner())) - .tagid(siteId) + .tagid(extImpConnectAd.getSiteId()) .secure(secure) .bidfloor(isValidBidFloor ? bidFloor : imp.getBidfloor()) .bidfloorcur(isValidBidFloor ? "USD" : imp.getBidfloorcur()) + .ext(modifyImpExt(imp.getExt(), extImpConnectAd)) .build(); } + private ObjectNode modifyImpExt(ObjectNode impExt, ExtImpConnectAd extImpConnectAd) { + final ObjectNode modifiedExt = impExt != null ? impExt.deepCopy() : mapper.mapper().createObjectNode(); + final String networkId = extImpConnectAd.getNetworkId(); + final String siteId = extImpConnectAd.getSiteId(); + + if (networkId != null) { + try { + modifiedExt.put("networkId", Integer.parseInt(networkId)); + } catch (NumberFormatException e) { + modifiedExt.put("networkId", networkId); + } + } + + if (siteId != null) { + try { + modifiedExt.put("siteId", Integer.parseInt(siteId)); + } catch (NumberFormatException e) { + modifiedExt.put("siteId", siteId); + } + } + + return modifiedExt; + } + private static Banner updateBanner(Banner banner) { if (banner == null) { - throw new PreBidException("We need a Banner Object in the request"); + return null; } if (banner.getW() != null || banner.getH() != null) { @@ -153,13 +184,13 @@ private static MultiMap resolveHeaders(Device device) { public final Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { try { final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.withValues(extractBids(bidResponse)); + return Result.withValues(extractBids(bidResponse, bidRequest)); } catch (DecodeException | PreBidException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private List extractBids(BidResponse bidResponse) { + private List extractBids(BidResponse bidResponse, BidRequest bidRequest) { if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { return Collections.emptyList(); } @@ -170,7 +201,38 @@ private List extractBids(BidResponse bidResponse) { .filter(Objects::nonNull) .flatMap(Collection::stream) .filter(Objects::nonNull) - .map(bid -> BidderBid.of(bid, BidType.banner, bidResponse.getCur())) + .map(bid -> BidderBid.of(bid, getBidType(bid, bidRequest), bidResponse.getCur())) .toList(); } + + private static BidType getBidType(Bid bid, BidRequest bidRequest) { + final Integer mType = bid.getMtype(); + if (mType != null) { + return switch (mType) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + case 3 -> BidType.audio; + case 4 -> BidType.xNative; + default -> BidType.banner; + }; + } + + if (bidRequest != null && CollectionUtils.isNotEmpty(bidRequest.getImp())) { + for (Imp imp : bidRequest.getImp()) { + if (imp.getId().equals(bid.getImpid())) { + if (imp.getBanner() != null) { + return BidType.banner; + } else if (imp.getVideo() != null) { + return BidType.video; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } else if (imp.getAudio() != null) { + return BidType.audio; + } + } + } + } + + return BidType.banner; + } } diff --git a/src/main/resources/bidder-config/connectad.yaml b/src/main/resources/bidder-config/connectad.yaml index e51102b891c..628286cb662 100644 --- a/src/main/resources/bidder-config/connectad.yaml +++ b/src/main/resources/bidder-config/connectad.yaml @@ -12,8 +12,14 @@ adapters: maintainer-email: support@connectad.io app-media-types: - banner + - video + - native + - audio site-media-types: - banner + - video + - native + - audio supported-vendors: vendor-id: 138 usersync: diff --git a/src/test/java/org/prebid/server/bidder/connectad/ConnectAdBidderTest.java b/src/test/java/org/prebid/server/bidder/connectad/ConnectAdBidderTest.java index 3ac89edc67a..6665203d9df 100644 --- a/src/test/java/org/prebid/server/bidder/connectad/ConnectAdBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/connectad/ConnectAdBidderTest.java @@ -49,6 +49,7 @@ public void makeHttpRequestsShouldReturnErrorWhenImpExtCouldNotBeParsed() { final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() .id("123") + .banner(Banner.builder().w(300).h(250).build()) .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) .build())) .build(); @@ -157,6 +158,26 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtHasNoSiteId() { BidderError.badInput("Error in preprocess of Imp"))); } + @Test + public void makeHttpRequestsShouldReturnErrorIfNoMediaTypePresent() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder + .banner(null) + .video(null) + .xNative(null) + .audio(null)); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(2); + assertThat(result.getErrors(), + containsInAnyOrder(BidderError.badInput("We need a Banner, Video, Native or Audio Object in the request"), + BidderError.badInput("Error in preprocess of Imp"))); + } + @Test public void impSecureShouldBeOneIfSitePageStartsFromHttps() { // given @@ -180,6 +201,58 @@ public void impSecureShouldBeOneIfSitePageStartsFromHttps() { .containsOnly(1); } + @Test + public void makeHttpRequestsShouldPropagateSiteIdAndNetworkId() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder + .id("123") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpConnectAd.of("12345", "67890", BigDecimal.ONE))))); + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .hasSize(1) + .first() + .satisfies(imp -> { + assertThat(imp.getTagid()).isEqualTo("67890"); + assertThat(imp.getExt().get("networkId").asInt()).isEqualTo(12345); + assertThat(imp.getExt().get("siteId").asInt()).isEqualTo(67890); + }); + } + + @Test + public void makeHttpRequestsShouldPropagateSiteIdAndNetworkIdAsStringsIfNonNumeric() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder + .id("123") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpConnectAd.of("net_abc", "site_xyz", BigDecimal.ONE))))); + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .hasSize(1) + .first() + .satisfies(imp -> { + assertThat(imp.getTagid()).isEqualTo("site_xyz"); + assertThat(imp.getExt().get("networkId").asText()).isEqualTo("net_abc"); + assertThat(imp.getExt().get("siteId").asText()).isEqualTo("site_xyz"); + }); + } + private static BidRequest givenBidRequest( Function bidRequestCustomizer, Function impCustomizer) {