Skip to content

Commit

Permalink
Global bidder parameters support (#1091)
Browse files Browse the repository at this point in the history
  • Loading branch information
BraslavskiyAndrey authored Sep 16, 2021
1 parent 8f85e76 commit e4d5b14
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 25 deletions.
16 changes: 14 additions & 2 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -1012,13 +1012,14 @@ private ExtRequest prepareExt(String bidder,
final boolean suppressSchains = extPrebidSchains != null;
final boolean suppressBidderConfig = extPrebidBidderconfig != null;
final boolean suppressPrebidData = extPrebidData != null;
final boolean suppressBidderParameters = extPrebid != null && extPrebid.getBidderparams() != null;

if (bidderToPrebidBidders.isEmpty()
&& bidderToMultiBid.isEmpty()
&& !suppressSchains
&& !suppressBidderConfig
&& !suppressPrebidData) {

&& !suppressPrebidData
&& !suppressBidderParameters) {
return requestExt;
}

Expand All @@ -1035,6 +1036,7 @@ private ExtRequest prepareExt(String bidder,
extPrebidBuilder
.multibid(resolveExtRequestMultiBids(bidderToMultiBid.get(bidder), bidder))
.bidders(bidders)
.bidderparams(prepareBidderParameters(extPrebid, bidder))
.schains(null)
.data(null)
.bidderconfig(null)
Expand All @@ -1049,6 +1051,16 @@ private List<ExtRequestPrebidMultiBid> resolveExtRequestMultiBids(MultiBidConfig
: null;
}

/**
* Prepares parameters for specified bidder removing parameters for all other bidders.
* Returns null if there are no parameters for specified bidder.
*/
private ObjectNode prepareBidderParameters(ExtRequestPrebid prebid, String bidder) {
final ObjectNode bidderParams = prebid != null ? prebid.getBidderparams() : null;
final JsonNode params = bidderParams != null ? bidderParams.get(bidder) : null;
return params != null ? mapper.mapper().createObjectNode().set(bidder, params) : null;
}

/**
* Updates 'account.*.request', 'request' and 'no_cookie_requests' metrics for each {@link BidderRequest}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import org.prebid.server.auction.PriceGranularity;
import org.prebid.server.auction.TimeoutResolver;
import org.prebid.server.auction.model.IpAddress;
import org.prebid.server.auction.model.Tuple2;
import org.prebid.server.exception.BlacklistedAppException;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.identity.IdGenerator;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.json.JsonMerger;
import org.prebid.server.model.CaseInsensitiveMultiMap;
import org.prebid.server.model.HttpRequestContext;
import org.prebid.server.proto.openrtb.ext.request.ExtDevice;
Expand Down Expand Up @@ -54,8 +56,8 @@ public class Ortb2ImplicitParametersResolver {

private static final Logger logger = LoggerFactory.getLogger(Ortb2ImplicitParametersResolver.class);

private static final String WEB_CHANNEL = "web";
private static final String APP_CHANNEL = "app";
public static final String WEB_CHANNEL = "web";
public static final String APP_CHANNEL = "app";

private static final String PREBID_EXT = "prebid";
private static final String BIDDER_EXT = "bidder";
Expand All @@ -69,6 +71,7 @@ public class Ortb2ImplicitParametersResolver {
private final ImplicitParametersExtractor paramsExtractor;
private final IpAddressHelper ipAddressHelper;
private final IdGenerator sourceIdGenerator;
private final JsonMerger jsonMerger;
private final JacksonMapper mapper;

public Ortb2ImplicitParametersResolver(boolean shouldCacheOnlyWinningBids,
Expand All @@ -77,6 +80,7 @@ public Ortb2ImplicitParametersResolver(boolean shouldCacheOnlyWinningBids,
ImplicitParametersExtractor paramsExtractor,
IpAddressHelper ipAddressHelper,
IdGenerator sourceIdGenerator,
JsonMerger jsonMerger,
JacksonMapper mapper) {

this.shouldCacheOnlyWinningBids = shouldCacheOnlyWinningBids;
Expand All @@ -85,6 +89,7 @@ public Ortb2ImplicitParametersResolver(boolean shouldCacheOnlyWinningBids,
this.paramsExtractor = Objects.requireNonNull(paramsExtractor);
this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper);
this.sourceIdGenerator = Objects.requireNonNull(sourceIdGenerator);
this.jsonMerger = Objects.requireNonNull(jsonMerger);
this.mapper = Objects.requireNonNull(mapper);
}

Expand Down Expand Up @@ -123,8 +128,7 @@ BidRequest resolve(BidRequest bidRequest,
final Source source = bidRequest.getSource();
final Source populatedSource = populateSource(source);

final List<Imp> imps = bidRequest.getImp();
final List<Imp> populatedImps = populateImps(imps, httpRequest);
final List<Imp> populatedImps = populateImps(bidRequest, httpRequest);

final Integer at = bidRequest.getAt();
final Integer resolvedAt = resolveAt(at);
Expand All @@ -136,6 +140,7 @@ BidRequest resolve(BidRequest bidRequest,
final Long resolvedTmax = resolveTmax(tmax, timeoutResolver);

final ExtRequest ext = bidRequest.getExt();
final List<Imp> imps = bidRequest.getImp();
final ExtRequest populatedExt = populateRequestExt(
ext, bidRequest, ObjectUtils.defaultIfNull(populatedImps, imps), endpoint);

Expand Down Expand Up @@ -414,28 +419,49 @@ private Source populateSource(Source source) {
}

/**
* - Updates imps with security 1, when secured request was received and imp security was not defined.
* - Updates imps with security and bidderparams.
* - Moves bidder parameters from imp.ext._bidder_ to imp.ext.prebid.bidder._bidder_
*/
private List<Imp> populateImps(List<Imp> imps, HttpRequestContext httpRequest) {
private List<Imp> populateImps(BidRequest bidRequest, HttpRequestContext httpRequest) {
final List<Imp> imps = bidRequest.getImp();
if (CollectionUtils.isEmpty(imps)) {
return null;
}

final Integer secureFromRequest = paramsExtractor.secureFrom(httpRequest);
final ObjectNode globalBidderParams = extractGlobalBidderParams(bidRequest);

if (!shouldModifyImps(imps, secureFromRequest)) {
if (!shouldModifyImps(imps, secureFromRequest, globalBidderParams)) {
return imps;
}

return imps.stream()
.map(imp -> populateImp(imp, secureFromRequest))
.map(imp -> populateImp(imp, secureFromRequest, globalBidderParams))
.collect(Collectors.toList());
}

private boolean shouldModifyImps(List<Imp> imps, Integer secureFromRequest) {
private ObjectNode extractGlobalBidderParams(BidRequest bidRequest) {
final ExtRequest extRequest = bidRequest.getExt();
final ExtRequestPrebid extBidPrebid = extRequest != null ? extRequest.getPrebid() : null;
final ObjectNode bidderParams = extBidPrebid != null ? extBidPrebid.getBidderparams() : null;

return isObjectNode(bidderParams)
? removeNonBidderFields(bidderParams)
: mapper.mapper().createObjectNode();
}

private static ObjectNode removeNonBidderFields(ObjectNode node) {
for (String field : IMP_EXT_NON_BIDDER_FIELDS) {
node.remove(field);
}

return node;
}

private boolean shouldModifyImps(List<Imp> imps, Integer secureFromRequest, ObjectNode globalBidderParams) {
return imps.stream()
.anyMatch(imp -> shouldSetImpSecure(imp, secureFromRequest) || shouldMoveBidderParams(imp));
.anyMatch(imp -> shouldSetImpSecure(imp, secureFromRequest) || shouldMoveBidderParams(imp)
|| !globalBidderParams.isEmpty());
}

private boolean shouldSetImpSecure(Imp imp, Integer secureFromRequest) {
Expand All @@ -451,31 +477,50 @@ private boolean isImpExtBidderField(String field) {
return !IMP_EXT_NON_BIDDER_FIELDS.contains(field);
}

private Imp populateImp(Imp imp, Integer secureFromRequest) {
private Imp populateImp(Imp imp, Integer secureFromRequest, ObjectNode globalBidderParams) {
final boolean shouldSetSecure = shouldSetImpSecure(imp, secureFromRequest);
final boolean shouldMoveBidderParams = shouldMoveBidderParams(imp);
final boolean shouldUpdateImpExt = shouldMoveBidderParams || !globalBidderParams.isEmpty();

if (shouldSetSecure || shouldMoveBidderParams) {
if (shouldSetSecure || shouldUpdateImpExt) {
final ObjectNode impExt = imp.getExt();

return imp.toBuilder()
.secure(shouldSetSecure ? Integer.valueOf(1) : imp.getSecure())
.ext(shouldMoveBidderParams ? populateImpExt(impExt) : impExt)
.ext(shouldUpdateImpExt
? populateImpExt(impExt, globalBidderParams, shouldMoveBidderParams)
: impExt)
.build();
}

return imp;
}

private ObjectNode populateImpExt(ObjectNode impExt) {
final ObjectNode modifiedExt = impExt.deepCopy();
private ObjectNode populateImpExt(ObjectNode impExt,
ObjectNode globalBidderParams,
boolean shouldMoveBidderParams) {
final ObjectNode impExtCopy = prepareValidImpExtCopy(impExt);
final ObjectNode normalizedExt = shouldMoveBidderParams ? moveBidderParamsToPrebid(impExtCopy) : impExtCopy;
if (!globalBidderParams.isEmpty()) {
mergeGlobalBidderParamsToImp(normalizedExt, globalBidderParams);
}

final ObjectNode modifiedExtPrebid = getOrCreateChildObjectNode(modifiedExt, PREBID_EXT);
modifiedExt.replace(PREBID_EXT, modifiedExtPrebid);
return normalizedExt;
}

private ObjectNode prepareValidImpExtCopy(ObjectNode impExt) {
final ObjectNode copiedImpExt = impExt != null ? impExt.deepCopy() : mapper.mapper().createObjectNode();
final ObjectNode modifiedExtPrebid = getOrCreateChildObjectNode(copiedImpExt, PREBID_EXT);
copiedImpExt.replace(PREBID_EXT, modifiedExtPrebid);
final ObjectNode modifiedExtPrebidBidder = getOrCreateChildObjectNode(modifiedExtPrebid, BIDDER_EXT);
modifiedExtPrebid.replace(BIDDER_EXT, modifiedExtPrebidBidder);
return copiedImpExt;
}

final Set<String> bidderFields = StreamUtil.asStream(modifiedExt.fieldNames())
private ObjectNode moveBidderParamsToPrebid(ObjectNode impExt) {
final ObjectNode modifiedExtPrebidBidder = (ObjectNode) impExt.get(PREBID_EXT).get(BIDDER_EXT);

final Set<String> bidderFields = StreamUtil.asStream(impExt.fieldNames())
.filter(this::isImpExtBidderField)
.collect(Collectors.toSet());

Expand All @@ -484,13 +529,12 @@ private ObjectNode populateImpExt(ObjectNode impExt) {
getOrCreateChildObjectNode(modifiedExtPrebidBidder, currentBidderField);
modifiedExtPrebidBidder.replace(currentBidderField, modifiedExtPrebidBidderCurrentBidder);

final JsonNode extCurrentBidder = modifiedExt.remove(currentBidderField);
final JsonNode extCurrentBidder = impExt.remove(currentBidderField);
if (isObjectNode(extCurrentBidder)) {
modifiedExtPrebidBidderCurrentBidder.setAll((ObjectNode) extCurrentBidder);
}
}

return modifiedExt;
return impExt;
}

private static ObjectNode getOrCreateChildObjectNode(ObjectNode parentNode, String fieldName) {
Expand All @@ -503,6 +547,25 @@ private static boolean isObjectNode(JsonNode node) {
return node != null && node.isObject();
}

private void mergeGlobalBidderParamsToImp(ObjectNode impExt, ObjectNode requestBidderParams) {
final ObjectNode modifiedExtPrebid = (ObjectNode) impExt.get(PREBID_EXT);
final ObjectNode modifiedExtBidder = (ObjectNode) modifiedExtPrebid.get(BIDDER_EXT);

StreamUtil.asStream(requestBidderParams.fields())
.forEach(bidderToParam -> mergeBidderParams(
Tuple2.of(bidderToParam.getKey(), bidderToParam.getValue()), modifiedExtBidder));
}

private void mergeBidderParams(Tuple2<String, JsonNode> bidderToParam, ObjectNode extBidder) {
final String bidder = bidderToParam.getLeft();
final JsonNode impParams = extBidder.get(bidder);
final JsonNode requestParams = bidderToParam.getRight();
final JsonNode mergedParams = impParams == null
? requestParams
: jsonMerger.merge(impParams, requestParams);
extBidder.set(bidder, mergedParams);
}

/**
* Returns updated {@link ExtRequest} if required or null otherwise.
*/
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/prebid/server/json/JsonMerger.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ public <T> T merge(T originalObject, T mergingObject, Class<T> classToCast) {
}

/**
* Returns 'toNode' with merged properties from 'fromNode'.
* Returns 'toNode' with merged properties from 'fromNode'
* <p>
* fromNode object fields has priority over the toNode
*/
public JsonNode merge(JsonNode fromNode, JsonNode toNode) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,9 @@ public class ExtRequestPrebid {
* Defines the contract for bidrequest.ext.prebid.pbs
*/
ExtRequestPrebidPbs pbs;

/**
* Defines the contract for bidrequest.ext.prebid.bidderparams
*/
ObjectNode bidderparams;
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver(
ImplicitParametersExtractor implicitParametersExtractor,
IpAddressHelper ipAddressHelper,
IdGenerator sourceIdGenerator,
JsonMerger jsonMerger,
JacksonMapper mapper) {

final List<String> blacklistedApps = splitToList(blacklistedAppsString);
Expand All @@ -204,6 +205,7 @@ Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver(
implicitParametersExtractor,
ipAddressHelper,
sourceIdGenerator,
jsonMerger,
mapper);
}

Expand Down
27 changes: 26 additions & 1 deletion src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ public void shouldAddMultiBidInfoAboutRequestedBidderIfDataShouldNotBeSuppressed
// given
final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)),
builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder()
.multibid(Collections.singletonList(
.multibid(singletonList(
ExtRequestPrebidMultiBid.of("someBidder", null, 3, "prefix")))
.build())));

Expand Down Expand Up @@ -1661,6 +1661,31 @@ public void shouldAddMultibidInfoOnlyAboutRequestedBidder() {
.containsExactly(ExtRequestPrebidMultiBid.of("someBidder", null, 3, null));
}

@Test
public void shouldRemoveBidderParametersWithBiddersOtherThanBidderRequestBidder() {
// given
final ObjectNode requestBidderParams = mapper.createObjectNode()
.set("someBidder", mapper.createObjectNode().put("key1", "value1"));
requestBidderParams.set("anotherBidder", mapper.createObjectNode().put("key2", "value2"));

final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)),
builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder()
.bidderparams(requestBidderParams)
.auctiontimestamp(1000L)
.build())));

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

// then
final ExtRequest capturedRequest = captureBidRequest().getExt();
assertThat(capturedRequest).isEqualTo(ExtRequest.of(ExtRequestPrebid.builder()
.auctiontimestamp(1000L)
.bidderparams(mapper.createObjectNode()
.set("someBidder", mapper.createObjectNode().put("key1", "value1")))
.build()));
}

@Test
public void shouldPassUserDataAndExtDataOnlyForAllowedBidder() {
// given
Expand Down
Loading

0 comments on commit e4d5b14

Please sign in to comment.