Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,8 +59,11 @@ public Result<List<HttpRequest<BidRequest>>> 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()));
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -153,13 +184,13 @@ private static MultiMap resolveHeaders(Device device) {
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> 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<BidderBid> extractBids(BidResponse bidResponse) {
private List<BidderBid> extractBids(BidResponse bidResponse, BidRequest bidRequest) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}
Expand All @@ -170,7 +201,38 @@ private List<BidderBid> 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;
}
}
6 changes: 6 additions & 0 deletions src/main/resources/bidder-config/connectad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<List<HttpRequest<BidRequest>>> 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
Expand All @@ -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<List<HttpRequest<BidRequest>>> 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<List<HttpRequest<BidRequest>>> 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<BidRequest.BidRequestBuilder, BidRequest.BidRequestBuilder> bidRequestCustomizer,
Function<Imp.ImpBuilder, Imp.ImpBuilder> impCustomizer) {
Expand Down