From e1d1209ceefa883197edca2feba5dc10b3bd8ae7 Mon Sep 17 00:00:00 2001 From: Sergii Chernysh Date: Fri, 29 Jan 2021 15:43:43 +0200 Subject: [PATCH] Support imp.ext.prebid.bidder. as a new place for bidder parameters (#976) * Moved bidder parameters from imp.ext to imp.ext.prebid.bidder before proceeding with an auction * Merge bidder parameters instead of replacing whole object * Adjust bid request validation to look into new location of bidder parameters * Update core exchange logic to look for bidder parameters in new location * Update stored response processing to look for bidders in new location * Update Rubicon bidder to look for cpmoverride in new location * Do not pass empty prebid sub-ext to bidders * Fix integration tests after moving bidder parameters to a new place * Tidy up * Update references to imp.ext. with a new location imp.ext.prebid.bidder. --- docs/bidders/appnexus.md | 12 +- docs/bidders/openx.md | 22 +- docs/bidders/pubmatic.md | 10 +- docs/bidders/pubnative.md | 16 +- docs/developers/add-new-bidder.md | 4 +- docs/developers/stored-requests.md | 16 +- docs/endpoints/info/bidders.md | 2 +- docs/endpoints/info/bidders/bidderName.md | 2 +- docs/endpoints/openrtb2/amp.md | 16 +- docs/endpoints/openrtb2/auction.md | 30 +- .../server/auction/AuctionRequestFactory.java | 116 +++- .../server/auction/ExchangeService.java | 50 +- .../auction/StoredResponseProcessor.java | 86 +-- .../bidder/adoppler/AdopplerBidder.java | 2 +- .../server/bidder/rubicon/RubiconBidder.java | 29 +- .../proto/RubiconImpExtPrebidBidder.java | 9 - .../RubiconImpExtPrebidRubiconDebug.java | 9 - .../sharethrough/SharethroughRequestUtil.java | 2 +- .../ext/request/rubicon/ExtImpRubicon.java | 2 + .../request/rubicon/ExtImpRubiconDebug.java | 9 + .../server/validation/RequestValidator.java | 55 +- .../auction/AuctionRequestFactoryTest.java | 106 +++- .../server/auction/ExchangeServiceTest.java | 97 ++- .../auction/StoredResponseProcessorTest.java | 577 ++++++++++++------ .../bidder/adoppler/AdopplerBidderTest.java | 2 +- .../bidder/rubicon/RubiconBidderTest.java | 13 +- .../validation/RequestValidatorTest.java | 96 ++- .../adman/test-auction-adman-request.json | 16 +- .../adocean/test-auction-adocean-request.json | 12 +- .../test-auction-adocean-response.json | 12 +- .../avocet/test-auction-avocet-request.json | 8 +- .../avocet/test-auction-avocet-response.json | 8 +- .../beintoo/test-auction-beintoo-request.json | 8 +- .../test-auction-beintoo-response.json | 8 +- .../test-auction-datablocks-request.json | 20 +- .../test-auction-datablocks-response.json | 20 +- .../test-auction-emxdigital-request.json | 10 +- .../test-auction-emxdigital-response.json | 8 +- .../kubient/test-auction-kubient-request.json | 8 +- .../test-auction-kubient-response.json | 8 +- ...test-auction-rubicon-appnexus-request.json | 134 ++-- ...est-auction-rubicon-appnexus-response.json | 146 +++-- .../test-auction-sharethrough-request.json | 20 +- .../test-auction-sharethrough-response.json | 20 +- .../test-auction-smartadserver-request.json | 14 +- .../test-auction-smartadserver-response.json | 14 +- ...est-auction-triplelift-native-request.json | 8 +- ...st-auction-triplelift-native-response.json | 8 +- .../test-auction-yieldlab-request.json | 22 +- .../test-auction-yieldlab-response.json | 20 +- .../test-auction-zeroclickfraud-request.json | 20 +- .../test-auction-zeroclickfraud-response.json | 20 +- 52 files changed, 1286 insertions(+), 696 deletions(-) delete mode 100644 src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidBidder.java delete mode 100644 src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidRubiconDebug.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubiconDebug.java diff --git a/docs/bidders/appnexus.md b/docs/bidders/appnexus.md index 434ba8048f0..7cd126fda71 100644 --- a/docs/bidders/appnexus.md +++ b/docs/bidders/appnexus.md @@ -37,9 +37,13 @@ that would match with the test creative. }] }, "ext": { - "appnexus": { - "placementId": 13144370 - } - } + "prebid": { + "bidder":{ + "appnexus": { + "placementId": 13144370 + } + } + } + } }] ``` \ No newline at end of file diff --git a/docs/bidders/openx.md b/docs/bidders/openx.md index f7fc0dab04f..f8f3b9fd492 100644 --- a/docs/bidders/openx.md +++ b/docs/bidders/openx.md @@ -34,9 +34,13 @@ If you have any questions regarding setting up, please reach out to your account ] }, "ext": { - "openx": { - "delDomain": "mobile-d.openx.net", - "unit": "541028953" + "prebid": { + "bidder":{ + "openx": { + "delDomain": "mobile-d.openx.net", + "unit": "541028953" + } + } } } } @@ -56,10 +60,14 @@ If you have any questions regarding setting up, please reach out to your account ] }, "ext": { - "openx": { - "unit": "540949380", - "delDomain": "sademo-d.openx.net" - }, + "prebid": { + "bidder":{ + "openx": { + "unit": "540949380", + "delDomain": "sademo-d.openx.net" + } + } + } } } ``` \ No newline at end of file diff --git a/docs/bidders/pubmatic.md b/docs/bidders/pubmatic.md index 610108b2e07..503c8e51026 100644 --- a/docs/bidders/pubmatic.md +++ b/docs/bidders/pubmatic.md @@ -23,9 +23,13 @@ and sizes that would match with the test creative. ] }, "ext":{ - "pubmatic":{ - "publisherId":“156276”, - "adSlot":"pubmatic_test" + "prebid": { + "bidder"{ + "pubmatic":{ + "publisherId":“156276”, + "adSlot":"pubmatic_test" + } + } } } } diff --git a/docs/bidders/pubnative.md b/docs/bidders/pubnative.md index ad421069fb2..17704e91305 100644 --- a/docs/bidders/pubnative.md +++ b/docs/bidders/pubnative.md @@ -10,9 +10,9 @@ Please see [documentation](https://developers.pubnative.net/docs/prebid-adding-p ## Configuration -- bidder should be always set to "pubnative" (`imp.ext.pubnative`) -- zone_id (int) should be always set to 1, unless special use case agreed with our account manager. (`imp.ext.pubnative.zone_id`) -- app_auth_token (string) is unique per publisher app. Please contact our account manager to obtain yours. (`imp.ext.pubnative.app_auth_token`) +- bidder should be always set to "pubnative" (`imp.ext.prebid.bidder.pubnative`) +- zone_id (int) should be always set to 1, unless special use case agreed with our account manager. (`imp.ext.prebid.bidder.pubnative.zone_id`) +- app_auth_token (string) is unique per publisher app. Please contact our account manager to obtain yours. (`imp.ext.prebid.bidder.pubnative.app_auth_token`) An example is illustrated in a section below. @@ -44,9 +44,13 @@ The following json can be used to do a request to prebid server for verifying it ] }, "ext": { - "pubnative": { - "zone_id": 1, - "app_auth_token": "b620e282f3c74787beedda34336a4821" + "prebid": { + "bidder": { + "pubnative": { + "zone_id": 1, + "app_auth_token": "b620e282f3c74787beedda34336a4821" + } + } } } } diff --git a/docs/developers/add-new-bidder.md b/docs/developers/add-new-bidder.md index 1bb8ac8b3dd..47d99dd930e 100644 --- a/docs/developers/add-new-bidder.md +++ b/docs/developers/add-new-bidder.md @@ -16,7 +16,7 @@ Throughout the rest of this document, substitute `{bidder}` with the name you've Bidders may define their own APIs for Publishers pass custom values. It is _strongly encouraged_ that these not duplicate values already present in the [OpenRTB 2.5 spec](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf). -Publishers will send values for these parameters in `request.imp[i].ext.{bidder}` of +Publishers will send values for these parameters in `request.imp[i].ext.prebid.bidder.{bidder}` of [the Auction endpoint](../endpoints/openrtb2/auction.md). Prebid Server will preprocess these so that your bidder will access them at `request.imp[i].ext.bidder`--regardless of what your `{bidder}` name is. @@ -134,7 +134,7 @@ We expect to see at least 90% code coverage on each bidder. Then `POST` an OpenRTB Request to `http://localhost:8080/openrtb2/auction`. -If at least one `request.imp[i].ext.{bidder}` is defined in your Request, then your bidder should be called. +If at least one `request.imp[i].ext.prebid.bidder.{bidder}` is defined in your Request, then your bidder should be called. To test user syncs, [save a UID](../endpoints/setuid.md) using the FamilyName of your Bidder. The next time you use `/openrtb2/auction`, the OpenRTB request sent to your Bidder should have diff --git a/docs/developers/stored-requests.md b/docs/developers/stored-requests.md index 8eac8fe9bc8..285e16b5771 100644 --- a/docs/developers/stored-requests.md +++ b/docs/developers/stored-requests.md @@ -36,8 +36,12 @@ Add the file `stored_imps/{id}.json` and populate it with some [Imp](https://www ] }, "ext": { - "appnexus": { - "placement_id": 10433394 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 10433394 + } + } } } } @@ -83,8 +87,12 @@ You can also store _part_ of the Imp on the server. For example: ] }, "ext": { - "appnexus": { - "placement_id": 10433394 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 10433394 + } + } } } } diff --git a/docs/endpoints/info/bidders.md b/docs/endpoints/info/bidders.md index a3868782993..103b9612389 100644 --- a/docs/endpoints/info/bidders.md +++ b/docs/endpoints/info/bidders.md @@ -3,7 +3,7 @@ ## `GET /info/bidders` This endpoint returns a list of active Bidders supported by Prebid Server. -These are the core values allowed to be used as `request.imp[i].ext.{bidder}` +These are the core values allowed to be used as `request.imp[i].ext.prebid.bidder.{bidder}` keys in [Auction](../openrtb2/auction.md) requests. For detailed info about a specific Bidder, use [`/info/bidders/{bidderName}`](bidders/bidderName.md) diff --git a/docs/endpoints/info/bidders/bidderName.md b/docs/endpoints/info/bidders/bidderName.md index bf4b65dc4e7..418377a45cc 100644 --- a/docs/endpoints/info/bidders/bidderName.md +++ b/docs/endpoints/info/bidders/bidderName.md @@ -40,7 +40,7 @@ The fields hold the following information: If `capabilities.app` or `capabilities.site` do not exist, then this Bidder does not support that platform. OpenRTB Requests which define a `request.app` or `request.site` property will fail if a -`request.imp[i].ext.{bidderName}` exists for a Bidder which doesn't support them. +`request.imp[i].ext.prebid.bidder.{bidderName}` exists for a Bidder which doesn't support them. ## `GET /info/bidders/all` diff --git a/docs/endpoints/openrtb2/amp.md b/docs/endpoints/openrtb2/amp.md index 178b68d48e7..7e7037c4790 100644 --- a/docs/endpoints/openrtb2/amp.md +++ b/docs/endpoints/openrtb2/amp.md @@ -54,12 +54,16 @@ An example Stored Request is given below: "id": "some-impression-id", "banner": {}, // The sizes are defined is set by your AMP tag query params "ext": { - "appnexus": { - // Insert parameters here - }, - "rubicon": { - // Insert parameters here - } + "prebid": { + "bidder": { + "appnexus": { + // Insert parameters here + }, + "rubicon": { + // Insert parameters here + } + } + } } } ] diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index a48a9a99bd0..f0792260993 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -39,8 +39,12 @@ The following is a "hello world" request which fetches the [Prebid sample ad](ht ] }, "ext": { - "appnexus": { - "placement_id": 10433394 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 10433394 + } + } } } } @@ -107,11 +111,11 @@ These fall under the `ext` property of JSON objects. If `ext` is defined on an object, Prebid Server uses the following conventions: -1. `ext` in "Request objects" uses `ext.prebid` and/or `ext.{anyBidderCode}`. +1. `ext` in "Request objects" uses `ext.prebid` and/or `ext.prebid.bidder.{anyBidderCode}`. 2. `ext` on "Response objects" uses `ext.prebid` and/or `ext.bidder`. The only exception here is the top-level `BidResponse`, because it's bidder-independent. -`ext.{anyBidderCode}` and `ext.bidder` extensions are defined by bidders. +`ext.prebid.bidder.{anyBidderCode}` and `ext.bidder` extensions are defined by bidders. `ext.prebid` extensions are defined by Prebid Server. Exceptions are made for extensions with "standard" recommendations: @@ -289,11 +293,15 @@ This can be used to request bids from the same Bidder with different params. For "mimes": ["video/mp4"] }, "ext": { - "appnexus: { - "placement_id": 123 - }, - "districtm": { - "placement_id": 456 + "prebid": { + "bidder": { + "appnexus: { + "placement_id": 123 + }, + "districtm": { + "placement_id": 456 + } + } } } } @@ -324,7 +332,7 @@ For example, if the Request defines an alias like this: } ``` -then any `imp.ext.appnexus` params will actually go to the **rubicon** adapter. +then any `imp.ext.prebid.bidder.appnexus` params will actually go to the **rubicon** adapter. It will become impossible to fetch bids from Appnexus within that Request. #### Bidder Response Times @@ -757,7 +765,7 @@ This section describes the ways in which Prebid Server **breaks** the OpenRTB sp Prebid Server returns a 400 on requests which define `wseat` or `bseat`. We may add support for these in the future, if there's compelling need. -Instead, an impression is only offered to a bidder if `bidrequest.imp[i].ext.{bidderName}` exists. +Instead, an impression is only offered to a bidder if `bidrequest.imp[i].ext.prebid.bidder.{bidderName}` exists. This supports publishers who want to sell different impressions to different bidders. diff --git a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java index de779a419fe..c191be47ba8 100644 --- a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -52,11 +53,13 @@ import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountStatus; import org.prebid.server.util.HttpUtil; +import org.prebid.server.util.StreamUtil; import org.prebid.server.validation.RequestValidator; import org.prebid.server.validation.model.ValidationResult; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Currency; import java.util.HashMap; @@ -68,8 +71,6 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; /** * Used in OpenRTB request processing. @@ -83,6 +84,12 @@ public class AuctionRequestFactory { private static final String WEB_CHANNEL = "web"; private static final String APP_CHANNEL = "app"; + private static final String PREBID_EXT = "prebid"; + private static final String BIDDER_EXT = "bidder"; + + private static final Set IMP_EXT_NON_BIDDER_FIELDS = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList(PREBID_EXT, "context"))); + private final long maxRequestSize; private final boolean enforceValidAccount; private final boolean shouldCacheOnlyWinningBids; @@ -454,18 +461,93 @@ 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 1, when secured request was received and imp security was not defined. + * - Moves bidder parameters from imp.ext._bidder_ to imp.ext.prebid.bidder._bidder_ */ private List populateImps(List imps, HttpServerRequest request) { - List result = null; + if (CollectionUtils.isEmpty(imps)) { + return null; + } + + final Integer secureFromRequest = paramsExtractor.secureFrom(request); - if (Objects.equals(paramsExtractor.secureFrom(request), 1) - && imps.stream().map(Imp::getSecure).anyMatch(Objects::isNull)) { - result = imps.stream() - .map(imp -> imp.getSecure() == null ? imp.toBuilder().secure(1).build() : imp) - .collect(Collectors.toList()); + if (!shouldModifyImps(imps, secureFromRequest)) { + return imps; } - return result; + + return imps.stream() + .map(imp -> populateImp(imp, secureFromRequest)) + .collect(Collectors.toList()); + } + + private boolean shouldModifyImps(List imps, Integer secureFromRequest) { + return imps.stream() + .anyMatch(imp -> shouldSetImpSecure(imp, secureFromRequest) || shouldMoveBidderParams(imp)); + } + + private boolean shouldSetImpSecure(Imp imp, Integer secureFromRequest) { + return imp.getSecure() == null && Objects.equals(secureFromRequest, 1); + } + + private boolean shouldMoveBidderParams(Imp imp) { + return imp.getExt() != null + && StreamUtil.asStream(imp.getExt().fieldNames()).anyMatch(this::isImpExtBidderField); + } + + private boolean isImpExtBidderField(String field) { + return !IMP_EXT_NON_BIDDER_FIELDS.contains(field); + } + + private Imp populateImp(Imp imp, Integer secureFromRequest) { + final boolean shouldSetSecure = shouldSetImpSecure(imp, secureFromRequest); + final boolean shouldMoveBidderParams = shouldMoveBidderParams(imp); + + if (shouldSetSecure || shouldMoveBidderParams) { + final ObjectNode impExt = imp.getExt(); + + return imp.toBuilder() + .secure(shouldSetSecure ? Integer.valueOf(1) : imp.getSecure()) + .ext(shouldMoveBidderParams ? populateImpExt(impExt) : impExt) + .build(); + } + + return imp; + } + + private ObjectNode populateImpExt(ObjectNode impExt) { + final ObjectNode modifiedExt = impExt.deepCopy(); + + final ObjectNode modifiedExtPrebid = getOrCreateChildObjectNode(modifiedExt, PREBID_EXT); + modifiedExt.replace(PREBID_EXT, modifiedExtPrebid); + final ObjectNode modifiedExtPrebidBidder = getOrCreateChildObjectNode(modifiedExtPrebid, BIDDER_EXT); + modifiedExtPrebid.replace(BIDDER_EXT, modifiedExtPrebidBidder); + + final Set bidderFields = StreamUtil.asStream(modifiedExt.fieldNames()) + .filter(this::isImpExtBidderField) + .collect(Collectors.toSet()); + + for (final String currentBidderField : bidderFields) { + final ObjectNode modifiedExtPrebidBidderCurrentBidder = + getOrCreateChildObjectNode(modifiedExtPrebidBidder, currentBidderField); + modifiedExtPrebidBidder.replace(currentBidderField, modifiedExtPrebidBidderCurrentBidder); + + final JsonNode extCurrentBidder = modifiedExt.remove(currentBidderField); + if (isObjectNode(extCurrentBidder)) { + modifiedExtPrebidBidderCurrentBidder.setAll((ObjectNode) extCurrentBidder); + } + } + + return modifiedExt; + } + + private static ObjectNode getOrCreateChildObjectNode(ObjectNode parentNode, String fieldName) { + final JsonNode childNode = parentNode.get(fieldName); + + return isObjectNode(childNode) ? (ObjectNode) childNode : parentNode.objectNode(); + } + + private static boolean isObjectNode(JsonNode node) { + return node != null && node.isObject(); } /** @@ -638,7 +720,7 @@ private Map aliasesOrNull(ExtRequestPrebid prebid, List imp final Map resolvedAliases = imps.stream() .filter(Objects::nonNull) .filter(imp -> imp.getExt() != null) // request validator is not called yet - .flatMap(imp -> asStream(imp.getExt().fieldNames()) + .flatMap(imp -> StreamUtil.asStream(biddersFromImp(imp)) .filter(bidder -> !aliases.containsKey(bidder)) .filter(bidderCatalog::isAlias)) .distinct() @@ -654,6 +736,13 @@ private Map aliasesOrNull(ExtRequestPrebid prebid, List imp return result; } + private Iterator biddersFromImp(Imp imp) { + final JsonNode extPrebid = imp.getExt().get(PREBID_EXT); + final JsonNode extPrebidBidder = isObjectNode(extPrebid) ? extPrebid.get(BIDDER_EXT) : null; + + return isObjectNode(extPrebidBidder) ? extPrebidBidder.fieldNames() : Collections.emptyIterator(); + } + /** * Returns populated {@link ExtRequestPrebidCache} or null if no changes were applied. */ @@ -717,11 +806,6 @@ private static Long resolveTmax(Long requestTimeout, TimeoutResolver timeoutReso return !Objects.equals(requestTimeout, timeout) ? timeout : null; } - private static Stream asStream(Iterator iterator) { - final Iterable iterable = () -> iterator; - return StreamSupport.stream(iterable.spliterator(), false); - } - private static R getIfNotNull(T target, Function getter) { return target != null ? getter.apply(target) : null; } diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index d2abfecdffd..11254451e54 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -79,6 +79,7 @@ public class ExchangeService { private static final Logger logger = LoggerFactory.getLogger(ExchangeService.class); private static final String PREBID_EXT = "prebid"; + private static final String BIDDER_EXT = "bidder"; private static final String CONTEXT_EXT = "context"; private static final String DATA = "data"; private static final String ALL_BIDDERS_CONFIG = "*"; @@ -269,9 +270,9 @@ private static List populateStoredResponse(StoredResponseResult storedRespo * i.e. the bidders will not see any other extension fields. If Imp extension name is alias, which is also defined * in bidRequest.ext.prebid.aliases and valid, separate {@link BidRequest} will be created for this alias and sent * to appropriate bidder. - * For example suppose {@link BidRequest} has two {@link Imp}s. First one with imp.ext[].rubicon and - * imp.ext[].rubiconAlias and second with imp.ext[].appnexus and imp.ext[].rubicon. Three {@link BidRequest}s will - * be created: + * For example suppose {@link BidRequest} has two {@link Imp}s. First one with imp.ext.prebid.bidder.rubicon and + * imp.ext.prebid.bidder.rubiconAlias and second with imp.ext.prebid.bidder.appnexus and + * imp.ext.prebid.bidder.rubicon. Three {@link BidRequest}s will be created: * 1. {@link BidRequest} with one {@link Imp}, where bidder extension points to rubiconAlias extension and will be * sent to Rubicon bidder. * 2. {@link BidRequest} with two {@link Imp}s, where bidder extension points to appropriate rubicon extension from @@ -291,15 +292,14 @@ private static List populateStoredResponse(StoredResponseResult storedRespo private Future> extractBidderRequests(AuctionContext context, List requestedImps, BidderAliases aliases) { - // sanity check: discard imps without extension + final List imps = requestedImps.stream() - .filter(imp -> imp.getExt() != null) + .filter(imp -> bidderParamsFromImpExt(imp.getExt()) != null) .collect(Collectors.toList()); // identify valid bidders and aliases out of imps final List bidders = imps.stream() - .flatMap(imp -> StreamUtil.asStream(imp.getExt().fieldNames()) - .filter(bidder -> !Objects.equals(bidder, PREBID_EXT) && !Objects.equals(bidder, CONTEXT_EXT)) + .flatMap(imp -> StreamUtil.asStream(bidderParamsFromImpExt(imp.getExt()).fieldNames()) .filter(bidder -> isValidBidder(bidder, aliases))) .distinct() .collect(Collectors.toList()); @@ -307,6 +307,10 @@ private Future> extractBidderRequests(AuctionContext context return makeBidderRequests(bidders, context, aliases, imps); } + private static JsonNode bidderParamsFromImpExt(ObjectNode ext) { + return ext.get(PREBID_EXT).get(BIDDER_EXT); + } + /** * Checks if bidder name is valid in case when bidder can also be alias name. */ @@ -587,7 +591,7 @@ private BidderRequest createBidderRequest(BidderPrivacyResult bidderPrivacyResul */ private List prepareImps(String bidder, List imps, boolean useFirstPartyData) { return imps.stream() - .filter(imp -> imp.getExt().hasNonNull(bidder)) + .filter(imp -> bidderParamsFromImpExt(imp.getExt()).hasNonNull(bidder)) .map(imp -> imp.toBuilder() .ext(prepareImpExt(bidder, imp.getExt(), useFirstPartyData)) .build()) @@ -599,33 +603,35 @@ private List prepareImps(String bidder, List imps, boolean useFirstPar *
    *
  • "prebid" field populated with an imp.ext.prebid field value, may be null
  • *
  • "context" field populated with an imp.ext.context field value, may be null
  • - *
  • "bidder" field populated with an imp.ext.{bidder} field value, not null
  • + *
  • "bidder" field populated with an imp.ext.prebid.bidder.{bidder} field value, not null
  • *
*/ private ObjectNode prepareImpExt(String bidder, ObjectNode impExt, boolean useFirstPartyData) { - final JsonNode impExtPrebid = prepareImpExtPrebid(bidder, impExt.get(PREBID_EXT)); - final ObjectNode result = mapper.mapper().valueToTree(ExtPrebid.of(impExtPrebid, impExt.get(bidder))); + final JsonNode impExtPrebid = cleanBidderParamsFromImpExtPrebid(impExt.get(PREBID_EXT)); + final JsonNode impExtBidder = bidderParamsFromImpExt(impExt).get(bidder); - final JsonNode contextNode = impExt.get(CONTEXT_EXT); - final boolean isContextNodePresent = contextNode != null && !contextNode.isNull(); - if (isContextNodePresent) { - final JsonNode contextNodeCopy = contextNode.deepCopy(); + final ObjectNode result = mapper.mapper().valueToTree(ExtPrebid.of(impExtPrebid, impExtBidder)); + + if (impExt.hasNonNull(CONTEXT_EXT)) { + final JsonNode contextNodeCopy = impExt.get(CONTEXT_EXT).deepCopy(); if (!useFirstPartyData && contextNodeCopy.isObject()) { ((ObjectNode) contextNodeCopy).remove(DATA); } result.set(CONTEXT_EXT, contextNodeCopy); } + return result; } - private JsonNode prepareImpExtPrebid(String bidder, JsonNode extImpPrebidNode) { - if (extImpPrebidNode != null && extImpPrebidNode.hasNonNull(bidder)) { - final ExtImpPrebid extImpPrebid = extImpPrebid(extImpPrebidNode).toBuilder() - .bidder((ObjectNode) extImpPrebidNode.get(bidder)) // leave appropriate bidder related data - .build(); - return mapper.mapper().valueToTree(extImpPrebid); + private JsonNode cleanBidderParamsFromImpExtPrebid(JsonNode extImpPrebidNode) { + if (extImpPrebidNode.size() > 1) { + return mapper.mapper().valueToTree( + extImpPrebid(extImpPrebidNode).toBuilder() + .bidder(null) + .build()); } - return extImpPrebidNode; + + return null; } /** diff --git a/src/main/java/org/prebid/server/auction/StoredResponseProcessor.java b/src/main/java/org/prebid/server/auction/StoredResponseProcessor.java index 63a827f100f..c3ec2c54a00 100644 --- a/src/main/java/org/prebid/server/auction/StoredResponseProcessor.java +++ b/src/main/java/org/prebid/server/auction/StoredResponseProcessor.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Imp; import com.iab.openrtb.response.Bid; @@ -26,21 +27,19 @@ import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.settings.ApplicationSettings; import org.prebid.server.settings.model.StoredResponseDataResult; +import org.prebid.server.util.StreamUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; /** * Resolves stored response data retrieving and BidderResponse merging processes. @@ -48,8 +47,10 @@ public class StoredResponseProcessor { private static final String PREBID_EXT = "prebid"; - private static final String CONTEXT_EXT = "context"; + private static final String BIDDER_EXT = "bidder"; + private static final String DEFAULT_BID_CURRENCY = "USD"; + private static final TypeReference> SEATBID_LIST_TYPEREFERENCE = new TypeReference>() { }; @@ -60,6 +61,7 @@ public class StoredResponseProcessor { public StoredResponseProcessor(ApplicationSettings applicationSettings, BidderCatalog bidderCatalog, JacksonMapper mapper) { + this.applicationSettings = Objects.requireNonNull(applicationSettings); this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.mapper = Objects.requireNonNull(mapper); @@ -72,7 +74,7 @@ Future getStoredResponseResult( final Map storedResponseIdToImpId = new HashMap<>(); try { - fillStoredResponseIdsAndRequestingImps(imps, requiredRequestImps, storedResponseIdToImpId, aliases); + fillStoredResponseIdsAndRequestingImps(imps, aliases, requiredRequestImps, storedResponseIdToImpId); } catch (InvalidRequestException e) { return Future.failedFuture(e); } @@ -90,6 +92,7 @@ Future getStoredResponseResult( List mergeWithBidderResponses(List bidderResponses, List storedResponses, List imps) { + if (CollectionUtils.isEmpty(storedResponses)) { return bidderResponses; } @@ -109,28 +112,34 @@ List mergeWithBidderResponses(List bidderRespons .collect(Collectors.toList()); } - private void fillStoredResponseIdsAndRequestingImps(List imps, List requiredRequestImps, - Map storedResponseIdToImpId, - BidderAliases aliases) { + private void fillStoredResponseIdsAndRequestingImps(List imps, + BidderAliases aliases, + List requiredRequestImps, + Map storedResponseIdToImpId) { + for (final Imp imp : imps) { final String impId = imp.getId(); final ObjectNode extImpNode = imp.getExt(); final ExtImp extImp = getExtImp(extImpNode, impId); final ExtImpPrebid extImpPrebid = extImp != null ? extImp.getPrebid() : null; - if (extImpPrebid == null) { + + final ExtStoredAuctionResponse storedAuctionResponse = + extImpPrebid != null ? extImpPrebid.getStoredAuctionResponse() : null; + final List storedBidResponse = + extImpPrebid != null ? extImpPrebid.getStoredBidResponse() : null; + + if (storedAuctionResponse == null && storedBidResponse == null) { requiredRequestImps.add(imp); continue; } - final ExtStoredAuctionResponse storedAuctionResponse = extImpPrebid.getStoredAuctionResponse(); final String storedAuctionResponseId = storedAuctionResponse != null ? storedAuctionResponse.getId() : null; if (StringUtils.isNotEmpty(storedAuctionResponseId)) { storedResponseIdToImpId.put(storedAuctionResponseId, impId); continue; } - resolveStoredBidResponse(requiredRequestImps, storedResponseIdToImpId, aliases, imp, impId, extImpNode, - extImpPrebid); + resolveStoredBidResponse(storedBidResponse, imp, aliases, requiredRequestImps, storedResponseIdToImpId); } } @@ -138,22 +147,25 @@ private ExtImp getExtImp(ObjectNode extImpNode, String impId) { try { return mapper.mapper().treeToValue(extImpNode, ExtImp.class); } catch (JsonProcessingException e) { - throw new InvalidRequestException(String.format("Error decoding bidRequest.imp.ext for impId = %s : %s", - impId, e.getMessage())); + throw new InvalidRequestException(String.format( + "Error decoding bidRequest.imp.ext for impId = %s : %s", impId, e.getMessage())); } } - private void resolveStoredBidResponse(List requiredRequestImps, Map storedResponseIdToImpId, - BidderAliases aliases, Imp imp, String impId, - ObjectNode extImpNode, ExtImpPrebid extImpPrebid) { + private void resolveStoredBidResponse(List storedBidResponse, + Imp imp, + BidderAliases aliases, + List requiredRequestImps, + Map storedResponseIdToImpId) { + + final String impId = imp.getId(); - final List storedBidResponse = extImpPrebid.getStoredBidResponse(); final Map bidderToStoredId = storedBidResponse != null ? getBidderToStoredResponseId(storedBidResponse, impId) : Collections.emptyMap(); if (!bidderToStoredId.isEmpty()) { - final Imp resolvedBiddersImp = removeStoredResponseBidders(imp, extImpNode, bidderToStoredId.keySet()); - if (hasValidBidder(aliases, resolvedBiddersImp)) { + final Imp resolvedBiddersImp = removeStoredResponseBidders(imp, bidderToStoredId.keySet()); + if (hasValidBidder(resolvedBiddersImp, aliases)) { requiredRequestImps.add(resolvedBiddersImp); } storedResponseIdToImpId.putAll(bidderToStoredId.values().stream() @@ -165,6 +177,7 @@ private void resolveStoredBidResponse(List requiredRequestImps, Map getBidderToStoredResponseId(List extStoredBidResponses, String impId) { + final Map bidderToStoredResponseId = new HashMap<>(); for (final ExtStoredBidResponse extStoredBidResponse : extStoredBidResponses) { final String bidder = extStoredBidResponse.getBidder(); @@ -184,26 +197,29 @@ private Map getBidderToStoredResponseId(List bidders) { - boolean isUpdated = false; - for (final String bidder : bidders) { - if (extImp.hasNonNull(bidder)) { - extImp.remove(bidder); - isUpdated = true; - } + private Imp removeStoredResponseBidders(Imp imp, Set bidders) { + final ObjectNode ext = imp.getExt(); + final JsonNode bidderParams = bidderParamsFromImpExt(ext); + + if (bidderParams == null || StreamUtil.asStream(bidderParams.fieldNames()).noneMatch(bidders::contains)) { + return imp; } - return isUpdated ? imp.toBuilder().ext(extImp).build() : imp; + + final ObjectNode modifiedExt = ext.deepCopy(); + ((ObjectNode) bidderParamsFromImpExt(modifiedExt)).remove(bidders); + + return imp.toBuilder().ext(modifiedExt).build(); } - private boolean hasValidBidder(BidderAliases aliases, Imp resolvedBiddersImp) { - return asStream(resolvedBiddersImp.getExt().fieldNames()) - .anyMatch(bidder -> !Objects.equals(bidder, PREBID_EXT) && !Objects.equals(bidder, CONTEXT_EXT) - && isValidBidder(bidder, aliases)); + private static JsonNode bidderParamsFromImpExt(ObjectNode ext) { + return ext.get(PREBID_EXT).get(BIDDER_EXT); } - private Stream asStream(Iterator iterator) { - final Iterable iterable = () -> iterator; - return StreamSupport.stream(iterable.spliterator(), false); + private boolean hasValidBidder(Imp imp, BidderAliases aliases) { + final JsonNode bidderParams = bidderParamsFromImpExt(imp.getExt()); + + return bidderParams != null + && StreamUtil.asStream(bidderParams.fieldNames()).anyMatch(bidder -> isValidBidder(bidder, aliases)); } private boolean isValidBidder(String bidder, BidderAliases aliases) { diff --git a/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java b/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java index 1b475e56dd2..b8842478526 100644 --- a/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java +++ b/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java @@ -82,7 +82,7 @@ private ExtImpAdoppler parseAndValidateImpExt(Imp imp) { throw new PreBidException(e.getMessage()); } if (StringUtils.isBlank(extImpAdoppler.getAdunit())) { - throw new PreBidException("$.imp.ext.adoppler.adunit required"); + throw new PreBidException("adunit parameter is required for adoppler bidder"); } return extImpAdoppler; } diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java index ac90f63aa8f..fb8040719d1 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -1,6 +1,5 @@ package org.prebid.server.bidder.rubicon; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -47,8 +46,6 @@ import org.prebid.server.bidder.rubicon.proto.RubiconExtPrebidBiddersBidder; import org.prebid.server.bidder.rubicon.proto.RubiconExtPrebidBiddersBidderDebug; import org.prebid.server.bidder.rubicon.proto.RubiconImpExt; -import org.prebid.server.bidder.rubicon.proto.RubiconImpExtPrebidBidder; -import org.prebid.server.bidder.rubicon.proto.RubiconImpExtPrebidRubiconDebug; import org.prebid.server.bidder.rubicon.proto.RubiconImpExtRp; import org.prebid.server.bidder.rubicon.proto.RubiconImpExtRpTrack; import org.prebid.server.bidder.rubicon.proto.RubiconPubExt; @@ -81,6 +78,7 @@ import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUid; import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUidExt; import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtImpRubicon; +import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtImpRubiconDebug; import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtUserTpIdRubicon; import org.prebid.server.proto.openrtb.ext.request.rubicon.RubiconVideoParams; import org.prebid.server.proto.openrtb.ext.response.BidType; @@ -117,7 +115,6 @@ public class RubiconBidder implements Bidder { private static final String TK_XINT_QUERY_PARAMETER = "tk_xint"; private static final String PREBID_SERVER_USER_AGENT = "prebid-server/1.0"; - private static final String PREBID_EXT = "prebid"; private static final String ADSERVER_EID = "adserver.org"; private static final String LIVEINTENT_EID = "liveintent.com"; @@ -1130,29 +1127,9 @@ private Float cmpOverrideFromRequest(BidRequest bidRequest) { } private Float cpmOverrideFromImp(Imp imp) { - final JsonNode extImpPrebidNode = imp.getExt().get(PREBID_EXT); - final ExtImpPrebid prebid = extImpPrebidNode != null ? extImpPrebid(extImpPrebidNode) : null; - final RubiconImpExtPrebidBidder bidder = prebid != null - ? extImpPrebidBidder(prebid.getBidder()) - : null; - final RubiconImpExtPrebidRubiconDebug debug = bidder != null ? bidder.getDebug() : null; - return debug != null ? debug.getCpmoverride() : null; - } - - private ExtImpPrebid extImpPrebid(JsonNode extImpPrebid) { - try { - return mapper.mapper().treeToValue(extImpPrebid, ExtImpPrebid.class); - } catch (JsonProcessingException e) { - throw new PreBidException(String.format("Error decoding imp.ext.prebid: %s", e.getMessage()), e); - } - } + final ExtImpRubiconDebug debug = parseRubiconExt(imp).getBidder().getDebug(); - private RubiconImpExtPrebidBidder extImpPrebidBidder(ObjectNode extImpPrebidBidder) { - try { - return mapper.mapper().treeToValue(extImpPrebidBidder, RubiconImpExtPrebidBidder.class); - } catch (JsonProcessingException e) { - throw new PreBidException(String.format("Error decoding imp.ext.prebid.bidder: %s", e.getMessage()), e); - } + return debug != null ? debug.getCpmoverride() : null; } private static BidType bidType(BidRequest bidRequest) { diff --git a/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidBidder.java deleted file mode 100644 index 9a8aa550c61..00000000000 --- a/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidBidder.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.bidder.rubicon.proto; - -import lombok.Value; - -@Value(staticConstructor = "of") -public class RubiconImpExtPrebidBidder { - - RubiconImpExtPrebidRubiconDebug debug; -} diff --git a/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidRubiconDebug.java b/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidRubiconDebug.java deleted file mode 100644 index 7f3c601f1e0..00000000000 --- a/src/main/java/org/prebid/server/bidder/rubicon/proto/RubiconImpExtPrebidRubiconDebug.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.bidder.rubicon.proto; - -import lombok.Value; - -@Value(staticConstructor = "of") -public class RubiconImpExtPrebidRubiconDebug { - - Float cpmoverride; -} diff --git a/src/main/java/org/prebid/server/bidder/sharethrough/SharethroughRequestUtil.java b/src/main/java/org/prebid/server/bidder/sharethrough/SharethroughRequestUtil.java index 9d814a3670d..5b41dbb9e7a 100644 --- a/src/main/java/org/prebid/server/bidder/sharethrough/SharethroughRequestUtil.java +++ b/src/main/java/org/prebid/server/bidder/sharethrough/SharethroughRequestUtil.java @@ -25,7 +25,7 @@ class SharethroughRequestUtil { private static final int MIN_SAFARI_VERSION = 10; /** - * Retrieves size from imp.ext.sharethrough.iframeSize or from im.banner.format. + * Retrieves size from iframeSize or from imp.banner.format. */ Size getSize(Imp imp, ExtImpSharethrough extImpSharethrough) { final List iframeSize = extImpSharethrough.getIframeSize(); diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubicon.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubicon.java index fc847965f79..c140e163661 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubicon.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubicon.java @@ -34,4 +34,6 @@ public class ExtImpRubicon { String pchain; List keywords; + + ExtImpRubiconDebug debug; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubiconDebug.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubiconDebug.java new file mode 100644 index 00000000000..ce00852bb5f --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/rubicon/ExtImpRubiconDebug.java @@ -0,0 +1,9 @@ +package org.prebid.server.proto.openrtb.ext.request.rubicon; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtImpRubiconDebug { + + Float cpmoverride; +} diff --git a/src/main/java/org/prebid/server/validation/RequestValidator.java b/src/main/java/org/prebid/server/validation/RequestValidator.java index f6bfb83cd11..795577e2265 100644 --- a/src/main/java/org/prebid/server/validation/RequestValidator.java +++ b/src/main/java/org/prebid/server/validation/RequestValidator.java @@ -77,8 +77,10 @@ public class RequestValidator { private static final String PREBID_EXT = "prebid"; - private static final String CONTEXT_EXT = "context"; + private static final String BIDDER_EXT = "bidder"; + private static final Locale LOCALE = Locale.US; + private static final String DOCUMENTATION = "https://iabtechlab.com/wp-content/uploads/2016/07/" + "OpenRTB-Native-Ads-Specification-Final-1.2.pdf"; @@ -518,12 +520,12 @@ private void fillAndValidateNative(Native xNative, int impIndex) throws Validati private Request parseNativeRequest(String rawStringNativeRequest, int impIndex) throws ValidationException { if (StringUtils.isBlank(rawStringNativeRequest)) { - throw new ValidationException("request.imp.[%d].ext.native contains empty request value", impIndex); + throw new ValidationException("request.imp[%d].native contains empty request value", impIndex); } try { return mapper.mapper().readValue(rawStringNativeRequest, Request.class); } catch (IOException e) { - throw new ValidationException("Error while parsing request.imp.[%d].ext.native.request", impIndex); + throw new ValidationException("Error while parsing request.imp[%d].native.request", impIndex); } } @@ -762,17 +764,38 @@ private String toEncodedRequest(Request nativeRequest, List updatedAssets } private void validateImpExt(ObjectNode ext, Map aliases, int impIndex) throws ValidationException { - if (ext == null || ext.size() < 1) { - throw new ValidationException("request.imp[%d].ext must contain at least one bidder", impIndex); + validateImpExtPrebid(childAsObjectNode(ext, PREBID_EXT), aliases, impIndex); + } + + private void validateImpExtPrebid(ObjectNode extPrebid, Map aliases, int impIndex) + throws ValidationException { + + if (extPrebid == null || extPrebid.size() < 1) { + throw new ValidationException( + "request.imp[%d].ext.prebid must be non-empty object", impIndex); } - final Iterator> bidderExtensions = ext.fields(); + validateImpExtPrebidBidder(extPrebid, aliases, impIndex); + } + + private void validateImpExtPrebidBidder(ObjectNode extPrebid, Map aliases, int impIndex) + throws ValidationException { + + final JsonNode extPrebidBidder = extPrebid.get(BIDDER_EXT); + + if (extPrebidBidder == null) { + return; + } + + if (!extPrebidBidder.isObject()) { + throw new ValidationException("request.imp[%d].ext.prebid.bidder must be object", impIndex); + } + + final Iterator> bidderExtensions = extPrebidBidder.fields(); while (bidderExtensions.hasNext()) { final Map.Entry bidderExtension = bidderExtensions.next(); final String bidder = bidderExtension.getKey(); - if (!Objects.equals(bidder, PREBID_EXT) && !Objects.equals(bidder, CONTEXT_EXT)) { - validateImpBidderExtName(impIndex, bidderExtension, aliases.getOrDefault(bidder, bidder)); - } + validateImpBidderExtName(impIndex, bidderExtension, aliases.getOrDefault(bidder, bidder)); } } @@ -781,12 +804,12 @@ private void validateImpBidderExtName(int impIndex, Map.Entry if (bidderCatalog.isValidName(bidderName)) { final Set messages = bidderParamValidator.validate(bidderName, bidderExtension.getValue()); if (!messages.isEmpty()) { - throw new ValidationException("request.imp[%d].ext.%s failed validation.\n%s", impIndex, + throw new ValidationException("request.imp[%d].ext.prebid.bidder.%s failed validation.\n%s", impIndex, bidderName, String.join("\n", messages)); } } else if (!bidderCatalog.isDeprecatedName(bidderName) && !bidderCatalog.isAlias(bidderName)) { throw new ValidationException( - "request.imp[%d].ext contains unknown bidder: %s", impIndex, bidderName); + "request.imp[%d].ext.prebid.bidder contains unknown bidder: %s", impIndex, bidderName); } } @@ -893,6 +916,16 @@ private void validateMetrics(List metrics, int impIndex) throws Validati } } + private ObjectNode childAsObjectNode(ObjectNode parent, String fieldName) { + final JsonNode child = parent != null ? parent.get(fieldName) : null; + + return isObjectNode(child) ? (ObjectNode) child : null; + } + + private static boolean isObjectNode(JsonNode node) { + return node != null && node.isObject(); + } + private static boolean hasPositiveValue(Integer value) { return value != null && value > 0; } diff --git a/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java index edb01f4bf29..719ecf2ca7c 100644 --- a/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.App; import com.iab.openrtb.request.Banner; @@ -556,20 +557,24 @@ public void shouldUpdateImpsWithSecurityOneIfRequestIsSecuredAndImpSecurityNotDe @Test public void shouldNotUpdateImpsWithSecurityOneIfRequestIsSecureAndImpSecurityIsZero() { // given - givenBidRequest(BidRequest.builder().imp(singletonList(Imp.builder().secure(0).build())).build()); + final List imps = singletonList(Imp.builder().secure(0).build()); + + givenBidRequest(BidRequest.builder().imp(imps).build()); + given(paramsExtractor.secureFrom(any())).willReturn(1); // when final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); // then - assertThat(request.getImp()).extracting(Imp::getSecure).containsOnly(0); + assertThat(request.getImp()).isSameAs(imps); } @Test public void shouldUpdateImpsOnlyWithNotDefinedSecurityWithSecurityOneIfRequestIsSecure() { // given - givenBidRequest(BidRequest.builder().imp(asList(Imp.builder().build(), Imp.builder().secure(0).build())) + givenBidRequest(BidRequest.builder() + .imp(asList(Imp.builder().build(), Imp.builder().secure(0).build())) .build()); given(paramsExtractor.secureFrom(any())).willReturn(1); @@ -583,14 +588,105 @@ public void shouldUpdateImpsOnlyWithNotDefinedSecurityWithSecurityOneIfRequestIs @Test public void shouldNotUpdateImpsWithSecurityOneIfRequestIsNotSecureAndImpSecurityIsNotDefined() { // given - givenBidRequest(BidRequest.builder().imp(singletonList(Imp.builder().build())).build()); + final List imps = singletonList(Imp.builder().build()); + + givenBidRequest(BidRequest.builder().imp(imps).build()); + given(paramsExtractor.secureFrom(any())).willReturn(0); // when final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); // then - assertThat(request.getImp()).extracting(Imp::getSecure).containsNull(); + assertThat(request.getImp()).isSameAs(imps); + } + + @Test + public void shouldMoveBidderParametersToImpExtPrebidBidderAndMergeWithExisting() { + // given + final List imps = singletonList( + Imp.builder() + .ext(mapper.createObjectNode() + .set("bidder1", mapper.createObjectNode().put("param1", "value1")) + .set("bidder2", mapper.createObjectNode().put("param2", "value2")) + .set("context", mapper.createObjectNode().put("data", "datavalue")) + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .set("bidder2", mapper.createObjectNode().put("param22", "value22"))) + .set("storedrequest", mapper.createObjectNode().put("id", "storedreq1")))) + .build()); + + givenBidRequest(BidRequest.builder().imp(imps).build()); + + // when + final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); + + // then + assertThat(request.getImp()).containsOnly( + Imp.builder() + .ext(mapper.createObjectNode() + .set("context", mapper.createObjectNode().put("data", "datavalue")) + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .set( + "bidder1", mapper.createObjectNode().put("param1", "value1")) + .set( + "bidder2", mapper.createObjectNode() + .put("param2", "value2") + .put("param22", "value22"))) + .set("storedrequest", mapper.createObjectNode().put("id", "storedreq1")))) + .build()); + } + + @Test + public void shouldMoveBidderParametersToImpExtPrebidBidderWhenImpExtPrebidAbsent() { + // given + final List imps = singletonList( + Imp.builder() + .ext(mapper.createObjectNode() + .set("bidder1", mapper.createObjectNode().put("param1", "value1")) + .set("bidder2", mapper.createObjectNode().put("param2", "value2"))) + .build()); + + givenBidRequest(BidRequest.builder().imp(imps).build()); + + // when + final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); + + // then + assertThat(request.getImp()).containsOnly( + Imp.builder() + .ext(mapper.createObjectNode() + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .set( + "bidder1", mapper.createObjectNode().put("param1", "value1")) + .set( + "bidder2", mapper.createObjectNode().put("param2", "value2"))))) + .build()); + } + + @Test + public void shouldNotChangeImpExtWhenBidderParametersAreAtImpExtPrebidBidderOnly() { + // given + final List imps = singletonList( + Imp.builder() + .ext(mapper.createObjectNode() + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .set( + "bidder1", mapper.createObjectNode().put("param1", "value1")) + .set( + "bidder2", mapper.createObjectNode().put("param2", "value2"))))) + .build()); + + givenBidRequest(BidRequest.builder().imp(imps).build()); + + // when + final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); + + // then + assertThat(request.getImp()).isSameAs(imps); } @Test diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 2e6f753899f..a0a9c836758 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -295,7 +295,7 @@ public void shouldExtractRequestWithBidderSpecificExtension() { givenBidder(givenEmptySeatBid()); final BidRequest bidRequest = givenBidRequest(singletonList( - givenImp(doubleMap("prebid", 0, "someBidder", 1), builder -> builder + givenImp(singletonMap("someBidder", 1), builder -> builder .id("impId") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) @@ -315,7 +315,7 @@ public void shouldExtractRequestWithBidderSpecificExtension() { .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) .build()) - .ext(mapper.valueToTree(ExtPrebid.of(0, 1))) + .ext(mapper.valueToTree(ExtPrebid.of(null, 1))) .build())) .tmax(500L) .build()); @@ -331,7 +331,7 @@ public void shouldExtractRequestWithCurrencyRatesExtension() { "UAH", singletonMap("EUR", BigDecimal.valueOf(1.1565))); final BidRequest bidRequest = givenBidRequest(singletonList( - givenImp(doubleMap("prebid", 0, "someBidder", 1), builder -> builder + givenImp(singletonMap("someBidder", 1), builder -> builder .id("impId") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) @@ -357,7 +357,7 @@ public void shouldExtractRequestWithCurrencyRatesExtension() { .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) .build()) - .ext(mapper.valueToTree(ExtPrebid.of(0, 1))) + .ext(mapper.valueToTree(ExtPrebid.of(null, 1))) .build())) .ext(ExtRequest.of( ExtRequestPrebid.builder().currency(ExtRequestCurrency.of(currencyRates, false)).build())) @@ -395,43 +395,6 @@ public void shouldExtractMultipleRequests() { .element(0).returns(2, imp -> imp.getExt().get("bidder").asInt()); } - @Test - public void shouldPassImpWithExtPrebidToDefinedBidder() { - // given - final String bidder1Name = "bidder1"; - final String bidder2Name = "bidder2"; - final Bidder bidder1 = mock(Bidder.class); - final Bidder bidder2 = mock(Bidder.class); - givenBidder(bidder1Name, bidder1, givenEmptySeatBid()); - givenBidder(bidder2Name, bidder2, givenEmptySeatBid()); - - final ObjectNode impExt = mapper.createObjectNode() - .put(bidder1Name, "ignored1") - .put(bidder2Name, "ignored2") - .putPOJO("prebid", doubleMap(bidder1Name, mapper.createObjectNode().put("somefield", "bidder1"), - bidder2Name, mapper.createObjectNode().put("somefield", "bidder2"))); - - final BidRequest bidRequest = givenBidRequest(singletonList(givenImp(impExt, identity())), identity()); - - // when - exchangeService.holdAuction(givenRequestContext(bidRequest)); - - // then - final ArgumentCaptor bidRequest1Captor = ArgumentCaptor.forClass(BidRequest.class); - verify(httpBidderRequester).requestBids(same(bidder1), bidRequest1Captor.capture(), any(), anyBoolean()); - assertThat(bidRequest1Captor.getValue().getImp()).hasSize(1) - .extracting(imp -> imp.getExt().get("prebid")) - .containsOnly(mapper.createObjectNode().set("bidder", - mapper.createObjectNode().put("somefield", "bidder1"))); - - final ArgumentCaptor bidRequest2Captor = ArgumentCaptor.forClass(BidRequest.class); - verify(httpBidderRequester).requestBids(same(bidder2), bidRequest2Captor.capture(), any(), anyBoolean()); - assertThat(bidRequest2Captor.getValue().getImp()).hasSize(1) - .extracting(imp -> imp.getExt().get("prebid")) - .containsOnly(mapper.createObjectNode().set("bidder", - mapper.createObjectNode().put("somefield", "bidder2"))); - } - @Test public void shouldPassRequestWithExtPrebidToDefinedBidder() { // given @@ -668,10 +631,14 @@ public void shouldTolerateBidderResultWithoutBids() { } @Test - public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderWereUsedWithingSingleImp() { + public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderWereUsedWithinSingleImp() { // given given(httpBidderRequester.requestBids(any(), - eq(givenBidRequest(givenSingleImp(mapper.valueToTree(ExtPrebid.of(null, 1))), + eq(givenBidRequest( + singletonList(givenImp( + null, + builder -> builder.ext(mapper.valueToTree( + ExtPrebid.of(null, 1))))), builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .auctiontimestamp(1000L) .aliases(singletonMap("bidderAlias", "bidder")).build())))), any(), anyBoolean())) @@ -679,7 +646,11 @@ public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderW givenBid(Bid.builder().price(BigDecimal.ONE).build()))))); given(httpBidderRequester.requestBids(any(), - eq(givenBidRequest(givenSingleImp(mapper.valueToTree(ExtPrebid.of(null, 2))), + eq(givenBidRequest( + singletonList(givenImp( + null, + builder -> builder.ext(mapper.valueToTree( + ExtPrebid.of(null, 2))))), builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .auctiontimestamp(1000L) .aliases(singletonMap("bidderAlias", "bidder")).build())))), any(), anyBoolean())) @@ -1034,12 +1005,12 @@ public void shouldCreateRequestsFromImpsReturnedByStoredResponseProcessor() { givenBidder(givenEmptySeatBid()); final BidRequest bidRequest = givenBidRequest(asList( - givenImp(doubleMap("prebid", 0, "someBidder1", 1), builder -> builder + givenImp(singletonMap("someBidder1", 1), builder -> builder .id("impId1") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) .build())), - givenImp(doubleMap("prebid", 0, "someBidder2", 1), builder -> builder + givenImp(singletonMap("someBidder2", 1), builder -> builder .id("impId2") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) @@ -1048,7 +1019,7 @@ public void shouldCreateRequestsFromImpsReturnedByStoredResponseProcessor() { given(storedResponseProcessor.getStoredResponseResult(any(), any(), any())) .willReturn(Future.succeededFuture(StoredResponseResult - .of(singletonList(givenImp(doubleMap("prebid", 0, "someBidder1", 1), builder -> builder + .of(singletonList(givenImp(singletonMap("someBidder1", 1), builder -> builder .id("impId1") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) @@ -1067,7 +1038,7 @@ public void shouldCreateRequestsFromImpsReturnedByStoredResponseProcessor() { .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) .build()) - .ext(mapper.valueToTree(ExtPrebid.of(0, 1))) + .ext(mapper.valueToTree(ExtPrebid.of(null, 1))) .build())) .tmax(500L) .build()); @@ -1226,13 +1197,20 @@ public void shouldNotChangeGdprFromRequestWhenDeviceLmtIsOne() { @Test public void shouldCleanImpExtContextDataWhenFirstPartyDataNotPermittedForBidder() { // given - final ObjectNode impExt = mapper.createObjectNode().put("someBidder", 1).set("context", - mapper.createObjectNode().put("data", "data").put("otherField", "value")); + final ObjectNode impExt = mapper.createObjectNode() + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .put("someBidder", 1))) + .set("context", mapper.createObjectNode() + .put("data", "data") + .put("otherField", "value")); final BidRequest bidRequest = givenBidRequest(singletonList(Imp.builder() .id("impId") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) - .build()).ext(impExt).build()), + .build()) + .ext(impExt) + .build()), builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(singletonList("otherBidder"))) .build()))); @@ -1251,13 +1229,21 @@ public void shouldCleanImpExtContextDataWhenFirstPartyDataNotPermittedForBidder( @Test public void shouldDeepCopyImpExtContextToEachImpressionAndNotRemoveDataForAllWhenDeprecatedOnlyOneBidder() { // given - final ObjectNode impExt = mapper.createObjectNode().put("someBidder", 1).put("deprecatedBidder", 2) - .set("context", mapper.createObjectNode().put("data", "data").put("otherField", "value")); + final ObjectNode impExt = mapper.createObjectNode() + .set("prebid", mapper.createObjectNode() + .set("bidder", mapper.createObjectNode() + .put("someBidder", 1) + .put("deprecatedBidder", 2))) + .set("context", mapper.createObjectNode() + .put("data", "data") + .put("otherField", "value")); final BidRequest bidRequest = givenBidRequest(singletonList(Imp.builder() .id("impId") .banner(Banner.builder() .format(singletonList(Format.builder().w(400).h(300).build())) - .build()).ext(impExt).build()), + .build()) + .ext(impExt) + .build()), builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(singletonList("someBidder"))) .build()))); @@ -2345,7 +2331,8 @@ private static BidRequest givenBidRequest(List imp) { private static Imp givenImp(T ext, Function impBuilderCustomizer) { return impBuilderCustomizer.apply(Imp.builder() - .ext(ext != null ? mapper.valueToTree(ext) : null)) + .ext(mapper.valueToTree(singletonMap( + "prebid", ext != null ? singletonMap("bidder", ext) : emptyMap())))) .build(); } diff --git a/src/test/java/org/prebid/server/auction/StoredResponseProcessorTest.java b/src/test/java/org/prebid/server/auction/StoredResponseProcessorTest.java index 6cfee994e73..c38d0888381 100644 --- a/src/test/java/org/prebid/server/auction/StoredResponseProcessorTest.java +++ b/src/test/java/org/prebid/server/auction/StoredResponseProcessorTest.java @@ -77,11 +77,11 @@ public void setUp() { @Test public void getStoredResponseResultShouldReturnSeatBidsForAuctionResponseId() throws JsonProcessingException { // given - final List imps = singletonList(Imp.builder().id("impId") - .ext(mapper.valueToTree( - ExtImp.of( - ExtImpPrebid.builder().storedAuctionResponse(ExtStoredAuctionResponse.of("1")).build(), - null))) + final List imps = singletonList(Imp.builder() + .id("impId") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder().storedAuctionResponse(ExtStoredAuctionResponse.of("1")).build(), + null))) .build()); given(applicationSettings.getStoredResponses(any(), any())) @@ -95,26 +95,32 @@ public void getStoredResponseResultShouldReturnSeatBidsForAuctionResponseId() th aliases, timeout); // then - assertThat(result.result()).isEqualTo(StoredResponseResult.of(emptyList(), - singletonList(SeatBid.builder().seat("rubicon") - .bid(singletonList(Bid.builder().id("id").impid("impId").build())).build()))); + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + emptyList(), + singletonList(SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id").impid("impId").build())) + .build()))); } @Test public void getStoredResponseResultShouldNotChangeImpsAndReturnSeatBidsWhenThereAreNoStoredIds() { // given - final List imps = singletonList(Imp.builder() - .ext(mapper.createObjectNode().put("rubicon", 1)) - .build()); + final Imp imp = Imp.builder() + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder().bidder(mapper.createObjectNode().put("rubicon", 1)).build(), + null))) + .build(); + given(bidderCatalog.isValidName(any())).willReturn(true); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(singletonList(imp), aliases, timeout); // then assertThat(result.result()).isEqualTo(StoredResponseResult.of( - singletonList(Imp.builder().ext(mapper.createObjectNode().put("rubicon", 1)).build()), + singletonList(imp), emptyList())); verifyZeroInteractions(applicationSettings); } @@ -122,22 +128,24 @@ public void getStoredResponseResultShouldNotChangeImpsAndReturnSeatBidsWhenThere @Test public void getStoredResponseResultShouldAddImpToRequiredRequestWhenItsStoredBidResponseIsEmpty() { // given - final List imps = singletonList(Imp.builder().id("impId1") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(emptyList()) - .build(), null))) + final List imps = singletonList(Imp.builder() + .id("impId1") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder().storedBidResponse(emptyList()).build(), + null))) .build()); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.result()).isEqualTo(StoredResponseResult.of( - singletonList(Imp.builder().id("impId1") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(emptyList()) - .build(), null))) + singletonList(Imp.builder() + .id("impId1") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder().storedBidResponse(emptyList()).build(), + null))) .build()), emptyList())); verifyZeroInteractions(applicationSettings); @@ -156,8 +164,8 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenErrorHappenedDuri .willReturn(Future.failedFuture(new PreBidException("Failed."))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -169,9 +177,11 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenErrorHappenedDuri @Test public void getStoredResponseResultShouldReturnSeatBidsForBidStoredResponseId() throws JsonProcessingException { // given - final List imps = singletonList(Imp.builder().id("impId1") + final List imps = singletonList(Imp.builder() + .id("impId1") .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(asList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"), + .storedBidResponse(asList( + ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"), ExtStoredBidResponse.of("appnexus", "storedBidResponseId2"))) .build(), null))) @@ -179,39 +189,49 @@ public void getStoredResponseResultShouldReturnSeatBidsForBidStoredResponseId() final Map storedResponse = new HashMap<>(); storedResponse.put("storedBidResponseId1", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id1").build())) - .build()))); + SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id1").build())).build()))); storedResponse.put("storedBidResponseId2", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id2").build())) - .build()))); + SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id2").build())).build()))); given(applicationSettings.getStoredResponses(any(), any())).willReturn( Future.succeededFuture(StoredResponseDataResult.of(storedResponse, emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then - assertThat(result.result()).isEqualTo(StoredResponseResult.of(emptyList(), + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + emptyList(), asList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id2").impid("impId1") - .build())).build(), - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id1").impid("impId1") - .build())).build()))); + SeatBid.builder() + .seat("appnexus") + .bid(singletonList(Bid.builder().id("id2").impid("impId1").build())) + .build(), + SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id1").impid("impId1").build())) + .build()))); } @Test public void getStoredResponseResultShouldReturnSeatBidsForBidAndAuctionStoredResponseId() throws JsonProcessingException { + // given final List imps = asList( - Imp.builder().id("impId1") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedAuctionResponse(ExtStoredAuctionResponse.of("storedAuctionRequest")) - .build(), null))).build(), - Imp.builder().id("impId2") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() + Imp.builder() + .id("impId1") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedAuctionResponse(ExtStoredAuctionResponse.of("storedAuctionRequest")) + .build(), + null))) + .build(), + Imp.builder() + .id("impId2") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() .storedBidResponse(singletonList( ExtStoredBidResponse.of("rubicon", "storedBidRequest"))) .build(), @@ -220,114 +240,143 @@ public void getStoredResponseResultShouldReturnSeatBidsForBidAndAuctionStoredRes final Map storedResponse = new HashMap<>(); storedResponse.put("storedAuctionRequest", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").build())) - .build()))); + SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").build())).build()))); storedResponse.put("storedBidRequest", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id2").build())) - .build()))); + SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id2").build())).build()))); given(applicationSettings.getStoredResponses(any(), any())).willReturn( Future.succeededFuture(StoredResponseDataResult.of(storedResponse, emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then - assertThat(result.result()).isEqualTo(StoredResponseResult.of(emptyList(), + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + emptyList(), asList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").impid("impId1") - .build())).build(), - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id2").impid("impId2") - .build())).build()))); + SeatBid.builder() + .seat("appnexus") + .bid(singletonList(Bid.builder().id("id1").impid("impId1").build())) + .build(), + SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id2").impid("impId2").build())) + .build()))); } @Test public void getStoredResponseResultShouldRemoveMockedBiddersFromImps() throws JsonProcessingException { - final ObjectNode impExt = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) - .build(), null)); - impExt.put("rubicon", 1); - impExt.put("appnexus", 2); + final ObjectNode impExt = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) + .bidder(mapper.createObjectNode() + .put("rubicon", 1) + .put("appnexus", 2)) + .build(), + null)); given(bidderCatalog.isValidName(any())).willReturn(true); - final List imps = singletonList(Imp.builder().id("impId1").ext(impExt).build()); + final List imps = singletonList(Imp.builder() + .id("impId1") + .ext(impExt).build()); final Map storedResponse = new HashMap<>(); storedResponse.put("storedBidResponseId1", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id1").build())) + SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id1").build())) .build()))); given(applicationSettings.getStoredResponses(any(), any())).willReturn( Future.succeededFuture(StoredResponseDataResult.of(storedResponse, emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then - final ObjectNode impExtResult = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) - .build(), null)); - impExtResult.put("appnexus", 2); - - assertThat(result.result()).isEqualTo(StoredResponseResult.of(singletonList(Imp.builder().id("impId1") - .ext(impExtResult).build()), - singletonList(SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().impid("impId1") - .id("id1").build())).build()))); + final ObjectNode impExtResult = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) + .bidder(mapper.createObjectNode() + .put("appnexus", 2)) + .build(), + null)); + + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + singletonList(Imp.builder().id("impId1").ext(impExtResult).build()), + singletonList(SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().impid("impId1").id("id1").build())) + .build()))); } @Test public void getStoredResponseResultShouldMergeStoredSeatBidsForTheSameBidder() throws JsonProcessingException { // given final List imps = asList( - Imp.builder().id("impId1") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() + Imp.builder() + .id("impId1") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() .storedAuctionResponse(ExtStoredAuctionResponse.of("storedAuctionRequest")) .build(), - null))).build(), - Imp.builder().id("impId2") - .ext(mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse( - singletonList( + null))) + .build(), + Imp.builder() + .id("impId2") + .ext(mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList( ExtStoredBidResponse.of("rubicon", "storedBidRequest"))) - .build(), null))) + .build(), + null))) .build()); final Map storedResponse = new HashMap<>(); storedResponse.put("storedAuctionRequest", mapper.writeValueAsString(asList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").build())) - .build(), SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id3").build())) - .build()))); + SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").build())).build(), + SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id3").build())).build()))); storedResponse.put("storedBidRequest", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id2").build())) - .build()))); + SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id2").build())).build()))); given(applicationSettings.getStoredResponses(any(), any())).willReturn( Future.succeededFuture(StoredResponseDataResult.of(storedResponse, emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then - assertThat(result.result()).isEqualTo(StoredResponseResult.of(emptyList(), + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + emptyList(), asList( - SeatBid.builder().seat("appnexus").bid(singletonList(Bid.builder().id("id1").impid("impId1") - .build())).build(), - SeatBid.builder().seat("rubicon").bid(asList(Bid.builder().id("id3").impid("impId1").build(), - Bid.builder().id("id2").impid("impId2").build())).build()))); + SeatBid.builder() + .seat("appnexus") + .bid(singletonList(Bid.builder().id("id1").impid("impId1").build())) + .build(), + SeatBid.builder() + .seat("rubicon") + .bid(asList( + Bid.builder().id("id3").impid("impId1").build(), + Bid.builder().id("id2").impid("impId2").build())) + .build()))); } @Test public void getStoredResponseResultShouldSupportAliasesWhenDecidingIfImpRequiredRequestToExchange() throws JsonProcessingException { - final ObjectNode impExt = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) - .build(), null)); - impExt.put("rubicon", 1); - impExt.put("appnexusAlias", 2); + + final ObjectNode impExt = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) + .bidder(mapper.createObjectNode() + .put("rubicon", 1) + .put("appnexusAlias", 2)) + .build(), + null)); given(bidderCatalog.isValidName(any())).willReturn(false); @@ -335,7 +384,9 @@ public void getStoredResponseResultShouldSupportAliasesWhenDecidingIfImpRequired final Map storedResponse = new HashMap<>(); storedResponse.put("storedBidResponseId1", mapper.writeValueAsString(singletonList( - SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder().id("id1").build())) + SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id1").build())) .build()))); given(applicationSettings.getStoredResponses(any(), any())).willReturn( @@ -343,33 +394,39 @@ public void getStoredResponseResultShouldSupportAliasesWhenDecidingIfImpRequired given(aliases.isAliasDefined(eq("appnexusAlias"))).willReturn(true); given(aliases.resolveBidder(eq("appnexusAlias"))).willReturn("appnexus"); - given(aliases.resolveAliasVendorId(eq("appnexusAlias"))).willReturn(1); // when final Future result = storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then - final ObjectNode impExtResult = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) - .build(), null)); - impExtResult.put("appnexusAlias", 2); - - assertThat(result.result()).isEqualTo(StoredResponseResult.of(singletonList(Imp.builder().ext(impExtResult) - .id("impId1").build()), - singletonList(SeatBid.builder().seat("rubicon").bid(singletonList(Bid.builder() - .id("id1").impid("impId1").build())).build()))); + final ObjectNode impExtResult = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", "storedBidResponseId1"))) + .bidder(mapper.createObjectNode() + .put("appnexusAlias", 2)) + .build(), + null)); + + assertThat(result.result()).isEqualTo(StoredResponseResult.of( + singletonList(Imp.builder().ext(impExtResult).id("impId1").build()), + singletonList(SeatBid.builder() + .seat("rubicon") + .bid(singletonList(Bid.builder().id("id1").impid("impId1").build())) + .build()))); } @Test public void getStoredResponseResultShouldReturnFailedFutureWhenImpExtIsNotValid() { // given - final List imps = singletonList(Imp.builder().id("impId").ext(mapper.createObjectNode() - .put("prebid", 5)).build()); + final List imps = singletonList(Imp.builder() + .id("impId") + .ext(mapper.createObjectNode().put("prebid", 5)) + .build()); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -380,14 +437,16 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenImpExtIsNotValid( @Test public void getStoredResponseResultShouldReturnFailedFutureWhenBidderIsMissedInStoredBidResponse() { // given - final ObjectNode impExt = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of(null, "storedBidResponseId1"))) - .build(), null)); + final ObjectNode impExt = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of(null, "storedBidResponseId1"))) + .build(), + null)); final List imps = singletonList(Imp.builder().id("impId").ext(impExt).build()); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -398,13 +457,16 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenBidderIsMissedInS @Test public void getStoredResponseResultShouldReturnFailedFutureWhenIdIsMissedInStoredBidResponse() { // given - final ObjectNode impExt = mapper.valueToTree(ExtImp.of(ExtImpPrebid.builder() - .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", null))).build(), null)); + final ObjectNode impExt = mapper.valueToTree(ExtImp.of( + ExtImpPrebid.builder() + .storedBidResponse(singletonList(ExtStoredBidResponse.of("rubicon", null))) + .build(), + null)); final List imps = singletonList(Imp.builder().ext(impExt).id("impId").build()); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -415,6 +477,7 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenIdIsMissedInStore @Test public void getStoredResponseResultShouldReturnFailedFutureWhenSeatIsEmptyInStoredSeatBid() throws JsonProcessingException { + // given final List imps = singletonList(Imp.builder() .ext(mapper.valueToTree(ExtImp.of( @@ -423,14 +486,17 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenSeatIsEmptyInStor .build()); given(applicationSettings.getStoredResponses(any(), any())) - .willReturn(Future.succeededFuture(StoredResponseDataResult.of(singletonMap("responseId", - mapper.writeValueAsString(singletonList(SeatBid.builder().bid(singletonList( - Bid.builder().id("id").build())).build()))), + .willReturn(Future.succeededFuture(StoredResponseDataResult.of( + singletonMap( + "responseId", + mapper.writeValueAsString(singletonList(SeatBid.builder() + .bid(singletonList(Bid.builder().id("id").build())) + .build()))), emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -441,6 +507,7 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenSeatIsEmptyInStor @Test public void getStoredResponseResultShouldReturnFailedFutureWhenBidsAreEmptyInStoredSeatBid() throws JsonProcessingException { + // given final List imps = singletonList(Imp.builder() .ext(mapper.valueToTree(ExtImp.of( @@ -449,15 +516,17 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenBidsAreEmptyInSto .build()); given(applicationSettings.getStoredResponses(any(), any())) - .willReturn(Future.succeededFuture(StoredResponseDataResult.of(singletonMap("responseId", - mapper.writeValueAsString(singletonList(SeatBid.builder() - .seat("seat") - .build()))), + .willReturn(Future.succeededFuture(StoredResponseDataResult.of( + singletonMap( + "responseId", + mapper.writeValueAsString(singletonList(SeatBid.builder() + .seat("seat") + .build()))), emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -466,20 +535,20 @@ public void getStoredResponseResultShouldReturnFailedFutureWhenBidsAreEmptyInSto } @Test - public void getStoredResponseResultShouldReturnFailedFutureSeatBidsCantBeParsed() { + public void getStoredResponseResultShouldReturnFailedFutureSeatBidsCannotBeParsed() { // given final List imps = singletonList(Imp.builder().id("impId") .ext(mapper.valueToTree(ExtImp.of( ExtImpPrebid.builder().storedAuctionResponse(ExtStoredAuctionResponse.of("1")).build(), - null))).build()); + null))) + .build()); - given(applicationSettings.getStoredResponses(any(), any())) - .willReturn(Future.succeededFuture(StoredResponseDataResult.of( - singletonMap("1", "{invalid"), emptyList()))); + given(applicationSettings.getStoredResponses(any(), any())).willReturn(Future.succeededFuture( + StoredResponseDataResult.of(singletonMap("1", "{invalid"), emptyList()))); // when - final Future result = storedResponseProcessor.getStoredResponseResult(imps, - aliases, timeout); + final Future result = + storedResponseProcessor.getStoredResponseResult(imps, aliases, timeout); // then assertThat(result.failed()).isTrue(); @@ -490,121 +559,210 @@ public void getStoredResponseResultShouldReturnFailedFutureSeatBidsCantBeParsed( @Test public void mergeWithBidderResponsesShouldReturnMergedStoredSeatWithResponse() { // given - final List bidderResponses = singletonList(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + final List bidderResponses = singletonList(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), + emptyList(), + emptyList()), + 100)); final List seatBid = singletonList(SeatBid.builder() - .seat("rubicon").bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())).build()); + .seat("rubicon") + .bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())) + .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, - imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, imps); // then - assertThat(result).contains(BidderResponse.of("rubicon", BidderSeatBid.of( - asList(BidderBid.of(Bid.builder().id("bid2").impid("storedImp").build(), BidType.banner, "USD"), - BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + assertThat(result).contains(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + asList( + BidderBid.of( + Bid.builder() + .id("bid2") + .impid("storedImp") + .build(), + BidType.banner, + "USD"), + BidderBid.of( + Bid.builder() + .id("bid1") + .build(), + BidType.banner, + "USD")), + emptyList(), + emptyList()), + 100)); } @Test public void mergeWithBidderResponsesShouldMergeBidderResponsesWithoutCorrespondingStoredSeatBid() { // given - final List bidderResponses = singletonList(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + final List bidderResponses = singletonList(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), + emptyList(), + emptyList()), + 100)); final List seatBid = singletonList(SeatBid.builder() - .seat("appnexus").bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())).build()); + .seat("appnexus") + .bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())) + .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, - imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, imps); // then assertThat(result).contains( - BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), - emptyList(), - emptyList()), 100), - BidderResponse.of("appnexus", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid2").impid("storedImp").build(), - BidType.banner, "USD")), emptyList(), emptyList()), 0)); + BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of( + Bid.builder().id("bid1").build(), + BidType.banner, + "USD")), + emptyList(), + emptyList()), + 100), + BidderResponse.of( + "appnexus", + BidderSeatBid.of( + singletonList(BidderBid.of( + Bid.builder().id("bid2").impid("storedImp").build(), + BidType.banner, + "USD")), + emptyList(), + emptyList()), + 0)); } @Test public void mergeWithBidderResponsesShouldMergeStoredSeatBidsWithoutBidderResponses() { // given final List seatBid = singletonList(SeatBid.builder() - .seat("rubicon").bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())).build()); + .seat("rubicon") + .bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())) + .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(emptyList(), seatBid, - imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(emptyList(), seatBid, imps); // then - assertThat(result).contains(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid2").impid("storedImp").build(), BidType.banner, "USD")), - emptyList(), emptyList()), 0)); + assertThat(result).contains(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of( + Bid.builder().id("bid2").impid("storedImp").build(), + BidType.banner, + "USD")), + emptyList(), + emptyList()), + 0)); } @Test public void mergeWithBidderResponsesShouldResolveCurrencyFromBidderResponse() { // given - final List bidderResponses = singletonList(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "EUR")), emptyList(), - emptyList()), 100)); + final List bidderResponses = singletonList(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "EUR")), + emptyList(), + emptyList()), + 100)); final List seatBid = singletonList(SeatBid.builder() - .seat("rubicon").bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())).build()); + .seat("rubicon") + .bid(singletonList(Bid.builder().id("bid2").impid("storedImp").build())) + .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, - imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, imps); // then - assertThat(result).contains(BidderResponse.of("rubicon", BidderSeatBid.of( - asList(BidderBid.of(Bid.builder().id("bid2").impid("storedImp").build(), BidType.banner, "EUR"), - BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "EUR")), emptyList(), - emptyList()), 100)); + assertThat(result).contains(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + asList( + BidderBid.of( + Bid.builder().id("bid2").impid("storedImp").build(), + BidType.banner, + "EUR"), + BidderBid.of( + Bid.builder().id("bid1").build(), + BidType.banner, + "EUR")), + emptyList(), + emptyList()), + 100)); } @Test public void mergeWithBidderResponsesShouldResolveBidTypeFromStoredBidExt() { // given - final List bidderResponses = singletonList(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + final List bidderResponses = singletonList(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), + emptyList(), + emptyList()), + 100)); final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder().type(BidType.video).build(); final List seatBid = singletonList(SeatBid.builder() - .seat("rubicon").bid(singletonList(Bid.builder().ext(mapper.createObjectNode() - .set("prebid", mapper.valueToTree(extBidPrebid))).id("bid2").impid("storedImp").build())) + .seat("rubicon") + .bid(singletonList(Bid.builder() + .id("bid2") + .impid("storedImp") + .ext(mapper.createObjectNode().set("prebid", mapper.valueToTree(extBidPrebid))) + .build())) .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, - imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(bidderResponses, seatBid, imps); // then - assertThat(result).contains(BidderResponse.of("rubicon", BidderSeatBid.of( - asList(BidderBid.of( - Bid.builder().id("bid2").impid("storedImp").ext(mapper.createObjectNode() - .set("prebid", mapper.valueToTree(extBidPrebid))).build(), BidType.video, "USD"), - BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + assertThat(result).contains(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + asList(BidderBid.of( + Bid.builder() + .id("bid2") + .impid("storedImp") + .ext(mapper.createObjectNode().set("prebid", mapper.valueToTree(extBidPrebid))) + .build(), + BidType.video, + "USD"), + BidderBid.of( + Bid.builder() + .id("bid1") + .build(), + BidType.banner, + "USD")), + emptyList(), + emptyList()), + 100)); } @Test @@ -613,8 +771,13 @@ public void mergeWithBidderResponsesShouldThrowPrebidExceptionWhenExtBidPrebidIn final ObjectNode extBidPrebid = mapper.createObjectNode().put("type", "invalid"); final List seatBid = singletonList(SeatBid.builder() - .seat("rubicon").bid(singletonList(Bid.builder().ext(mapper.createObjectNode() - .set("prebid", extBidPrebid)).id("bid2").impid("storedImp").build())).build()); + .seat("rubicon") + .bid(singletonList(Bid.builder() + .id("bid2") + .impid("storedImp") + .ext(mapper.createObjectNode().set("prebid", extBidPrebid)) + .build())) + .build()); final List imps = singletonList(Imp.builder().id("storedImp").banner(Banner.builder().build()).build()); @@ -626,19 +789,27 @@ public void mergeWithBidderResponsesShouldThrowPrebidExceptionWhenExtBidPrebidIn @Test public void mergeWithBidderResponsesShouldReturnSameResponseWhenThereAreNoStoredResponses() { // given - final List bidderResponses = singletonList(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + final List bidderResponses = singletonList(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), + emptyList(), + emptyList()), + 100)); final List imps = singletonList(Imp.builder().banner(Banner.builder().build()).build()); // when - final List result = storedResponseProcessor.mergeWithBidderResponses(bidderResponses, - emptyList(), imps); + final List result = + storedResponseProcessor.mergeWithBidderResponses(bidderResponses, emptyList(), imps); // then - assertThat(result).containsOnly(BidderResponse.of("rubicon", BidderSeatBid.of( - singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), emptyList(), - emptyList()), 100)); + assertThat(result).containsOnly(BidderResponse.of( + "rubicon", + BidderSeatBid.of( + singletonList(BidderBid.of(Bid.builder().id("bid1").build(), BidType.banner, "USD")), + emptyList(), + emptyList()), + 100)); } } diff --git a/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java b/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java index 2f5aad3ea3a..9fbc71e9990 100644 --- a/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java @@ -63,7 +63,7 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // then assertThat(result.getErrors()) - .containsExactly(BidderError.badInput("$.imp.ext.adoppler.adunit required")); + .containsExactly(BidderError.badInput("adunit parameter is required for adoppler bidder")); } @Test diff --git a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java index 5d8b50ae993..4896ac6f8a5 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -40,8 +40,6 @@ import org.prebid.server.bidder.rubicon.proto.RubiconBannerExt; import org.prebid.server.bidder.rubicon.proto.RubiconBannerExtRp; import org.prebid.server.bidder.rubicon.proto.RubiconImpExt; -import org.prebid.server.bidder.rubicon.proto.RubiconImpExtPrebidBidder; -import org.prebid.server.bidder.rubicon.proto.RubiconImpExtPrebidRubiconDebug; import org.prebid.server.bidder.rubicon.proto.RubiconImpExtRp; import org.prebid.server.bidder.rubicon.proto.RubiconImpExtRpTrack; import org.prebid.server.bidder.rubicon.proto.RubiconPubExt; @@ -58,7 +56,6 @@ import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.ExtPrebidBidders; import org.prebid.server.proto.openrtb.ext.request.ExtApp; -import org.prebid.server.proto.openrtb.ext.request.ExtImp; import org.prebid.server.proto.openrtb.ext.request.ExtImpContext; import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtPublisher; @@ -72,6 +69,7 @@ import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUidExt; import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtImpRubicon; import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtImpRubicon.ExtImpRubiconBuilder; +import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtImpRubiconDebug; import org.prebid.server.proto.openrtb.ext.request.rubicon.ExtUserTpIdRubicon; import org.prebid.server.proto.openrtb.ext.request.rubicon.RubiconVideoParams; import org.prebid.server.util.HttpUtil; @@ -2356,10 +2354,11 @@ public void makeBidsShouldReturnBidWithOverriddenCpmFromImp() throws JsonProcess mapper.createObjectNode().put("cpmoverride", 5.55))))) // will be ignored .build()); - final ExtImp extImp = ExtImp.of(ExtImpPrebid.builder() - .bidder(mapper.valueToTree( - RubiconImpExtPrebidBidder.of(RubiconImpExtPrebidRubiconDebug.of(4.44f)))) - .build(), null); + final ExtPrebid extImp = ExtPrebid.of( + null, + ExtImpRubicon.builder() + .debug(ExtImpRubiconDebug.of(4.44f)) + .build()); // when final Result> result = rubiconBidder.makeBids(httpCall, givenBidRequest( diff --git a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java index 6b9544862ba..9a7478ffaeb 100644 --- a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java @@ -278,7 +278,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasNullFormatAndNoSiz .banner(Banner.builder() .format(null) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -302,7 +303,8 @@ public void validateShouldReturnEmptyValidationMessagesWhenBannerHasNullFormatAn .h(250) .format(null) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -322,7 +324,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndNoSi .banner(Banner.builder() .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -345,7 +348,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndNoHe .w(300) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -368,7 +372,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndNoWi .h(600) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -392,7 +397,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndZero .h(0) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -416,7 +422,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasZeroHeight() { .h(0) .format(singletonList(Format.builder().build())) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -439,7 +446,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndZero .w(0) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -463,7 +471,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasZeroWidth() { .w(0) .format(singletonList(Format.builder().build())) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -486,7 +495,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndNega .w(-300) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -510,7 +520,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasNegativeWidth() { .w(-300) .format(singletonList(Format.builder().build())) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -533,7 +544,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasEmptyFormatAndNega .w(600) .format(emptyList()) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -557,7 +569,8 @@ public void validateShouldReturnValidationMessageWhenBannerHasNegativeHeight() { .w(600) .format(singletonList(Format.builder().build())) .build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))) + .ext(mapper.valueToTree( + singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))) .build())) .build(); @@ -1150,7 +1163,7 @@ public void validateShouldReturnEmptyValidationMessagesWhenBidRequestIsOk() { } @Test - public void validateShouldReturnValidationMessageWhenNoImpExtBiddersPresent() { + public void validateShouldReturnValidationMessageWhenNoImpExtPrebidPresent() { // given final BidRequest bidRequest = validBidRequestBuilder() .imp(singletonList(validImpBuilder().ext(null).build())) @@ -1161,29 +1174,30 @@ public void validateShouldReturnValidationMessageWhenNoImpExtBiddersPresent() { // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.imp[0].ext must contain at least one bidder"); + .containsOnly("request.imp[0].ext.prebid must be non-empty object"); } @Test - public void validateShouldReturnValidationMessagesWhenImpExtBidderIsUnknown() { + public void validateShouldReturnValidationMessageWhenImpExtPrebidIsNotObject() { // given - final BidRequest bidRequest = validBidRequestBuilder().build(); - given(bidderCatalog.isValidName(eq(RUBICON))).willReturn(false); + final BidRequest bidRequest = validBidRequestBuilder() + .imp(singletonList(validImpBuilder().ext(mapper.valueToTree(singletonMap("prebid", "test"))).build())) + .build(); // when final ValidationResult result = requestValidator.validate(bidRequest); // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.imp[0].ext contains unknown bidder: rubicon"); + .containsOnly("request.imp[0].ext.prebid must be non-empty object"); } @Test - public void validateShouldReturnEmptyValidationMessagesWhenOnlyPrebidImpExtExist() { + public void validateShouldReturnEmptyValidationMessagesWhenOnlyImpExtPrebidExist() { // given final BidRequest bidRequest = validBidRequestBuilder() .imp(singletonList(validImpBuilder() - .ext(mapper.valueToTree(singletonMap("prebid", "test"))).build())) + .ext(mapper.valueToTree(singletonMap("prebid", singletonMap("attr", "value")))).build())) .build(); // when @@ -1193,6 +1207,37 @@ public void validateShouldReturnEmptyValidationMessagesWhenOnlyPrebidImpExtExist assertThat(result.getErrors()).isEmpty(); } + @Test + public void validateShouldReturnValidationMessageWhenImpExtPrebidBidderIsNotObject() { + // given + final BidRequest bidRequest = validBidRequestBuilder() + .imp(singletonList(validImpBuilder() + .ext(mapper.valueToTree(singletonMap("prebid", singletonMap("bidder", "test")))) + .build())) + .build(); + + // when + final ValidationResult result = requestValidator.validate(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .containsOnly("request.imp[0].ext.prebid.bidder must be object"); + } + + @Test + public void validateShouldReturnValidationMessagesWhenImpExtPrebidBidderIsUnknown() { + // given + final BidRequest bidRequest = validBidRequestBuilder().build(); + given(bidderCatalog.isValidName(eq(RUBICON))).willReturn(false); + + // when + final ValidationResult result = requestValidator.validate(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .containsOnly("request.imp[0].ext.prebid.bidder contains unknown bidder: rubicon"); + } + @Test public void validateShouldReturnValidationMessageWhenBidderExtIsInvalid() { // given @@ -1205,7 +1250,8 @@ public void validateShouldReturnValidationMessageWhenBidderExtIsInvalid() { // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.imp[0].ext.rubicon failed validation.\nerrorMessage1\nerrorMessage2"); + .containsOnly( + "request.imp[0].ext.prebid.bidder.rubicon failed validation.\nerrorMessage1\nerrorMessage2"); } @Test @@ -1748,7 +1794,7 @@ public void validateShouldThrowExceptionWhenNativeRequestEmpty() { // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.imp.[0].ext.native contains empty request value"); + .containsOnly("request.imp[0].native contains empty request value"); } @Test @@ -1761,7 +1807,7 @@ public void validateShouldThrowExceptionWhenNativeRequestMalformed() { // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("Error while parsing request.imp.[0].ext.native.request"); + .containsOnly("Error while parsing request.imp[0].native.request"); } @Test @@ -2518,7 +2564,7 @@ private static Imp.ImpBuilder validImpBuilder() { .format(singletonList(Format.builder().wmin(1).wratio(5).hratio(1).build())) .build()) .pmp(Pmp.builder().deals(singletonList(Deal.builder().id("1").build())).build()) - .ext(mapper.valueToTree(singletonMap("rubicon", 0))); + .ext(mapper.valueToTree(singletonMap("prebid", singletonMap("bidder", singletonMap("rubicon", 0))))); } private static BidRequest overwriteBannerFormatInFirstImp( diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-auction-adman-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-auction-adman-request.json index 4478ab51de1..ed309b7c5f3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-auction-adman-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-auction-adman-request.json @@ -12,8 +12,12 @@ ] }, "ext": { - "adman": { - "TagID": "first-tagid" + "prebid": { + "bidder": { + "adman": { + "TagID": "first-tagid" + } + } } } }, @@ -27,8 +31,12 @@ "h": 480 }, "ext": { - "adman": { - "TagID": "second-tagid" + "prebid": { + "bidder": { + "adman": { + "TagID": "second-tagid" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json index fd5e23cb665..819bab7a863 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json @@ -12,10 +12,14 @@ ] }, "ext": { - "adocean": { - "emiter": "myao.adocean.pl", - "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "slaveId": "adoceanmyaozpniqismex" + "prebid": { + "bidder": { + "adocean": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json index bb50cf1fdd9..c8de82b117d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -76,10 +76,14 @@ ] }, "ext": { - "adocean": { - "emiter": "myao.adocean.pl", - "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "slaveId": "adoceanmyaozpniqismex" + "prebid": { + "bidder": { + "adocean": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-request.json b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-request.json index 7f817cd6eb4..5eabb73f301 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-request.json @@ -14,8 +14,12 @@ "h": 400 }, "ext": { - "avocet": { - "placement": "5ea9601ac865f911007f1b6a" + "prebid": { + "bidder": { + "avocet": { + "placement": "5ea9601ac865f911007f1b6a" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-response.json b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-response.json index c758d0c5d95..d7f70aa47e7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-auction-avocet-response.json @@ -90,8 +90,12 @@ "h": 400 }, "ext": { - "avocet": { - "placement": "5ea9601ac865f911007f1b6a" + "prebid": { + "bidder": { + "avocet": { + "placement": "5ea9601ac865f911007f1b6a" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-request.json b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-request.json index 4cf9ae64ba2..f22fe111f8d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-request.json @@ -12,8 +12,12 @@ ] }, "ext": { - "beintoo": { - "tagid": "25251" + "prebid": { + "bidder": { + "beintoo": { + "tagid": "25251" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-response.json b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-response.json index 0e159e53c0e..6487685eeaf 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-auction-beintoo-response.json @@ -78,8 +78,12 @@ ] }, "ext": { - "beintoo": { - "tagid": "25251" + "prebid": { + "bidder": { + "beintoo": { + "tagid": "25251" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-request.json b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-request.json index a3edebf4a84..6282fac2e20 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-request.json @@ -12,9 +12,13 @@ ] }, "ext": { - "datablocks": { - "host": "localhost:8090", - "sourceId": 1 + "prebid": { + "bidder": { + "datablocks": { + "host": "localhost:8090", + "sourceId": 1 + } + } } } }, @@ -29,9 +33,13 @@ ] }, "ext": { - "datablocks": { - "host": "localhost:8090", - "sourceId": 2 + "prebid": { + "bidder": { + "datablocks": { + "host": "localhost:8090", + "sourceId": 2 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-response.json b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-response.json index 66ddb8803ef..6334dca90c2 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-auction-datablocks-response.json @@ -129,9 +129,13 @@ ] }, "ext": { - "datablocks": { - "host": "localhost:8090", - "sourceId": 1 + "prebid": { + "bidder": { + "datablocks": { + "host": "localhost:8090", + "sourceId": 1 + } + } } } }, @@ -146,9 +150,13 @@ "pos": 1 }, "ext": { - "datablocks": { - "host": "localhost:8090", - "sourceId": 2 + "prebid": { + "bidder": { + "datablocks": { + "host": "localhost:8090", + "sourceId": 2 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-request.json b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-request.json index 9f0db45cbbc..3b58e7b6288 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-request.json @@ -12,8 +12,12 @@ ] }, "ext": { - "emx_digital": { - "tagid": "25251" + "prebid": { + "bidder": { + "emx_digital": { + "tagid": "25251" + } + } } } } @@ -24,7 +28,7 @@ "language": "en", "ifa": "ifaId", "ua": "Android Chrome/60", - "ip" : "127.0.0.1" + "ip": "127.0.0.1" }, "site": { "page": "http://www.example.com", diff --git a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-response.json b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-response.json index 1eb31ec7cf8..74d80b920ab 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-auction-emxdigital-response.json @@ -78,8 +78,12 @@ ] }, "ext": { - "emx_digital": { - "tagid": "25251" + "prebid": { + "bidder": { + "emx_digital": { + "tagid": "25251" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-request.json b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-request.json index a2d90139b66..1cc440f4aaa 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-request.json @@ -14,8 +14,12 @@ "h": 400 }, "ext": { - "kubient": { - "zoneid": "9042" + "prebid": { + "bidder": { + "kubient": { + "zoneid": "9042" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-response.json b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-response.json index 22af329541c..f606cc5a73d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-auction-kubient-response.json @@ -84,8 +84,12 @@ "h": 400 }, "ext": { - "kubient": { - "zoneid":"9042" + "prebid": { + "bidder": { + "kubient": { + "zoneid": "9042" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-request.json b/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-request.json index 2484264b8a9..c6c718fbb83 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-request.json @@ -22,35 +22,37 @@ ] }, "ext": { - "rubicon": { - "accountId": 2001, - "siteId": 3001, - "zoneId": 4001, - "inventory": { - "rating": [ - "5-star" - ], - "prodtype": [ - "tech" - ] - }, - "visitor": { - "ucat": [ - "new" - ], - "search": [ - "iphone" - ] - }, - "video": { - "size_id": 15, - "playerWidth": 780, - "playerHeight": "438", - "skip": 5, - "skipdelay": 1 - } - }, "prebid": { + "bidder": { + "rubicon": { + "accountId": 2001, + "siteId": 3001, + "zoneId": 4001, + "inventory": { + "rating": [ + "5-star" + ], + "prodtype": [ + "tech" + ] + }, + "visitor": { + "ucat": [ + "new" + ], + "search": [ + "iphone" + ] + }, + "video": { + "size_id": 15, + "playerWidth": 780, + "playerHeight": "438", + "skip": 5, + "skipdelay": 1 + } + } + }, "is_rewarded_inventory": 1 } } @@ -79,37 +81,41 @@ ] }, "ext": { - "appnexus": { - "member": "103", - "inv_code": "abc", - "reserve": 1.0, - "position": "below", - "traffic_source_code": "trafficSource", - "keywords": [ - { - "key": "foo", - "value": [ - "bar", - "baz" + "prebid": { + "bidder": { + "appnexus": { + "member": "103", + "inv_code": "abc", + "reserve": 1.0, + "position": "below", + "traffic_source_code": "trafficSource", + "keywords": [ + { + "key": "foo", + "value": [ + "bar", + "baz" + ] + } ] - } - ] - }, - "appnexusAlias": { - "member": "104", - "inv_code": "abc", - "reserve": 1.0, - "position": "above", - "traffic_source_code": "trafficSourceAlias", - "keywords": [ - { - "key": "foo", - "value": [ - "barAlias", - "bazAlias" + }, + "appnexusAlias": { + "member": "104", + "inv_code": "abc", + "reserve": 1.0, + "position": "above", + "traffic_source_code": "trafficSourceAlias", + "keywords": [ + { + "key": "foo", + "value": [ + "barAlias", + "bazAlias" + ] + } ] } - ] + } } } }, @@ -120,8 +126,12 @@ "ver": "1.1" }, "ext": { - "appnexus": { - "placement_id": 9880618 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 9880618 + } + } } } }, @@ -133,8 +143,12 @@ ] }, "ext": { - "appnexus": { - "placement_id": 10433394 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 10433394 + } + } } } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-response.json b/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-response.json index 1683448972d..99dc0964de7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rubicon_appnexus/test-auction-rubicon-appnexus-response.json @@ -430,35 +430,37 @@ ] }, "ext": { - "rubicon": { - "accountId": 2001, - "siteId": 3001, - "zoneId": 4001, - "inventory": { - "rating": [ - "5-star" - ], - "prodtype": [ - "tech" - ] - }, - "visitor": { - "ucat": [ - "new" - ], - "search": [ - "iphone" - ] - }, - "video": { - "size_id": 15, - "playerWidth": 780, - "playerHeight": "438", - "skip": 5, - "skipdelay": 1 - } - }, "prebid": { + "bidder": { + "rubicon": { + "accountId": 2001, + "siteId": 3001, + "zoneId": 4001, + "inventory": { + "rating": [ + "5-star" + ], + "prodtype": [ + "tech" + ] + }, + "visitor": { + "ucat": [ + "new" + ], + "search": [ + "iphone" + ] + }, + "video": { + "size_id": 15, + "playerWidth": 780, + "playerHeight": "438", + "skip": 5, + "skipdelay": 1 + } + } + }, "is_rewarded_inventory": 1 } } @@ -476,12 +478,14 @@ "h": 600 }, "ext": { - "rubicon": { - "accountId": 5001, - "siteId": 6001, - "zoneId": 7001 - }, "prebid": { + "bidder": { + "rubicon": { + "accountId": 5001, + "siteId": 6001, + "zoneId": 7001 + } + }, "storedrequest": { "id": "test-rubicon-stored-request-2" } @@ -503,37 +507,41 @@ ] }, "ext": { - "appnexus": { - "member": "103", - "inv_code": "abc", - "reserve": 1.0, - "position": "below", - "traffic_source_code": "trafficSource", - "keywords": [ - { - "key": "foo", - "value": [ - "bar", - "baz" + "prebid": { + "bidder": { + "appnexus": { + "member": "103", + "inv_code": "abc", + "reserve": 1.0, + "position": "below", + "traffic_source_code": "trafficSource", + "keywords": [ + { + "key": "foo", + "value": [ + "bar", + "baz" + ] + } ] - } - ] - }, - "appnexusAlias": { - "member": "104", - "inv_code": "abc", - "reserve": 1.0, - "position": "above", - "traffic_source_code": "trafficSourceAlias", - "keywords": [ - { - "key": "foo", - "value": [ - "barAlias", - "bazAlias" + }, + "appnexusAlias": { + "member": "104", + "inv_code": "abc", + "reserve": 1.0, + "position": "above", + "traffic_source_code": "trafficSourceAlias", + "keywords": [ + { + "key": "foo", + "value": [ + "barAlias", + "bazAlias" + ] + } ] } - ] + } } } }, @@ -544,8 +552,12 @@ "ver": "1.1" }, "ext": { - "appnexus": { - "placement_id": 9880618 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 9880618 + } + } } } }, @@ -557,8 +569,12 @@ ] }, "ext": { - "appnexus": { - "placement_id": 10433394 + "prebid": { + "bidder": { + "appnexus": { + "placement_id": 10433394 + } + } } } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-request.json b/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-request.json index 073016adb28..276ab3616a8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-request.json @@ -12,14 +12,18 @@ ] }, "ext": { - "sharethrough": { - "pkey": "abc123", - "iframe": true, - "iframeSize": [ - 50, - 50 - ], - "bidfloor": 3.5 + "prebid": { + "bidder": { + "sharethrough": { + "pkey": "abc123", + "iframe": true, + "iframeSize": [ + 50, + 50 + ], + "bidfloor": 3.5 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-response.json b/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-response.json index e86ac798d08..b20ecd3bf0a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sharethrough/test-auction-sharethrough-response.json @@ -79,14 +79,18 @@ ] }, "ext": { - "sharethrough": { - "pkey": "abc123", - "iframe": true, - "iframeSize": [ - 50, - 50 - ], - "bidfloor": 3.5 + "prebid": { + "bidder": { + "sharethrough": { + "pkey": "abc123", + "iframe": true, + "iframeSize": [ + 50, + 50 + ], + "bidfloor": 3.5 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-request.json b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-request.json index 34abff9aca8..5076a6a85dd 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-request.json @@ -14,11 +14,15 @@ "h": 400 }, "ext": { - "smartadserver": { - "siteId": 1, - "pageId": 2, - "formatId": 3, - "networkId": 73 + "prebid": { + "bidder": { + "smartadserver": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-response.json b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-response.json index f53c1f4dcc4..3eadc63eab9 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-auction-smartadserver-response.json @@ -84,11 +84,15 @@ "h": 400 }, "ext": { - "smartadserver": { - "siteId": 1, - "pageId": 2, - "formatId": 3, - "networkId": 73 + "prebid": { + "bidder": { + "smartadserver": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-request.json index c3c161d62aa..bc3effc291b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-request.json @@ -7,8 +7,12 @@ "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"required\":1,\"title\":{\"len\":500}}]}" }, "ext": { - "triplelift_native": { - "inventoryCode": "foo" + "prebid": { + "bidder": { + "triplelift_native": { + "inventoryCode": "foo" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-response.json b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-response.json index 570aefa9feb..1e9e509aa44 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-auction-triplelift-native-response.json @@ -78,8 +78,12 @@ "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":500}}]}" }, "ext": { - "triplelift_native": { - "inventoryCode": "foo" + "prebid": { + "bidder": { + "triplelift_native": { + "inventoryCode": "foo" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-request.json index c53a1e56cb3..13e6ab9ba21 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-request.json @@ -12,15 +12,19 @@ ] }, "ext": { - "yieldlab": { - "adslotId": "12345", - "supplyId": "123456789", - "adSize": "400x300", - "targeting": { - "key1": "value1", - "key2": "value2" - }, - "extId": "abc" + "prebid": { + "bidder": { + "yieldlab": { + "adslotId": "12345", + "supplyId": "123456789", + "adSize": "400x300", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-response.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-response.json index 1f970ec91b1..92eda441221 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldlab/test-auction-yieldlab-response.json @@ -127,14 +127,18 @@ ] }, "ext": { - "yieldlab": { - "adSize": "400x300", - "adslotId": "12345", - "extId": "abc", - "supplyId": "123456789", - "targeting": { - "key1": "value1", - "key2": "value2" + "prebid": { + "bidder": { + "yieldlab": { + "adSize": "400x300", + "adslotId": "12345", + "extId": "abc", + "supplyId": "123456789", + "targeting": { + "key1": "value1", + "key2": "value2" + } + } } } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-request.json b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-request.json index be89aba4f07..cf0d5e2ef9c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-request.json @@ -12,9 +12,13 @@ ] }, "ext": { - "zeroclickfraud": { - "host": "localhost:8090", - "sourceId": 1 + "prebid": { + "bidder": { + "zeroclickfraud": { + "host": "localhost:8090", + "sourceId": 1 + } + } } } }, @@ -29,9 +33,13 @@ ] }, "ext": { - "zeroclickfraud": { - "host": "localhost:8090", - "sourceId": 2 + "prebid": { + "bidder": { + "zeroclickfraud": { + "host": "localhost:8090", + "sourceId": 2 + } + } } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-response.json b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-response.json index 6b9f939a0b9..e4bd8fe0950 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-auction-zeroclickfraud-response.json @@ -129,9 +129,13 @@ ] }, "ext": { - "zeroclickfraud": { - "host": "localhost:8090", - "sourceId": 1 + "prebid": { + "bidder": { + "zeroclickfraud": { + "host": "localhost:8090", + "sourceId": 1 + } + } } } }, @@ -146,9 +150,13 @@ "pos": 1 }, "ext": { - "zeroclickfraud": { - "host": "localhost:8090", - "sourceId": 2 + "prebid": { + "bidder": { + "zeroclickfraud": { + "host": "localhost:8090", + "sourceId": 2 + } + } } } }