Skip to content

Commit

Permalink
Add multiple root schain processing
Browse files Browse the repository at this point in the history
```
ext.prebid.schains: [
   { bidders: ["shinyClicks"], schain: { SCHAIN OBJECT }},
   { bidders: ["*"], schain: { SCHAIN OBJECT }}
]
```
 * pass the correct source.ext.schain to each bidder
 * remove ext.prebid.schains before sending to the adapter

 For more information prebid/prebid-server#1091
  • Loading branch information
DGarbar committed Dec 2, 2019
1 parent 09caf87 commit 39322e1
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 18 deletions.
66 changes: 63 additions & 3 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Site;
import com.iab.openrtb.request.Source;
import com.iab.openrtb.request.User;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
Expand Down Expand Up @@ -44,8 +45,10 @@
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCache;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchains;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
import org.prebid.server.proto.openrtb.ext.request.ExtSite;
import org.prebid.server.proto.openrtb.ext.request.ExtSource;
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
import org.prebid.server.settings.model.Account;
import org.prebid.server.validation.ResponseBidValidator;
Expand All @@ -72,8 +75,8 @@ public class ExchangeService {
private static final String PREBID_EXT = "prebid";
private static final String CONTEXT_EXT = "context";

private static final String DEFAULT_CURRENCY = "USD";
private static final BigDecimal THOUSAND = BigDecimal.valueOf(1000);
private static final String GENERIC_SCHAIN_KEY = "*";

private final BidderCatalog bidderCatalog;
private final StoredResponseProcessor storedResponseProcessor;
Expand Down Expand Up @@ -447,12 +450,13 @@ private static List<BidderRequest> getBidderRequests(
ExtBidRequest requestExt, List<Imp> imps, List<String> firstPartyDataBidders) {

final Map<String, JsonNode> bidderToPrebidBidders = bidderToPrebidBidders(requestExt);
final Map<String, ObjectNode> bidderToPrebidSchains = bidderToPrebidSchains(requestExt);
final List<BidderRequest> bidderRequests = bidderToPrivacyEnforcementResult.entrySet().stream()
// for each bidder create a new request that is a copy of original request except buyerid, imp
// extensions, ext.prebid.data.bidders and ext.prebid.bidders.
// Also, check whether to pass user.ext.data, app.ext.data and site.ext.data or not.
.map(entry -> createBidderRequest(entry.getKey(), bidRequest, requestExt, imps, entry.getValue(),
firstPartyDataBidders, bidderToPrebidBidders))
firstPartyDataBidders, bidderToPrebidBidders, bidderToPrebidSchains))
.collect(Collectors.toList());
Collections.shuffle(bidderRequests);
return bidderRequests;
Expand All @@ -478,13 +482,36 @@ private static Map<String, JsonNode> bidderToPrebidBidders(ExtBidRequest request
return bidderToPrebidParameters;
}

/**
* Extracts a map of bidders to their arguments from {@link ObjectNode} prebid.schains.
*/
private static Map<String, ObjectNode> bidderToPrebidSchains(ExtBidRequest requestExt) {
final ExtRequestPrebid prebid = requestExt == null ? null : requestExt.getPrebid();
final List<ExtRequestPrebidSchains> schains = prebid == null ? null : prebid.getSchains();

if (CollectionUtils.isEmpty(schains)) {
return Collections.emptyMap();
}

final Map<String, ObjectNode> bidderToPrebidSchains = new HashMap<>();
for (ExtRequestPrebidSchains schain : schains) {
final List<String> schainBidders = schain.getBidders();
if (CollectionUtils.isNotEmpty(schainBidders)) {
schainBidders.forEach(bidder -> bidderToPrebidSchains.put(bidder, schain.getSchain()));
}
}
return bidderToPrebidSchains;
}


/**
* Returns created {@link BidderRequest}
*/
private static BidderRequest createBidderRequest(String bidder, BidRequest bidRequest, ExtBidRequest requestExt,
List<Imp> imps, PrivacyEnforcementResult privacyEnforcementResult,
List<String> firstPartyDataBidders,
Map<String, JsonNode> bidderToPrebidBidders) {
Map<String, JsonNode> bidderToPrebidBidders,
Map<String, ObjectNode> bidderToPrebidSchains) {
final App app = bidRequest.getApp();
final ExtApp extApp = extApp(app);
final Site site = bidRequest.getSite();
Expand All @@ -496,6 +523,7 @@ private static BidderRequest createBidderRequest(String bidder, BidRequest bidRe
.imp(prepareImps(bidder, imps, firstPartyDataBidders.contains(bidder)))
.app(prepareApp(app, extApp, firstPartyDataBidders.contains(bidder)))
.site(prepareSite(site, extSite, firstPartyDataBidders.contains(bidder)))
.source(prepareSource(bidder, bidderToPrebidSchains, bidRequest.getSource()))
.ext(prepareExt(bidder, firstPartyDataBidders, bidderToPrebidBidders, requestExt, bidRequest.getExt()))
.build());
}
Expand Down Expand Up @@ -585,14 +613,45 @@ private static Site prepareSite(Site site, ExtSite extSite, boolean useFirstPart
: site;
}

