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

Global bidder parameters support #1091

Merged
merged 7 commits into from
Sep 16, 2021
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
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