-
Notifications
You must be signed in to change notification settings - Fork 247
feat: support video, native, and audio media types in ConnectAd Adapter #4506
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
base: master
Are you sure you want to change the base?
Changes from all commits
2d1f95e
4e20129
7cf81a8
57aa9e3
3f9dd5e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,13 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request | |
|
|
||
| for (Imp imp : request.getImp()) { | ||
| try { | ||
| // Validate that at least one media type (banner, video, native, or audio) is present. | ||
| // This extends the previous banner-only validation to support the new media types. | ||
| 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 +105,59 @@ 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(); | ||
| } | ||
|
|
||
| /** | ||
| * Modifies the impression extension to propagate networkId and siteId to the root level. | ||
| * | ||
| * The ConnectAd server endpoint requires these values to be present at the root level | ||
| * of the imp.ext object for proper request routing and processing. This method extracts | ||
| * networkId and siteId from the adapter parameters and adds them to the root level of | ||
| * the extension object. Values are parsed as integers when possible, falling back to | ||
| * string representation if parsing fails (e.g., for non-numeric IDs). | ||
| * | ||
| * @param impExt the original impression extension object | ||
| * @param extImpConnectAd the parsed adapter parameters containing networkId and siteId | ||
| * @return the modified impression extension object with networkId and siteId at root level | ||
| */ | ||
| private ObjectNode modifyImpExt(ObjectNode impExt, ExtImpConnectAd extImpConnectAd) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reorder methods, so they are in call order |
||
| final ObjectNode modifiedExt = impExt != null ? impExt.deepCopy() : mapper.mapper().createObjectNode(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| 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; | ||
| } | ||
|
Comment on lines
+139
to
+156
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need this? This fields already present in initial |
||
|
|
||
| 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 +199,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(); | ||
| } | ||
|
|
@@ -170,7 +216,49 @@ 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(); | ||
| } | ||
|
|
||
| /** | ||
| * Determines the bid type based on the bid's mtype field or falls back to the impression type. | ||
| * | ||
| * The method first checks if the bid contains an explicit mtype value and converts it to the | ||
| * corresponding BidType (1=banner, 2=video, 3=audio, 4=native). If mtype is not present, | ||
| * it looks up the impression by ID in the bidRequest and infers the type from the impression's | ||
| * media type fields (banner, video, native, audio). | ||
| * | ||
| * @param bid the bid response object | ||
| * @param bidRequest the original bid request (guaranteed to be non-null when called from makeBids) | ||
| * @return the determined BidType, defaulting to banner if no type can be determined | ||
| */ | ||
| 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; | ||
| }; | ||
| } | ||
|
|
||
| // bidRequest is guaranteed to be non-null at this point (passed from makeBids method) | ||
| 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; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this validation