Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iqzone: Add mType support #3044

Merged
merged 4 commits into from
Mar 25, 2024
Merged
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
69 changes: 37 additions & 32 deletions src/main/java/org/prebid/server/bidder/iqzone/IqzoneBidder.java
CTMBNara marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.databind.node.TextNode;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.collections4.CollectionUtils;
Expand Down Expand Up @@ -49,32 +50,43 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();

for (Imp imp : request.getImp()) {
final ExtImpIqzone extImpIqzone;
try {
final ExtImpIqzone extImpIqzone = parseImpExt(imp);
final Imp modifiedImp = modifyImp(imp, extImpIqzone);

httpRequests.add(makeHttpRequest(request, modifiedImp));
extImpIqzone = parseImpExt(imp);
} catch (IllegalArgumentException e) {
return Result.withError(BidderError.badInput(e.getMessage()));
}

final Imp modifiedImp = modifyImp(imp, extImpIqzone);
httpRequests.add(makeHttpRequest(request, modifiedImp));
}

return Result.withValues(httpRequests);
}

private ExtImpIqzone parseImpExt(Imp imp) {
return mapper.mapper().convertValue(imp.getExt(), IQZONE_EXT_TYPE_REFERENCE).getBidder();
try {
return mapper.mapper().convertValue(imp.getExt(), IQZONE_EXT_TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage());
}
}

private Imp modifyImp(Imp imp, ExtImpIqzone impExt) {
final String placementId = impExt.getPlacementId();
final ObjectNode modifiedImpExtBidder = mapper.mapper().createObjectNode();
final String endpointId = impExt.getEndpointId();

final boolean isPlacementIdEmpty = StringUtils.isEmpty(placementId);
if (isPlacementIdEmpty && StringUtils.isEmpty(endpointId)) {
return imp;
}

if (StringUtils.isNotEmpty(placementId)) {
final ObjectNode modifiedImpExtBidder = mapper.mapper().createObjectNode();
if (!isPlacementIdEmpty) {
modifiedImpExtBidder.set("placementId", TextNode.valueOf(placementId));
modifiedImpExtBidder.set("type", TextNode.valueOf("publisher"));
} else {
modifiedImpExtBidder.set("endpointId", TextNode.valueOf(impExt.getEndpointId()));
modifiedImpExtBidder.set("endpointId", TextNode.valueOf(endpointId));
modifiedImpExtBidder.set("type", TextNode.valueOf("network"));
}

Expand All @@ -84,54 +96,47 @@ private Imp modifyImp(Imp imp, ExtImpIqzone impExt) {
}

private HttpRequest<BidRequest> makeHttpRequest(BidRequest request, Imp imp) {
final BidRequest outgoingRequest = request.toBuilder().imp(List.of(imp)).build();

final BidRequest outgoingRequest = request.toBuilder().imp(Collections.singletonList(imp)).build();
return BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
}

@Override
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
return Result.of(extractBids(httpCall.getRequest().getPayload(), bidResponse), Collections.emptyList());
return Result.withValues(extractBids(bidResponse));
} catch (DecodeException | PreBidException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private List<BidderBid> extractBids(BidRequest bidRequest, BidResponse bidResponse) {
private List<BidderBid> extractBids(BidResponse bidResponse) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}

return bidsFromResponse(bidRequest, bidResponse);
}

private List<BidderBid> bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) {
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur()))
.filter(Objects::nonNull)
.map(bid -> BidderBid.of(bid, getBidMediaType(bid), bidResponse.getCur()))
CTMBNara marked this conversation as resolved.
Show resolved Hide resolved
.toList();
}

private static BidType getBidType(String impId, List<Imp> imps) {
for (Imp imp : imps) {
if (imp.getId().equals(impId)) {
if (imp.getBanner() != null) {
return BidType.banner;
}
if (imp.getVideo() != null) {
return BidType.video;
}
if (imp.getXNative() != null) {
return BidType.xNative;
}
throw new PreBidException("Unknown impression type for ID: \"%s\"".formatted(impId));
}
private static BidType getBidMediaType(Bid bid) {
final Integer markupType = bid.getMtype();
if (markupType == null) {
throw new PreBidException("Missing MType for bid: " + bid.getId());
}
throw new PreBidException("Failed to find impression for ID: \"%s\"".formatted(impId));

return switch (markupType) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
case 4 -> BidType.xNative;
default -> throw new PreBidException(
"Unable to fetch mediaType " + bid.getMtype() + " in multi-format: " + bid.getImpid());
};
}
}
54 changes: 33 additions & 21 deletions src/test/java/org/prebid/server/bidder/iqzone/IqzoneBidderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.iab.openrtb.request.Banner;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Native;
import com.iab.openrtb.request.Video;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
Expand Down Expand Up @@ -116,6 +113,26 @@ public void makeHttpRequestsShouldModifyImpExtWithEndpointIdAndTypeIfEndpointIdP
.set("bidder", mapper.valueToTree(expectedImpExtBidder)));
}

