Skip to content

Commit

Permalink
Pubmatic Adapter Updates (prebid#2965)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoxaAntoxic authored Feb 14, 2024
1 parent 9355d50 commit a4d2870
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 46 deletions.
82 changes: 65 additions & 17 deletions src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.bidder.Bidder;
Expand All @@ -23,7 +24,6 @@
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticBidderImpExt;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticExtData;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticExtDataAdServer;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticWrapper;
import org.prebid.server.bidder.pubmatic.model.response.PubmaticBidExt;
Expand All @@ -48,7 +48,9 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
Expand All @@ -65,6 +67,10 @@ public class PubmaticBidder implements Bidder<BidRequest> {
private static final String WRAPPER_EXT_REQUEST = "wrapper";
private static final String BIDDER_NAME = "pubmatic";
private static final String AE = "ae";
private static final String IMP_EXT_PBADSLOT = "pbadslot";
private static final String IMP_EXT_ADSERVER = "adserver";
private static final List<String> IMP_EXT_DATA_RESERVED_FIELD = List.of(IMP_EXT_PBADSLOT, IMP_EXT_ADSERVER);
private static final String DCTR_VALUE_FORMAT = "%s=%s";

private final String endpointUrl;
private final JacksonMapper mapper;
Expand Down Expand Up @@ -276,11 +282,7 @@ private static Banner assignSizesIfMissing(Banner banner) {
private ObjectNode makeKeywords(PubmaticBidderImpExt impExt) {
final ObjectNode keywordsNode = mapper.mapper().createObjectNode();
putExtBidderKeywords(keywordsNode, impExt.getBidder());

final PubmaticExtData pubmaticExtData = impExt.getData();
if (pubmaticExtData != null) {
putExtDataKeywords(keywordsNode, pubmaticExtData);
}
putExtDataKeywords(keywordsNode, impExt.getData(), impExt.getBidder());

return keywordsNode;
}
Expand All @@ -299,22 +301,66 @@ private static void putExtBidderKeywords(ObjectNode keywords, ExtImpPubmatic ext
} else if (pmZoneIdKeyWords != null) {
keywords.set(PM_ZONE_ID_KEY_NAME, pmZoneIdKeyWords);
}
}

private void putExtDataKeywords(ObjectNode keywords, ObjectNode extData, ExtImpPubmatic extBidder) {
final List<String> dctrValues = new ArrayList<>();

final String dctr = extBidder.getDctr();
if (StringUtils.isNotEmpty(dctr)) {
keywords.put(DCTR_KEY_NAME, dctr);
dctrValues.add(dctr);
}

if (extData != null) {
final String pbaAdSlot = Optional.ofNullable(extData.get(IMP_EXT_PBADSLOT))
.map(JsonNode::asText)
.orElse(null);
final PubmaticExtDataAdServer extAdServer = extractAdServer(extData);
final String adServerName = extAdServer != null ? extAdServer.getName() : null;
final String adServerAdSlot = extAdServer != null ? extAdServer.getAdSlot() : null;
if (AD_SERVER_GAM.equals(adServerName) && StringUtils.isNotEmpty(adServerAdSlot)) {
keywords.put(IMP_EXT_AD_UNIT_KEY, adServerAdSlot);
} else if (StringUtils.isNotEmpty(pbaAdSlot)) {
keywords.put(IMP_EXT_AD_UNIT_KEY, pbaAdSlot);
}

dctrValues.addAll(extractDctrValues(extData));
}

if (!dctrValues.isEmpty()) {
keywords.put(DCTR_KEY_NAME, String.join("|", dctrValues));
}
}

private static void putExtDataKeywords(ObjectNode keywords, PubmaticExtData extData) {
final String pbaAdSlot = extData.getPbAdSlot();
final PubmaticExtDataAdServer extAdServer = extData.getAdServer();
final String adSeverName = extAdServer != null ? extAdServer.getName() : null;
final String adSeverAdSlot = extAdServer != null ? extAdServer.getAdSlot() : null;
if (AD_SERVER_GAM.equals(adSeverName) && StringUtils.isNotEmpty(adSeverAdSlot)) {
keywords.put(IMP_EXT_AD_UNIT_KEY, adSeverAdSlot);
} else if (StringUtils.isNotEmpty(pbaAdSlot)) {
keywords.put(IMP_EXT_AD_UNIT_KEY, pbaAdSlot);
private static List<String> extractDctrValues(ObjectNode extData) {
final List<String> dctrValues = new ArrayList<>();
final Iterator<Map.Entry<String, JsonNode>> extDataIterator = extData.fields();
while (extDataIterator.hasNext()) {
final Map.Entry<String, JsonNode> entry = extDataIterator.next();
final String key = entry.getKey();
if (IMP_EXT_DATA_RESERVED_FIELD.contains(key)) {
continue;
}

final JsonNode value = entry.getValue();
if (value.isValueNode()) {
dctrValues.add(DCTR_VALUE_FORMAT.formatted(key, StringUtils.trim(value.asText())));
} else if (value.isArray()) {
final String arrayNodeValue = Lists.newArrayList(value.elements()).stream()
.map(JsonNode::asText)
.collect(Collectors.joining(","));
dctrValues.add(DCTR_VALUE_FORMAT.formatted(key, arrayNodeValue));
}
}

return dctrValues;
}

private PubmaticExtDataAdServer extractAdServer(ObjectNode extData) {
try {
return mapper.mapper().treeToValue(extData.get(IMP_EXT_ADSERVER), PubmaticExtDataAdServer.class);
} catch (JsonProcessingException e) {
return null;
}
}

Expand Down Expand Up @@ -347,7 +393,9 @@ private ExtRequest modifyExtRequest(ExtRequest extRequest, PubmaticWrapper wrapp
extNode.set(ACAT_EXT_REQUEST, mapper.mapper().valueToTree(acat));
}

return extNode.isEmpty() ? extRequest : mapper.fillExtension(ExtRequest.empty(), extNode);
return extNode.isEmpty()
? extRequest
: mapper.fillExtension(extRequest == null ? ExtRequest.empty() : extRequest, extNode);
}

private static Site modifySite(Site site, String publisherId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prebid.server.bidder.pubmatic.model.request;

import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Value;
import org.prebid.server.proto.openrtb.ext.request.pubmatic.ExtImpPubmatic;

Expand All @@ -8,7 +9,7 @@ public class PubmaticBidderImpExt {

ExtImpPubmatic bidder;

PubmaticExtData data;
ObjectNode data;

Integer ae;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.prebid.server.bidder.model.HttpResponse;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticBidderImpExt;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticExtData;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticExtDataAdServer;
import org.prebid.server.bidder.pubmatic.model.request.PubmaticWrapper;
import org.prebid.server.bidder.pubmatic.model.response.PubmaticBidExt;
Expand Down Expand Up @@ -199,7 +198,9 @@ public void makeHttpRequestsShouldReturnBidRequestExtIfAcatFieldIsValidAndTrimWh
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);

// then
final ExtRequest expectedExtRequest = ExtRequest.empty();
final ExtRequest expectedExtRequest = ExtRequest.of(ExtRequestPrebid.builder()
.bidderparams(pubmaticNode)
.build());
expectedExtRequest.addProperty("acat",
mapper.createArrayNode().add("te st Value").add("test Value").add("Value"));

Expand All @@ -226,7 +227,7 @@ public void makeHttpRequestsShouldMergeWrappersFromImpAndBidRequestExt() {
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getExt)
.containsExactly(expectedBidRequestExt(123, 456));
.containsExactly(expectedBidRequestExt(bidRequest.getExt(), 123, 456));
}

@Test
Expand Down Expand Up @@ -278,7 +279,7 @@ public void makeHttpRequestsShouldUseWrapperFromBidRequestExtIfPresent() {
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getExt)
.containsExactly(expectedBidRequestExt(21, 33));
.containsExactly(expectedBidRequestExt(bidRequestExt, 21, 33));
}

@Test
Expand Down Expand Up @@ -522,14 +523,16 @@ public void makeHttpRequestsShouldSetImpExtFromKeywords() {
@Test
public void makeHttpRequestsShouldAddImpExtAddUnitKeyKeyWordFromDataAdSlotIfAdServerNameIsNotGam() {
// given
final ObjectNode extData = mapper.createObjectNode()
.put("pbadslot", "pbaAdSlot")
.set("adserver", mapper.valueToTree(PubmaticExtDataAdServer.of("adServerName", "adServerAdSlot")));
final BidRequest bidRequest = BidRequest.builder()
.imp(singletonList(Imp.builder()
.id("123")
.banner(Banner.builder().build())
.ext(mapper.valueToTree(PubmaticBidderImpExt.of(
ExtImpPubmatic.builder().build(),
PubmaticExtData.of("pbaAdSlot",
PubmaticExtDataAdServer.of("adServerName", "adServerAdSlot")),
extData,
null
)))
.build()))
Expand All @@ -552,13 +555,16 @@ public void makeHttpRequestsShouldAddImpExtAddUnitKeyKeyWordFromDataAdSlotIfAdSe
@Test
public void makeHttpRequestsShouldAddImpExtAddUnitKeyKeyWordFromAdServerAdSlotIfAdServerNameIsGam() {
// given
final ObjectNode extData = mapper.createObjectNode()
.put("pbadslot", "pbaAdSlot")
.set("adserver", mapper.valueToTree(PubmaticExtDataAdServer.of("gam", "adServerAdSlot")));
final BidRequest bidRequest = BidRequest.builder()
.imp(singletonList(Imp.builder()
.id("123")
.banner(Banner.builder().build())
.ext(mapper.valueToTree(PubmaticBidderImpExt.of(
ExtImpPubmatic.builder().build(),
PubmaticExtData.of("pbaAdSlot", PubmaticExtDataAdServer.of("gam", "adServerAdSlot")),
extData,
null
)))
.build()))
Expand All @@ -578,6 +584,75 @@ public void makeHttpRequestsShouldAddImpExtAddUnitKeyKeyWordFromAdServerAdSlotIf
.containsOnly(expectedImpExt);
}

@Test
public void makeHttpRequestsShouldAddImpExtWithKeyValWithDctrAndExtDataExceptForPbaSlotAndAdServer() {
// given
final ObjectNode extData = mapper.createObjectNode()
.put("pbadslot", "pbaAdSlot")
.put("key1", "value")
.set("adserver", mapper.valueToTree(PubmaticExtDataAdServer.of("gam", "adServerAdSlot")));
final BidRequest bidRequest = BidRequest.builder()
.imp(singletonList(Imp.builder()
.id("123")
.banner(Banner.builder().build())
.ext(mapper.valueToTree(PubmaticBidderImpExt.of(
ExtImpPubmatic.builder().dctr("dctr").build(),
extData,
null
)))
.build()))
.build();

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

// then
final ObjectNode expectedImpExt = mapper.createObjectNode();
expectedImpExt.put("dfp_ad_unit_code", "adServerAdSlot");
expectedImpExt.put("key_val", "dctr|key1=value");
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
.extracting(HttpRequest::getPayload)
.flatExtracting(BidRequest::getImp)
.extracting(Imp::getExt)
.containsOnly(expectedImpExt);
}

@Test
public void makeHttpRequestsShouldAddImpExtWithKeyValWithExtDataWhenDctrIsAbsent() {
// given
final ObjectNode extData = mapper.createObjectNode()
.put("key1", " value")
.put("key2", 1)
.put("key3", true)
.put("key4", 3.42)
.set("key5", mapper.createArrayNode().add("elem1").add("elem2"));
final BidRequest bidRequest = BidRequest.builder()
.imp(singletonList(Imp.builder()
.id("123")
.banner(Banner.builder().build())
.ext(mapper.valueToTree(PubmaticBidderImpExt.of(
ExtImpPubmatic.builder().dctr(null).build(),
extData,
null
)))
.build()))
.build();

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

// then
final ObjectNode expectedImpExt = mapper.createObjectNode();
expectedImpExt.put("key_val", "key1=value|key2=1|key3=true|key4=3.42|key5=elem1,elem2");
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1)
.extracting(HttpRequest::getPayload)
.flatExtracting(BidRequest::getImp)
.extracting(Imp::getExt)
.containsOnly(expectedImpExt);
}

@Test
public void makeHttpRequestsShouldAddImpExtAddAE() {
// given
Expand Down Expand Up @@ -642,7 +717,7 @@ public void makeHttpRequestsShouldSetRequestExtFromWrapExt() {
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getExt)
.containsExactly(expectedBidRequestExt(1, 1));
.containsExactly(expectedBidRequestExt(ExtRequest.empty(), 1, 1));
}

@Test
Expand Down Expand Up @@ -815,6 +890,8 @@ public void makeHttpRequestsShouldFillMethodAndUrlAndExpectedHeaders() {
tuple(HttpUtil.ACCEPT_HEADER.toString(), "application/json"));
}

//-------todo

@Test
public void makeBidderResponseShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
// given
Expand Down Expand Up @@ -1169,11 +1246,13 @@ private static ExtRequest givenBidRequestExt(Integer wrapperProfile, Integer wra
return ExtRequest.of(ExtRequestPrebid.builder().bidderparams(pubmaticNode).build());
}

private static ExtRequest expectedBidRequestExt(Integer wrapperProfile, Integer wrapperVersion) {
private static ExtRequest expectedBidRequestExt(ExtRequest originalExtRequest,
Integer wrapperProfile,
Integer wrapperVersion) {
final ObjectNode wrapperNode = mapper.createObjectNode()
.set("wrapper", mapper.valueToTree(PubmaticWrapper.of(wrapperProfile, wrapperVersion)));

return jacksonMapper.fillExtension(ExtRequest.empty(), wrapperNode);
return jacksonMapper.fillExtension(originalExtRequest, wrapperNode);
}

private static BidRequest givenBidRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,29 @@
}
},
"ext": {
"prebid": {
"server": {
"externalurl": "http://localhost:8080",
"gvlid": 1,
"datacenter": "local",
"endpoint": "/openrtb2/auction"
},
"bidderparams": {
"pubmatic": {
"acat": [
"testValue1",
"testValue2"
]
}
}
},
"acat": [
"testValue1",
"testValue2"
],
"wrapper": {
"version": 1,
"profile": 5123
"profile": 5123,
"version": 1
}
}
}

0 comments on commit a4d2870

Please sign in to comment.