/**
* Checks whether to pass the site.ext.data depending on request having a first party data
* allowed for given bidder or not.
*/
private static Source prepareSource(String bidder, Map<String, ObjectNode> bidderToSchain, Source receivedSource) {
final ObjectNode defaultSchain = bidderToSchain.get(GENERIC_SCHAIN_KEY);
final ObjectNode bidderSchain = bidderToSchain.getOrDefault(bidder, defaultSchain);

if (bidderSchain == null || bidderSchain.isNull()) {
return receivedSource;
}

final ObjectNode jsonExtSource = Json.mapper.valueToTree(ExtSource.of(bidderSchain));

if (receivedSource == null) {
return Source.builder().ext(jsonExtSource).build();
} else {
return receivedSource.toBuilder().ext(jsonExtSource).build();
}
}

/**
* Removes all bidders except the given bidder from bidrequest.ext.prebid.data.bidders and
* bidrequest.ext.prebid.bidders to hide list of allowed bidders from initial request.
* Also mask bidrequest.source.ext.schain.
*/
private static ObjectNode prepareExt(String bidder, List<String> firstPartyDataBidders,
Map<String, JsonNode> bidderToPrebidBidders, ExtBidRequest requestExt,
ObjectNode requestExtNode) {
final ExtRequestPrebid extPrebid = requestExt != null ? requestExt.getPrebid() : null;
if (extPrebid == null) {
return requestExtNode;
}

if (firstPartyDataBidders.isEmpty() && bidderToPrebidBidders.isEmpty()) {
if (extPrebid.getSchains() != null) {
return Json.mapper.valueToTree(ExtBidRequest.of(extPrebid.toBuilder().schains(null).build()));
}

return requestExtNode;
}

Expand All @@ -608,6 +667,7 @@ private static ObjectNode prepareExt(String bidder, List<String> firstPartyDataB
return Json.mapper.valueToTree(ExtBidRequest.of(requestExt.getPrebid().toBuilder()
.data(prebidData)
.bidders(bidders)
.schains(null)
.build()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.Value;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -49,6 +50,11 @@ public class ExtRequestPrebid {
*/
ExtRequestPrebidData data;

/**
* Defines the contract for bidrequest.ext.prebid.schains
*/
List<ExtRequestPrebidSchains> schains;

/**
* Defines the contract for bidrequest.ext.prebid.bidders
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.prebid.server.proto.openrtb.ext.request;

import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.Value;

import java.util.List;

/**
* Defines the contract for bidrequest.ext.prebid.schains
*/
@AllArgsConstructor(staticName = "of")
@Value
public class ExtRequestPrebidSchains {

/**
* Defines the contract for bidrequest.ext.prebid.schains[i].bidders
*/
List<String> bidders;

/**
* Defines the contract for bidrequest.ext.prebid.schains[i].schain
*/
ObjectNode schain;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.prebid.server.proto.openrtb.ext.request;

import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.Value;

/**
* Defines the contract for bidrequest.source.ext
*/
@AllArgsConstructor(staticName = "of")
@Value
public class ExtSource {

/**
* Defines the contract for bidrequest.source.ext.schain
*/
ObjectNode schain;
}

58 changes: 58 additions & 0 deletions src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCacheBids;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCacheVastxml;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchains;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
import org.prebid.server.proto.openrtb.ext.request.ExtSite;
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
Expand Down Expand Up @@ -354,6 +355,63 @@ public void shouldPassRequestWithExtPrebidToDefinedBidder() {
.containsOnly(entry("bidder", mapper.createObjectNode().put("test2", "test2")));
}

@Test
public void shouldPassRequestWithInjectedSchainInSourceExt() {
// given
final String bidder1Name = "bidder1";
final String bidder2Name = "bidder2";
final String bidder3Name = "bidder3";
final Bidder<?> bidder1 = mock(Bidder.class);
final Bidder<?> bidder2 = mock(Bidder.class);
final Bidder<?> bidder3 = mock(Bidder.class);
givenBidder(bidder1Name, bidder1, givenEmptySeatBid());
givenBidder(bidder2Name, bidder2, givenEmptySeatBid());
givenBidder(bidder3Name, bidder3, givenEmptySeatBid());

final ObjectNode schainObject = mapper.createObjectNode().put("var", "1");
final ObjectNode allSchainObject = mapper.createObjectNode().put("any", "any");
final ExtRequestPrebidSchains schain1 = ExtRequestPrebidSchains.of(Arrays.asList(bidder1Name, bidder2Name), schainObject);
final ExtRequestPrebidSchains allSchain = ExtRequestPrebidSchains.of(singletonList("*"), allSchainObject);
final ObjectNode ext = mapper.valueToTree(ExtBidRequest.of(
ExtRequestPrebid.builder()
.schains(Arrays.asList(schain1, allSchain))
.build()));

final BidRequest bidRequest = givenBidRequest(asList(
givenImp(singletonMap(bidder1Name, 1), identity()),
givenImp(singletonMap(bidder2Name, 2), identity()),
givenImp(singletonMap(bidder3Name, 3), identity())),
reqBuilder -> reqBuilder.ext(ext));

// when
exchangeService.holdAuction(givenRequestContext(bidRequest));

// then
final ArgumentCaptor<BidRequest> bidRequest1Captor = ArgumentCaptor.forClass(BidRequest.class);
verify(httpBidderRequester).requestBids(same(bidder1), bidRequest1Captor.capture(), any(), anyBoolean());
final BidRequest capturedBidRequest1 = bidRequest1Captor.getValue();
final JsonNode requestSchain1 = capturedBidRequest1.getSource().getExt().get("schain");
assertThat(requestSchain1).isNotNull();
assertThat(requestSchain1).isEqualTo(schainObject);
assertThat(capturedBidRequest1.getExt().get("prebid").get("schains")).isNull();

final ArgumentCaptor<BidRequest> bidRequest2Captor = ArgumentCaptor.forClass(BidRequest.class);
verify(httpBidderRequester).requestBids(same(bidder2), bidRequest2Captor.capture(), any(), anyBoolean());
final BidRequest capturedBidRequest2 = bidRequest2Captor.getValue();
final JsonNode requestSchain2 = capturedBidRequest2.getSource().getExt().get("schain");
assertThat(requestSchain2).isNotNull();
assertThat(requestSchain2).isEqualTo(schainObject);
assertThat(capturedBidRequest2.getExt().get("prebid").get("schains")).isNull();

final ArgumentCaptor<BidRequest> bidRequest3Captor = ArgumentCaptor.forClass(BidRequest.class);
verify(httpBidderRequester).requestBids(same(bidder3), bidRequest3Captor.capture(), any(), anyBoolean());
final BidRequest capturedBidRequest3 = bidRequest3Captor.getValue();
final JsonNode requestSchain3 = capturedBidRequest3.getSource().getExt().get("schain");
assertThat(requestSchain3).isNotNull();
assertThat(requestSchain3).isEqualTo(allSchainObject);
assertThat(capturedBidRequest3.getExt().get("prebid").get("schains")).isNull();
}

@Test
public void shouldReturnFailedFutureWithUnchangedMessageWhenPrivacyEnforcementServiceFails() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,17 @@
],
"source": {
"fd": 1,
"tid": "tid"
"tid": "tid",
"ext": {
"schain": {
"ver": "1.0"
}
}
},
"regs": {
"ext": {
"us_privacy": "1YN"
}
}
},
"ext": {
"prebid": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@
],
"source": {
"fd": 1,
"tid": "tid"
"tid": "tid",
"ext": {
"schain": {
"ver": "1.0"
}
}
},
"regs": {
"ext": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@
},
"ext": {
"prebid": {
"schains": [
{
"bidders": [
"rubicon"
],
"schain": {
"ver": "1.0",
"complete": 1,
"nodes": [
{
"asi": "superads.com",
"sid": "123",
"hp": 1
}
]
}
},
{
"bidders": [
"*"
],
"schain": {
"ver": "1.0"
}
}
],
"bidders": {
"rubicon": {
"integration": "dmbjs"
Expand Down Expand Up @@ -284,4 +310,5 @@
"ext": {
"us_privacy": "1YN"
}
}}
}
}
Loading

0 comments on commit 39322e1

Please sign in to comment.