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..062c1c68128 100644 --- a/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java +++ b/src/main/java/org/prebid/server/bidder/connectad/ConnectAdBidder.java @@ -1,12 +1,14 @@ package org.prebid.server.bidder.connectad; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; 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 io.vertx.core.MultiMap; @@ -38,6 +40,7 @@ public class ConnectAdBidder implements Bidder { private static final TypeReference> CONNECTAD_EXT_TYPE_REFERENCE = new TypeReference<>() { }; + private static final String BIDDER_EXT_KEY = "bidder"; private static final String HTTPS_PREFIX = "https"; private final String endpointUrl; @@ -58,7 +61,7 @@ public Result>> makeHttpRequests(BidRequest request for (Imp imp : request.getImp()) { try { 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())); @@ -91,27 +94,42 @@ private ExtImpConnectAd parseImpExt(Imp imp) { } catch (IllegalArgumentException e) { throw new PreBidException("Impression id=%s, has invalid Ext".formatted(imp.getId())); } - final String siteId = extImpConnectAd.getSiteId(); - if (siteId == null) { + if (extImpConnectAd.getSiteId() == null) { throw new PreBidException("Impression id=%s, has no siteId present".formatted(imp.getId())); } 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(String.valueOf(extImpConnectAd.getSiteId())) .secure(secure) .bidfloor(isValidBidFloor ? bidFloor : imp.getBidfloor()) .bidfloorcur(isValidBidFloor ? "USD" : imp.getBidfloorcur()) + .ext(modifyImpExt(imp.getExt())) .build(); } + private ObjectNode modifyImpExt(ObjectNode impExt) { + final ObjectNode modifiedExt = impExt.deepCopy(); + final var bidder = impExt.get(BIDDER_EXT_KEY); + if (bidder != null) { + if (bidder.has("networkId")) { + modifiedExt.set("networkId", bidder.get("networkId")); + } + if (bidder.has("siteId")) { + modifiedExt.set("siteId", bidder.get("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 +171,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 +188,36 @@ 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; + }; + } + + 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/java/org/prebid/server/proto/openrtb/ext/request/connectad/ExtImpConnectAd.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/connectad/ExtImpConnectAd.java index a75c4846f64..cb4e0badd21 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/connectad/ExtImpConnectAd.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/connectad/ExtImpConnectAd.java @@ -9,10 +9,10 @@ public class ExtImpConnectAd { @JsonProperty("networkId") - String networkId; + Integer networkId; @JsonProperty("siteId") - String siteId; + Integer siteId; @JsonProperty("bidfloor") BigDecimal bidFloor; diff --git a/src/main/resources/bidder-config/connectad.yaml b/src/main/resources/bidder-config/connectad.yaml index e51102b891c..0912a23eb98 100644 --- a/src/main/resources/bidder-config/connectad.yaml +++ b/src/main/resources/bidder-config/connectad.yaml @@ -2,18 +2,25 @@ adapters: connectad: # Please uncomment the appropriate endpoint URL for your datacenter # Europe - endpoint: "http://bidder.connectad.io/API?src=pbs" + endpoint: "https://bidder.connectad.io/API?src=pbs" + ortb-version: "2.6" # North/South America - # endpoint: "http://bidder-us.connectad.io/API?src=pbs" + # endpoint: "https://bidder-us.connectad.io/API?src=pbs" # APAC - # endpoint: "http://bidder-apac.connectad.io/API?src=pbs" + # endpoint: "https://bidder-apac.connectad.io/API?src=pbs" endpoint-compression: gzip meta-info: 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/main/resources/static/bidder-params/connectad.json b/src/main/resources/static/bidder-params/connectad.json index e36410928da..5a6f6ec9294 100644 --- a/src/main/resources/static/bidder-params/connectad.json +++ b/src/main/resources/static/bidder-params/connectad.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "ConnectAd S2S dapter Params", + "title": "ConnectAd S2S Adapter Params", "description": "A schema which validates params accepted by the ConnectAd Adapter", "type": "object", "properties": { @@ -20,7 +20,11 @@ }, "bidfloor": { "type": "number", - "description": "Requests Floorprice" + "description": "Requested Floorprice" + }, + "endpointUrl": { + "type": "string", + "description": "Client-side only (Prebid.js): override the bid endpoint URL for testing or a custom datacenter. Ignored by Prebid Server, which always uses its configured endpoint." } }, "required": [ 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..122f4ffad8e 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(); @@ -109,12 +110,15 @@ public void makeBidsShouldReturnEmptyListWhenBidResponseSeatBidIsNull() throws J @Test public void makeBidsShouldReturnBannerBid() throws JsonProcessingException { // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build())) + .build(); final BidderCall httpCall = givenHttpCall( - BidRequest.builder().imp(singletonList(Imp.builder().id("123").build())).build(), + bidRequest, mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123")))); // when - final Result> result = target.makeBids(httpCall, null); + final Result> result = target.makeBids(httpCall, bidRequest); // then assertThat(result.getErrors()).isEmpty(); @@ -146,7 +150,7 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtHasNoSiteId() { impBuilder -> impBuilder .id("123") .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpConnectAd.of("12", null, BigDecimal.ONE))))); + ExtImpConnectAd.of(12, null, BigDecimal.ONE))))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -164,7 +168,7 @@ public void impSecureShouldBeOneIfSitePageStartsFromHttps() { impBuilder -> impBuilder .id("123") .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpConnectAd.of("12", "1", BigDecimal.ONE))))); + ExtImpConnectAd.of(12, 1, BigDecimal.ONE))))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -180,6 +184,32 @@ 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); + }); + } + private static BidRequest givenBidRequest( Function bidRequestCustomizer, Function impCustomizer) { @@ -202,7 +232,7 @@ private static Imp givenImp(Function impCustomiz .w(14) .h(15).build()) .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpConnectAd.of("12", "12", BigDecimal.ONE))))) + ExtImpConnectAd.of(12, 12, BigDecimal.ONE))))) .build(); } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/connectad/test-connectad-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/connectad/test-connectad-bid-request.json index c511c5b8e65..3e0a0922bc4 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/connectad/test-connectad-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/connectad/test-connectad-bid-request.json @@ -3,7 +3,7 @@ "imp": [ { "id": "imp_id", - "secure": 1, + "secure": 0, "banner": { "w": 300, "h": 250 @@ -11,14 +11,15 @@ "tagid": "15", "bidfloor": 14.7, "bidfloorcur": "USD", - "secure": 0, "ext": { "tid": "${json-unit.any-string}", "bidder": { "networkId": "12", "siteId": "15", "bidfloor": 14.7 - } + }, + "networkId": "12", + "siteId": "15" } } ], @@ -45,9 +46,7 @@ "USD" ], "regs": { - "ext": { - "gdpr": 0 - } + "gdpr": 0 }, "ext": { "prebid": {