@Test
public void makeHttpRequestsShouldNotModifyImpExt() {
// given
final BidRequest bidRequest = givenBidRequest(impBuilder ->
impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpIqzone.of(null, null)))));

// when
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.flatExtracting(BidRequest::getImp)
.extracting(Imp::getExt)
.extracting(ext -> ext.get("bidder"))
.map(JsonNode::isEmpty)
.containsExactly(true);
}

@Test
public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
// given
Expand Down Expand Up @@ -163,9 +180,8 @@ public void makeBidsShouldReturnEmptyResponseIfBidResponseSeatBidIsNull() throws
@Test
public void makeBidsShouldCorrectlyProceedWithVideo() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(impBuilder -> impBuilder
.id("someId").video(Video.builder().build())),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("someId"))));
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(2))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -179,9 +195,8 @@ public void makeBidsShouldCorrectlyProceedWithVideo() throws JsonProcessingExcep
@Test
public void makeBidsShouldCorrectlyProceedWithNative() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(impBuilder -> impBuilder
.id("someId").xNative(Native.builder().build())),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("someId"))));
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(4))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -195,9 +210,8 @@ public void makeBidsShouldCorrectlyProceedWithNative() throws JsonProcessingExce
@Test
public void makeBidsShouldCorrectlyProceedWithBanner() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(impBuilder -> impBuilder
.id("someId").banner(Banner.builder().build())),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("someId"))));
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(1))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -211,9 +225,8 @@ public void makeBidsShouldCorrectlyProceedWithBanner() throws JsonProcessingExce
@Test
public void makeBidsShouldReturnErrorIfImpIdDoesNotMatchImpIdInBid() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(impBuilder -> impBuilder
.id("someIdThatIsDifferentFromIDInBid").xNative(Native.builder().build())),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("someId"))));
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(identity()),
mapper.writeValueAsString(givenBidResponse(identity())));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -222,17 +235,16 @@ public void makeBidsShouldReturnErrorIfImpIdDoesNotMatchImpIdInBid() throws Json
assertThat(result.getErrors()).hasSize(1)
.allSatisfy(error -> {
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response);
assertThat(error.getMessage()).startsWith("Failed to find impression for ID:");
assertThat(error.getMessage()).startsWith("Missing MType for bid: null");
});
assertThat(result.getValue()).isEmpty();
}

@Test
public void makeBidsShouldReturnErrorWhenMissingType() throws JsonProcessingException {
public void makeBidsShouldReturnErrorWhenMissingMType() throws JsonProcessingException {
// given
final BidderCall<BidRequest> httpCall = givenHttpCall(
givenBidRequest(impBuilder -> impBuilder.id("someId")),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("someId"))));
final BidderCall<BidRequest> httpCall = givenHttpCall(givenBidRequest(),
mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(null))));

// when
final Result<List<BidderBid>> result = target.makeBids(httpCall, null);
Expand All @@ -241,7 +253,7 @@ public void makeBidsShouldReturnErrorWhenMissingType() throws JsonProcessingExce
assertThat(result.getErrors()).hasSize(1)
.allSatisfy(error -> {
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response);
assertThat(error.getMessage()).startsWith("Unknown impression type for ID");
assertThat(error.getMessage()).startsWith("Missing MType for bid: null");
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"impid": "imp_id",
"price": 3.33,
"adm": "adm001",
"mtype": 1,
"adid": "adid001",
"cid": "cid001",
"crid": "crid001",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"id": "bid_id",
"impid": "imp_id",
"price": 3.33,
"mtype": 1,
"adid": "adid001",
"crid": "crid001",
"cid": "cid001",
Expand Down
Loading