diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index f5d07b2639d..b34d4827eae 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -14,7 +14,7 @@ jobs: - name: Extract tag from commit message run: | target_tag=${COMMIT_MSG#"Prebid Server prepare release "} - echo "::set-env name=TARGET_TAG::$target_tag" + echo "TARGET_TAG=$target_tag" >> $GITHUB_ENV env: COMMIT_MSG: ${{ github.event.head_commit.message }} - name: Create and publish release diff --git a/README.md b/README.md index e7edc4aca48..6f4788e5bb1 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -### This code is being used in production by multiple Prebid.org members, but is not the "official" version. See https://github.com/prebid/prebid-server/ +### This is the Java version of Prebid Server. See the Prebid Server [Feature List](https://docs.prebid.org/prebid-server/features/pbs-feature-idx.html) and [FAQ entry](https://docs.prebid.org/faq/prebid-server-faq.html#why-are-there-two-versions-of-prebid-server-are-they-kept-in-sync) to understand the differences between PBS-Java and [PBS-Go](https://github.com/prebid/prebid-server). -# Prebid Server +# Prebid Server (Java) -[![GitHub version](https://badge.fury.io/gh/rubicon-project%2fprebid-server-java.svg)](http://badge.fury.io/gh/rubicon-project%2fprebid-server-java) -[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/rubicon-project/prebid-server-java.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/rubicon-project/prebid-server-java/context:java) -[![Total alerts](https://img.shields.io/lgtm/alerts/g/rubicon-project/prebid-server-java.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/rubicon-project/prebid-server-java/alerts/) -[![GitHub contributors](https://img.shields.io/github/contributors/rubicon-project/prebid-server-java.svg)](https://GitHub.com/rubicon-project/prebid-server-java/contributors/) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/rubicon-project/prebid-server-java/blob/master/docs/contributing.md) -[![GitHub pull-requests closed](https://img.shields.io/github/issues-pr-closed/rubicon-project/prebid-server-java.svg)](https://GitHub.com/rubicon-project/prebid-server-java/pull/) +[![GitHub version](https://badge.fury.io/gh/prebid%2fprebid-server-java.svg)](http://badge.fury.io/gh/prebid%2fprebid-server-java) +[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/prebid/prebid-server-java.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/prebid-server-java/context:java) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/prebid/prebid-server-java.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/prebid/prebid-server-java/alerts/) +[![GitHub contributors](https://img.shields.io/github/contributors/prebid/prebid-server-java.svg)](https://GitHub.com/prebid/prebid-server-java/contributors/) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/prebid/prebid-server-java/blob/master/docs/contributing.md) +[![GitHub pull-requests closed](https://img.shields.io/github/issues-pr-closed/prebid/prebid-server-java.svg)](https://GitHub.com/prebid/prebid-server-java/pull/) Prebid Server is an open source implementation of Server-Side Header Bidding. -It is managed by [Prebid.org](http://prebid.org/overview/what-is-prebid-org.html), -and upholds the principles from the [Prebid Code of Conduct](http://prebid.org/wrapper_code_of_conduct.html). +It is managed by Prebid.org, +and upholds the principles from the [Prebid Code of Conduct](https://prebid.org/wrapper_code_of_conduct.html). This project does not support the same set of Bidders as Prebid.js, although there is overlap. The current set can be found in the [adapters](./src/main/java/org/prebid/server/bidder) package. If you don't see the one you want, feel free to [contribute it](docs/developers/add-new-bidder.md). For more information, see: -- [What is Prebid?](http://prebid.org/overview/intro.html) -- [Getting started with Prebid Server](http://prebid.org/dev-docs/get-started-with-prebid-server.html) -- [Current Bidders](http://prebid.org/dev-docs/prebid-server-bidders.html) +- [What is Prebid?](https://prebid.org/why-prebid/) +- [Getting started with Prebid Server](https://docs.prebid.org/prebid-server/overview/prebid-server-overview.html) +- [Current Bidders](https://docs.prebid.org/dev-docs/pbs-bidders.html) # Getting Started @@ -28,23 +28,21 @@ The server makes the following assumptions: - No ranking or decisioning is performed by this server. It just proxies requests. - No ad quality management (e.g., malware, viruses, deceptive creatives) is performed by this server. - This server does no fraud scanning and does nothing to prevent bad traffic. -- This server does no logging. -- This server has not user profiling or user data collection capabilities. +- This server logs errors but not requests. +- This server has no user profiling or user data collection capabilities. This project is built upon [Vert.x](http://vertx.io) to achieve high request throughput. We use [Maven](https://maven.apache.org) and attempt to introduce minimal dependencies. When running, the server responds to several HTTP [endpoints](docs/endpoints). -To start the Prebid Server you need to do the following steps: - ## Building Follow next steps to create JAR file which can be deployed locally. - Download or clone a project: ```bash -git clone https://github.com/rubicon-project/prebid-server-java.git +git clone https://github.com/prebid/prebid-server-java.git ``` - Move to project directory: @@ -84,28 +82,27 @@ and verify response status is `200 OK`. # Documentation ## Development -- [Differences Between Prebid Server Go and Java](differenceBetweenPBSGo-and-Java.md) -- [Endpoints](endpoints) -- [Adding new bidder](developers/add-new-bidder.md) -- [Adding new analytics module](developers/add-new-analytics-module.md) -- [Adding viewability support](developers/add-viewability-vendors.md) -- [Auction result post-processing](developers/auction-result-post-processing.md) -- [Cookie Syncs](developers/cookie-syncs.md) -- [Stored Requests](developers/stored-requests.md) -- [Unit Tests](developers/unit-tests.md) -- [GDPR](developers/gdpr.md) +- [Endpoints](docs/endpoints) +- [Adding new bidder](docs/developers/add-new-bidder.md) +- [Adding new analytics module](docs/developers/add-new-analytics-module.md) +- [Adding viewability support](docs/developers/add-viewability-vendors.md) +- [Auction result post-processing](docs/developers/auction-result-post-processing.md) +- [Cookie Syncs](docs/developers/cookie-syncs.md) +- [Stored Requests](docs/developers/stored-requests.md) +- [Unit Tests](docs/developers/unit-tests.md) +- [GDPR](docs/developers/gdpr.md) ## Maintenance -- [Build for local](build.md) -- [Build for AWS](build-aws.md) -- [Configure application](config.md) - - [Full list of configuration options](config-app.md) - - [Application settings](application-settings.md) -- [Run with optimizations](run.md) -- [Metrics](metrics.md) +- [Build for local](docs/build.md) +- [Build for AWS](docs/build-aws.md) +- [Configure application](docs/config.md) + - [Full list of configuration options](docs/config-app.md) + - [Application settings](docs/application-settings.md) +- [Run with optimizations](docs/run.md) +- [Metrics](docs/metrics.md) ## Contributing -- [Contributing](contributing.md) -- [Code Style](code-style.md) -- [Code Review](code-reviews.md) -- [Versioning](versioning.md) +- [Contributing](docs/contributing.md) +- [Code Style](docs/code-style.md) +- [Code Review](docs/code-reviews.md) +- [Versioning](docs/versioning.md) diff --git a/docs/application-settings.md b/docs/application-settings.md index 178ae5c9cc2..0f2ed0076c3 100644 --- a/docs/application-settings.md +++ b/docs/application-settings.md @@ -23,14 +23,17 @@ There are two ways to configure application settings: database and file. This do - `truncate-target-attr` - Maximum targeting attributes size. Values between 1 and 255. - `default-integration` - Default integration to assume. - `analytics-config.auction-events.` - defines which channels are supported by analytics for this account +- `bid-validations.banner-creative-max-size` - Overrides creative max size validation for banners. +- `status` - allows to mark account as `active` or `inactive`. +Here are the definitions of the "purposes" that can be defined in the GDPR setting configurations: ``` Purpose | Purpose goal | Purpose meaning for PBS (n\a - not affected) ----------|---------------------------------|--------------------------------------------- p1 | Access device | Stops usersync for given vendor and stops settings cookie on `/seuid` p2 | Select basic ads | Verify consent for each vendor as appropriate for the enforcement method before calling a bid adapter. If consent is not granted, log a metric and skip it. p3 | Personalized ads profile | n\a -p4 | Select personalized ads | Verify consent for each vendor that passed the Purpose 2. If consent is not granted, remove the bidrequest.userId, user.ext.eids, user.ext.digitrust, device.if attributes and call the adapter. +p4 | Select personalized ads | Verify consent for each vendor that passed the Purpose 2. If consent is not granted, remove the bidrequest.userId, user.ext.eids, device.if attributes and call the adapter. p5 | Personalized content profile | n\a p6 | Select personalized content | n\a p7 | Measure ad performance | Verify consent for each analytics module. If consent is not grantet skip it. @@ -42,22 +45,26 @@ sf1 | Precise geo | Verifies user opt-in. If the user sf2 | Fingerprinting | n\a ``` -## File application setting +## Setting Account Configuration in Files In file based approach all configuration stores in .yaml files, path to which are defined in application properties. ### Configuration in application.yaml -``` +The general idea is that you'll place all the account-specific settings in a separate YAML file and point to that file. + +```yaml settings: filesystem: settings-filename: ``` ### File format -``` +Here's an example YAML file containing account-specific settings: + +```yaml accounts: - - id: 14062 + - id: 1111 bannerCacheTtl: 100 videoCacheTtl: 100 eventsEnabled: true @@ -69,6 +76,7 @@ accounts: analytics-config: auction-events: amp: true + status: active gdpr: enabled: true integration-enabled: @@ -151,54 +159,71 @@ accounts: purpose-one-treatment-interpretation: ignore ``` - -## Database application setting +## Setting Account Configuration in the Database + +In database approach account properties are stored in database table. -In database approach account properties are stored in database table with name accounts_account. +SQL query for retrieving account is configurable and can be specified in [application configuration](config-app.md). +Requirements for the SQL query stated below. ### Configuration in application.yaml -``` + +```yaml settings: database: - type: pool-size: 20 - type: mysql + type: host: port: + account-query: ``` -### Table description +### Configurable SQL query for account requirements -Query to create accounts_account table: +The general approach is that each host company can set up their database however they wish, so long as the configurable query run by +Prebid Server returns expected data in the expected order. Here's an example configuration: +```yaml +settings: + database: + type: mysql + account-query: SELECT uuid, price_granularity, banner_cache_ttl, video_cache_ttl, events_enabled, enforce_ccpa, tcf_config, analytics_sampling_factor, truncate_target_attr, default_integration, analytics_config, bid_validations, status FROM accounts_account where uuid = ? LIMIT 1 ``` -'CREATE TABLE `accounts_account` ( -`id` int(10) unsigned NOT NULL AUTO_INCREMENT, -`uuid` varchar(40) NOT NULL, -`price_granularity` enum('low','med','high','auto','dense','unknown') NOT NULL DEFAULT 'unknown', -`granularityMultiplier` decimal(9,3) DEFAULT NULL, -`banner_cache_ttl` int(11) DEFAULT NULL, -`video_cache_ttl` int(11) DEFAULT NULL, -`events_enabled` bit(1) DEFAULT NULL, -`enforce_ccpa` bit(1) DEFAULT NULL, -`enforce_gdpr` bit(1) DEFAULT NULL, -`tcf_config` json DEFAULT NULL, -`analytics_sampling_factor` tinyint(4) DEFAULT NULL, -`truncate_target_attr` tinyint(3) unsigned DEFAULT NULL, -`default_integration` varchar(64) DEFAULT NULL, -`analytics_config` varchar(512) DEFAULT NULL, -`status` enum('active','inactive') DEFAULT 'active', -`updated_by` int(11) DEFAULT NULL, -`updated_by_user` varchar(64) DEFAULT NULL, -`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -PRIMARY KEY (`id`), -UNIQUE KEY `uuid` (`uuid`)) -ENGINE=InnoDB DEFAULT CHARSET=utf8' -``` -where tcf_config column is json with next format +The SQL query for account must: +* return following columns, with specified type, in this order: + * account ID, string + * price granularity, string + * banner cache TTL, integer + * video cache TTL, integer + * events enabled flag, boolean + * enforce CCPA flag, boolean + * TCF configuration, JSON string, see below + * analytics sampling factor, integer + * maximum targeting attribute size, integer + * default integration value, string + * analytics configuration, JSON string, see below + * status, string. Expected values: "active", "inactive", NULL. Only "inactive" has any effect and only when settings.enforce-valid-account is on. +* specify a special single `%ACCOUNT_ID%` placeholder in the `WHERE` clause that will be replaced with account ID in +runtime + +It is recommended to include `LIMIT 1` clause in the query because only the very first result returned will be taken. + +If a host company doesn't support a given field, or they have a different table name, they can just update the query with whatever values are needed. e.g. +```yaml +settings: + database: + type: mysql + account-query: SELECT uuid, 'med', banner_cache_ttl, video_cache_ttl, events_enabled, enforce_ccpa, tcf_config, 0, null, default_integration, '{}', '{}' FROM myaccountstable where uuid = ? LIMIT 1 ``` +### Configuration Details + +#### TCF configuration JSON + +Here's an example of the value that the `tcf_config` column can take: + +```json { "enabled": true, "integration-enabled": { @@ -206,8 +231,8 @@ where tcf_config column is json with next format "web": true, "app": true, "amp": true - } - "purpose-one-treatment-interpretation": "ignore" + }, + "purpose-one-treatment-interpretation": "ignore", "purposes": { "p1": { "enforce-purpose": "full", @@ -309,10 +334,62 @@ where tcf_config column is json with next format } ``` -Query used to get an account: +#### Bid Validations configuration JSON + +The `bid_validations` column is json with this format: + +```json +{ + "banner-creative-max-size": "enforce" +} +``` + +Valid values are: +- "skip": don't do anything about creative max size for this publisher +- "warn": if a bidder returns a creative that's larger in height or width than any of the allowed sizes, log an operational warning. +- "enforce": if a bidder returns a creative that's larger in height or width than any of the allowed sizes, reject the bid and log an operational warning. + +#### Analytics Validations configuration JSON + +The `analytics_config` configuration column format: + +```json +{ + "auction-events": { + "web": true, // the analytics adapter should log auction events when the channel is web + "amp": true, // the analytics adapter should log auction events when the channel is AMP + "app": false // the analytics adapter should not log auction events when the channel is app + } +} ``` -SELECT uuid, price_granularity, banner_cache_ttl, video_cache_ttl, events_enabled, enforce_ccpa, tcf_config, analytics_sampling_factor, truncate_target_attr, default_integration, analytics_config -FROM accounts_account where uuid = ? -LIMIT 1 +#### Creating the accounts table + +Traditionally the table name used by Prebid Server is `accounts_account`. No one remembers why. But here's SQL +you could use to create your table: + +```sql +'CREATE TABLE `accounts_account` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) NOT NULL, + `price_granularity` enum('low','med','high','auto','dense','unknown') NOT NULL DEFAULT 'unknown', + `granularityMultiplier` decimal(9,3) DEFAULT NULL, + `banner_cache_ttl` int(11) DEFAULT NULL, + `video_cache_ttl` int(11) DEFAULT NULL, + `events_enabled` bit(1) DEFAULT NULL, + `enforce_ccpa` bit(1) DEFAULT NULL, + `enforce_gdpr` bit(1) DEFAULT NULL, + `tcf_config` json DEFAULT NULL, + `analytics_sampling_factor` tinyint(4) DEFAULT NULL, + `truncate_target_attr` tinyint(3) unsigned DEFAULT NULL, + `default_integration` varchar(64) DEFAULT NULL, + `analytics_config` varchar(512) DEFAULT NULL, + `bid_validations` json DEFAULT NULL, + `status` enum('active','inactive') DEFAULT 'active', + `updated_by` int(11) DEFAULT NULL, + `updated_by_user` varchar(64) DEFAULT NULL, + `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +PRIMARY KEY (`id`), +UNIQUE KEY `uuid` (`uuid`)) +ENGINE=InnoDB DEFAULT CHARSET=utf8' ``` 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/build.md b/docs/build.md index 89e3d9a20f9..44b13bee47b 100644 --- a/docs/build.md +++ b/docs/build.md @@ -19,7 +19,7 @@ Follow next steps to create JAR which can be deployed locally. Download or clone a project locally: ```bash -git clone https://github.com/rubicon-project/prebid-server-java.git +git clone https://github.com/prebid/prebid-server-java.git ``` Move to project directory: diff --git a/docs/code-reviews.md b/docs/code-reviews.md index 972597751f5..78728fef18a 100644 --- a/docs/code-reviews.md +++ b/docs/code-reviews.md @@ -1,7 +1,7 @@ # Code Reviews ## Standards -Anyone is free to review and comment on any [open pull requests](https://github.com/rubicon-project/prebid-server-java/pulls). +Anyone is free to review and comment on any [open pull requests](https://github.com/prebid/prebid-server-java/pulls). All pull requests must be reviewed and approved by at least one [core member](https://github.com/orgs/prebid/teams/core/members) before merge. @@ -38,7 +38,7 @@ Some examples include: - Can we improve the user's experience in any way? - Have the relevant [docs]() been added or updated? If not, add the `needs docs` label. - Do you believe that the code works by looking at the unit tests? If not, suggest more tests until you do! -- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/rubicon-project/prebid-server-java/issues) +- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/prebid/prebid-server-java/issues) explaining it. Are there better ways to achieve those goals? - Does the code use any global, mutable state? [Inject dependencies](https://en.wikipedia.org/wiki/Dependency_injection) instead! - Can the code be organized into smaller, more modular pieces? diff --git a/docs/config-app.md b/docs/config-app.md index 5716bd19717..7d59a920ed9 100644 --- a/docs/config-app.md +++ b/docs/config-app.md @@ -63,6 +63,9 @@ Removes and downloads file again if depending service cant process probably corr - `max-timeout-ms` - this setting controls maximum timeout for /auction endpoint. - `timeout-adjustment-ms` - reduces timeout value passed in legacy Auction request so that Prebid Server can handle timeouts from adapters and respond to the request before it times out. +## Default bid request +- `default-request.file.path` - path to a JSON file containing the default request + ## Auction (OpenRTB) - `auction.blacklisted-accounts` - comma separated list of blacklisted account IDs. - `auction.blacklisted-apps` - comma separated list of blacklisted applications IDs, requests from which should not be processed. @@ -75,7 +78,10 @@ Removes and downloads file again if depending service cant process probably corr - `auction.cache.expected-request-time-ms` - approximate value in milliseconds for Cache Service interacting. This time will be subtracted from global timeout. - `auction.cache.only-winning-bids` - if equals to `true` only the winning bids would be cached. Has lower priority than request-specific flags. - `auction.generate-bid-id` - whether to generate seatbid[].bid[].ext.prebid.bidid in the OpenRTB response. -- `auction.id-generator-type` - if generate-bid-id is on, then this defines how the ID should be generated. Currently onlye `uuid` is supported. +- `auction.generate-source-tid` - whether to generate bidrequest.source.tid in the OpenRTB request. +- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. +- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. +- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` passed to bidders ## Amp (OpenRTB) - `amp.default-timeout-ms` - default operation timeout for OpenRTB Amp requests. @@ -134,6 +140,8 @@ But feel free to add additional bidder's specific options. - `currency-converter.external-rates.url` - the url for Prebid.org’s currency file. [More details](http://prebid.org/dev-docs/modules/currency.html) - `currency-converter.external-rates.default-timeout-ms` - default operation timeout for fetching currency rates. - `currency-converter.external-rates.refresh-period-ms` - default refresh period for currency rates updates. +- `currency-converter.external-rates.stale-after-ms` - how old currency rates should be to become considered stale. +- `currency-converter.external-rates.stale-period-ms` - stale period after which the latest external currency rates get discarded. ## Admin Endpoints - `admin-endpoints.version.enabled` - if equals to `true` the endpoint will be available. @@ -237,6 +245,7 @@ For database data source available next options: - `settings.database.user` - database user. - `settings.database.password` - database password. - `settings.database.pool-size` - set the initial/min/max pool size of database connections. +- `settings.database.account-query` - the SQL query to fetch account. - `settings.database.stored-requests-query` - the SQL query to fetch stored requests. - `settings.database.amp-stored-requests-query` - the SQL query to fetch AMP stored requests. - `settings.database.stored-responses-query` - the SQL query to fetch stored responses. @@ -253,6 +262,24 @@ For HTTP data source available next options: For account processing rules available next options: - `settings.enforce-valid-account` - if equals to `true` then request without account id will be rejected with 401. +It is possible to specify default account configuration values that will be assumed if account config have them +unspecified or missing at all. Example: +```yaml +settings: + default-account-config: + events-enabled: true + enforce-ccpa: true + gdpr: '{"enabled": true}' + analytics-sampling-factor: 1 + default-integration: pbjs + analytics-config: '{"auction-events":{"amp":true}}' +``` +See [application settings](application-settings.md) for full reference of available configuration parameters. +Be aware that individual configuration values will not be merged with concrete +account values if they exist in account configuration but account value will completely replace the default value. For +example, if account configuration defines `gdpr` field, it will completely replace `settings.default-account-config.gdpr` +value in the final account configuration model. + For caching available next options: - `settings.in-memory-cache.ttl-seconds` - how long (in seconds) data will be available in LRU cache. - `settings.in-memory-cache.cache-size` - the size of LRU cache. @@ -322,6 +349,9 @@ If not defined in config all other Health Checkers would be disabled and endpoin ## CCPA - `ccpa.enforce` - if equals to `true` enforces to check ccpa policy, otherwise ignore ccpa verification. +## LMT +- `lmt.enforce` - if equals to `true` enforces to check lmt policy, otherwise ignore lmt verification. + ## Geo Location - `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client request. - `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client more robust. diff --git a/docs/contributing.md b/docs/contributing.md index 3ca432f3905..ae3f10dc4e9 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,7 +2,7 @@ ## Create an issue -[Create an issue](https://github.com/rubicon-project/prebid-server-java/issues/new) describing the motivation for your changes. +[Create an issue](https://github.com/prebid/prebid-server-java/issues/new) describing the motivation for your changes. Are you fixing a bug? Improving documentation? Optimizing some slow code? Pull Requests without associated Issues may still be accepted, if the motivation is obvious. @@ -29,6 +29,6 @@ those updates must be submitted in the same Pull Request as the code changes. ## Open a Pull Request When you're ready, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request/) -against the `master` branch of [our GitHub repository](https://github.com/rubicon-project/prebid-server-java/compare). +against the `master` branch of [our GitHub repository](https://github.com/prebid/prebid-server-java/compare). If the tests pass locally, but fail on your PR, [update your fork](https://help.github.com/articles/syncing-a-fork/) with the latest code from `master`. diff --git a/docs/developers/PrebidServerJava_GDPR_Requirements.pdf b/docs/developers/PrebidServerJava_GDPR_Requirements.pdf deleted file mode 100644 index 33792ab1d4c..00000000000 Binary files a/docs/developers/PrebidServerJava_GDPR_Requirements.pdf and /dev/null differ 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/default-request.md b/docs/developers/default-request.md new file mode 100644 index 00000000000..08bf1041009 --- /dev/null +++ b/docs/developers/default-request.md @@ -0,0 +1,29 @@ +# Server Based Global Default Request + +This allows a default request to be defined that allows the server to set up some defaults for all incoming +requests. A stored request(s) referenced by a bid request override default request, and of course any options specified +directly in the bid request override both. The default request is only read on server startup, it is meant as an +installation static default rather than a dynamic tuning option. + +## Config Options + +Following config options are exposed to support this feature. +```yaml +default-request: + file: + path : path/to/default/request.json +``` + +The `path` option is the path to a JSON file containing the default request JSON. +```json +{ + "tmax": "2000", + "regs": { + "ext": { + "gdpr": 1 + } + } +} +``` +This will be JSON merged into the incoming requests at the top level. These will be used as fallbacks which can be +overridden by both Stored Requests _and_ the incoming HTTP request payload. diff --git a/docs/developers/stored-requests.md b/docs/developers/stored-requests.md index e76cddb6811..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 + } + } } } } @@ -122,7 +130,7 @@ So far, our examples have only used Stored Imp data. However, Stored Requests are also allowed on the [BidRequest](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf#page=15). These work exactly the same way, but support storing properties like timeouts and price granularity. -For example, assume the following `stored-requests/stored-request.json`: +For example, assume the following `stored-requests/{id}.json`: ```json { @@ -148,7 +156,7 @@ Then HTTP request like: "ext": { "prebid": { "storedrequest": { - "id": "stored-request.json" + "id": "{id}" } } } diff --git a/docs/differenceBetweenPBSGo-and-Java.md b/docs/differenceBetweenPBSGo-and-Java.md deleted file mode 100644 index 3e6d185f550..00000000000 --- a/docs/differenceBetweenPBSGo-and-Java.md +++ /dev/null @@ -1,46 +0,0 @@ -# Differences Between Prebid Server Go and Java - -January 24, 2019 - -The sister Prebid Server projects are both busy and moving forward at different paces on different features. Sometimes a feature may exist in one implementation -and not the other for an interim period. This page tracks known differences that may persist for longer than a couple of weeks. - -[Feature Checklist Overview](pbs-java-and-go-features-review.md) - -## Feature Differences - -1) PBS-Java supports Stored Responses [issue 861](https://github.com/prebid/prebid-server/issues/861). PBS-Java [PR 354](https://github.com/rubicon-project/prebid-server-java/pull/354). -1) PBS-Java supports Currency conversion. PBS-Go has it implemented, but disabled by default(still under dev) [issue 280](https://github.com/prebid/prebid-server/issues/280), [issue 760](https://github.com/prebid/prebid-server/pull/760). PBS-Java [PR 22](https://github.com/rubicon-project/prebid-server-java/pull/22) -1) PBS-Java Currency conversion supports finding intermediate conversion rate, e.g. if pairs USD : AUD = 1.2 and EUR : AUD = 1.5 are present and EUR to USD conversion is needed, will return (1/1.5) * 1.2 conversion rate. -1) PBS-Go Currency conversion admin debug endpoint exposes following information: Sync source URL, Internal rates, Update frequency, Last update. PBS-Java `/currency-rates` admin endpoint currently supports checking the latest update time only and is not available if currency conversion is disabled. -1) PBS-Java supports IP-address lookup in certain scenarios around GDPR. See https://github.com/rubicon-project/prebid-server-java/blob/master/docs/developers/PrebidServerJava_GDPR_Requirements.pdf -1) PBS-Java supports InfluxDB, Graphite and Prometheus, PBS-Go supports InfluxDB and Prometheus as metrics backend. -1) PBS-Java has Circuit Breaker mechanism for database, http and geolocation requests. This can protect the server in scenarios where an external service becomes unavailable. -1) PBS-Java supports `ext.prebid.cache.{bids,vastxml}.returnCreative` field to control creative presence in response (`true` by default). -1) PBS-Java support caching winning bids only through `auction.cache.only-winning-bids` configuration property or request field `request.ext.prebid.cache.winningonly`. PBS-Java [issue 279](https://github.com/rubicon-project/prebid-server-java/issues/279), [PR 484](https://github.com/rubicon-project/prebid-server-java/pull/484). -1) PBS-Java has a specific `host-cookie` and `uids` cookie processing for all endpoints, that sets `uids.HOST-BIDDER` from `host-cookie` if first is absent or not equal to second. -1) PBS-Java has a specific `/cookie-sync` behaviour, that sets `/setuid` as usersync-url for host-bidder if `host-cookie` specified but `uids.HOST-BIDDER` undefined or differs. -1) PBS-Java has `/event` endpoint to allow Web browsers and mobile applications to notify about different ad events (win, view etc). Filling new bid extensions `response.seatbid.bid.ext.prebid.events.{win,view}` with events url after successful auction completing makes it possible. -1) PBS-Java supports per-account cache TTL and event URLs configuration in the database in columns `banner_cache_ttl`, `video_cache_ttl` and `events_enabled`. -1) PBS-Java does not support passing bidder extensions in `imp[...].ext.prebid.bidder`. PBS-Go [PR 846](https://github.com/prebid/prebid-server/pull/846) -1) PBS-Java responds with active bidders only in `/info/bidders` and `/info/bidders/all` endpoints, although PBS-Go returns all implemented ones. Calling `/info/bidders/{bidderName}` with a disabled bidder name will result in 404 Not Found, which is a desired behaviour, unlike in PBS-Go [issue 988](https://github.com/prebid/prebid-server/issues/988), [PR](https://github.com/prebid/prebid-server/pull/989). -1) PBS-Java supports video impression tracking [issue 1015](https://github.com/prebid/prebid-server/issues/1015). PBS-Java [PR 437](https://github.com/rubicon-project/prebid-server-java/pull/437). - -## Minor differences - -- PBS-Java removes null objects or empty strings (e.g. in Go `/auction` response bid object will have field `hb_cache: ""` whereas in Java it will be absent; also `digitrust: null` in PBS Go is not there in PBS Java). PBS-Go [Issue 476](https://github.com/prebid/prebid-server/issues/476) -- All adapters have been ported to use OpenRTB directly in PBS-Java. PBS-Go Facebook AudienceNetwork adapter [Issue 211](https://github.com/prebid/prebid-server/issues/211) -- Java and Go adapter internal interface returns currency in different ways: - - in PBS-Go, the adapter sets BidResponse.currency, which is outside of each TypedBid. - - in PBS-Java, the adapter set BidderBid[N].currency. -- PBS-Go use "60 seconds buffer + {bid,imp,mediaType}TTL" approach to determine caching TTL period. -- PBS-Java has different names for system metrics. For example instead of `active_connections` it uses `vertx.http.servers.[IP]:[PORT].open-netsockets.count`. See [Metrics](metrics.md) for details. -- PBS-Go `/openrtb2/{auction,amp}` returns HTTP 503 Service Unavailable if request has blacklisted account or app, PBS-Java returns HTTP 403 Forbidden. - -## GDPR differences -- PBS-Java supports geo location service interface to determine the country for incoming client request and provides a default implementation using MaxMind GeoLite2 Country database. -- Different checking of purpose IDs (1 - `Storage and access of information`, 3 - `Ad selection, delivery, reporting`): - - for `/auction` endpoint: in PBS-Java - doesn't support GDPR processing. - - for `/openrtb2/{auction,amp}` endpoint: in PBS-Java - 1 and 3 (for each bidder from request); in PBS-Go - doesn't support GDPR processing. - - for `/cookie_sync` endpoint: in PBS-Java - doesn't support GDPR processing; in PBS-Go - only 1 checked. -- PBS-Java allows bidder to enforce GDPR processing. This information available in bidder meta info. diff --git a/docs/endpoints/auction.md b/docs/endpoints/auction.md index ac4f19f1c6a..9eb35f77914 100644 --- a/docs/endpoints/auction.md +++ b/docs/endpoints/auction.md @@ -1,6 +1,6 @@ # Auction The `/auction` endpoint expects a JSON request in to format defined by -[pbs_request.json](https://github.com/rubicon-project/prebid-server-java/blob/master/src/main/resources/static/pbs_request.json). +[pbs_request.json](../../src/main/resources/static/pbs_request.json). This endpoint does not support OpenRTB request, so can be assumed as legacy. 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 f3a041635ca..418377a45cc 100644 --- a/docs/endpoints/info/bidders/bidderName.md +++ b/docs/endpoints/info/bidders/bidderName.md @@ -34,13 +34,13 @@ This endpoint returns JSON like: The fields hold the following information: -- `maintainer.email`: A contact email for the Bidder's maintainer. In general, Bidder bugs should be logged as [issues](https://github.com/rubicon-project/prebid-server-java/issues)... but this contact email may be useful in case of emergency. +- `maintainer.email`: A contact email for the Bidder's maintainer. In general, Bidder bugs should be logged as [issues](https://github.com/prebid/prebid-server-java/issues)... but this contact email may be useful in case of emergency. - `capabilities.app.mediaTypes`: A list of media types this Bidder supports from Mobile Apps. - `capabilities.site.mediaTypes`: A list of media types this Bidder supports from Web pages. 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 ed43a951a1a..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,16 +111,15 @@ 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: -- `request.user.ext.digitrust` -- To support Digitrust support - `request.regs.ext.gdpr` and `request.user.ext.consent` -- To support GDPR - `request.site.ext.amp` -- To identify AMP as the request source - `request.app.ext.source` and `request.app.ext.version` -- To support identifying the displaymanager/SDK in mobile apps. If given, we expect these to be strings. @@ -165,6 +168,7 @@ to set these params on the response at `response.seatbid[i].bid[j].ext.prebid.ta }, "includewinners": false, // Optional param defaulting to true "includebidderkeys": false // Optional param defaulting to true + "includeformat": false // Optional param defaulting to false } } } @@ -179,6 +183,8 @@ For backwards compatibility the following strings will also be allowed as price One of "includewinners" or "includebidderkeys" must be true (both default to true if unset). If both were false, then no targeting keys would be set, which is better configured by omitting targeting altogether. +The parameter "includeformat" indicates the type of the bid (banner, video, etc) for multiformat requests. It will add the key `hb_format` and/or `hb_format_{bidderName}` as per "includewinners" and "includebidderkeys" above. + MediaType PriceGranularity - when a single OpenRTB request contains multiple impressions with different mediatypes, or a single impression supports multiple formats, the different mediatypes may need different price granularities. If `mediatypepricegranularity` is present, `pricegranularity` would only be used for any mediatypes not specified. ``` @@ -287,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 + } + } } } } @@ -322,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 @@ -683,11 +693,7 @@ Prebid Server adapters can support the [Prebid.js User ID modules](http://prebid "source": "pubcommon", "id":"11111111" } - ], - "digitrust": { - "id": "11111111111", - "keyv": 4 - } + ] } } } @@ -759,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. @@ -768,8 +774,8 @@ This supports publishers who want to sell different impressions to different bid This endpoint returns a 400 if the request contains deprecated properties (e.g. `imp.wmin`, `imp.hmax`). The error message in the response should describe how to "fix" the request to make it legal. -If the message is unclear, please [log an issue](https://github.com/rubicon-project/prebid-server-java/issues) -or [submit a pull request](https://github.com/rubicon-project/prebid-server-java/pulls) to improve it. +If the message is unclear, please [log an issue](https://github.com/prebid/prebid-server-java/issues) +or [submit a pull request](https://github.com/prebid/prebid-server-java/pulls) to improve it. #### Determining Bid Security (http/https) diff --git a/docs/metrics.md b/docs/metrics.md index 51521670249..189a5b242c8 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -38,8 +38,6 @@ where `[DATASOURCE]` is a data source name, `DEFAULT_DS` by defaul. ## General auction metrics - `app_requests` - number of requests received from applications - `no_cookie_requests` - number of requests without `uids` cookie or with one that didn't contain at least one live UID -- `safari_requests` - number of requests received from Safari browser -- `safari_no_cookie_requests` - number of requests received from Safari browser without `uids` cookie or with one that didn't contain at least one live UID - `request_time` - timer tracking how long did it take for Prebid Server to serve a request - `imps_requested` - number if impressions requested - `imps_banner` - number of banner impressions @@ -63,6 +61,10 @@ where `[DATASOURCE]` is a data source name, `DEFAULT_DS` by defaul. - `circuit-breaker.geo.opened` - state of the geo location circuit breaker: `1` means opened (geo location resource is unavailable), `0` - closed - `timeout_notification.ok` - number of times bidders were successfully notified about timeouts - `timeout_notification.failed` - number of unsuccessful attempts to notify bidders about timeouts +- `currency-rates.stale` - a flag indicating if currency rates obtained from external source are fresh (`0`) or stale (`1`) +- `settings.cache.(stored-request|amp-stored-request).refresh.(initialize|update).db_query_time` - timer tracking how long was settings cache population +- `settings.cache.(stored-request|amp-stored-request).refresh.(initialize|update).err` - number of errors during settings cache population +- `settings.cache.account.(hit|miss)` - number of times account was found or was missing in cache ## Auction per-adapter metrics - `adapter..no_cookie_requests` - number of requests made to `` that did not contain UID @@ -76,17 +78,21 @@ where `[DATASOURCE]` is a data source name, `DEFAULT_DS` by defaul. - `adapter..(openrtb2-web|openrtb-app|amp|legacy).tcf.geo_masked` - number of requests made to `` that required geo information removed as a result of TCF enforcement for that bidder - `adapter..(openrtb2-web|openrtb-app|amp|legacy).tcf.request_blocked` - number of requests made to `` that were blocked as a result of TCF enforcement for that bidder - `adapter..(openrtb2-web|openrtb-app|amp|legacy).tcf.analytics_blocked` - number of requests made to `` that required analytics blocked as a result of TCF enforcement for that bidder +- `adapter..response.validation.size.(warn|err)` - number of banner bids received from the `` that had invalid size +- `adapter..response.validation.secure.(warn|err)` - number of bids received from the `` that had insecure creative while in secure context ## Auction per-account metrics Following metrics are collected and submitted if account is configured with `basic` verbosity: - `account..requests` - number of requests received from account with `` +- `account..response.validation.size.(warn|err)` - number of banner bids received from account with `` that had invalid size +- `account..response.validation.secure.(warn|err)` - number of bids received from account with `` that had insecure creative while in secure context Following metrics are collected and submitted if account is configured with `detailed` verbosity: - `account..requests.type.(openrtb2-web,openrtb-app,amp,legacy)` - number of requests received from account with `` broken down by type of incoming request -- `account...request_time` - timer tracking how long did it take to make a request to `` when incoming request was from `` -- `account...bids_received` - number of bids received from `` when incoming request was from `` -- `account...requests.(gotbids|nobid)` - number of requests made to `` broken down by result status when incoming request was from `` -- `account..requests.rejected` - number of rejected requests caused by incorrect `accountId` ([UnauthorizedAccountException.java](https://github.com/rubicon-project/prebid-server-java/blob/master/src/main/java/org/prebid/server/exception/UnauthorizedAccountException.java)) +- `account..requests.rejected` - number of rejected requests caused by incorrect `accountId` +- `account..adapter..request_time` - timer tracking how long did it take to make a request to `` when incoming request was from `` +- `account..adapter..bids_received` - number of bids received from `` when incoming request was from `` +- `account..adapter..requests.(gotbids|nobid)` - number of requests made to `` broken down by result status when incoming request was from `` ## General Prebid Cache metrics - `prebid_cache.requests.ok` - timer tracking how long did successful cache requests take @@ -112,9 +118,12 @@ Following metrics are collected and submitted if account is configured with `det ## Privacy metrics - `privacy.tcf.(missing|invalid)` - number of requests lacking a valid consent string +- `privacy.tcf.(v1,v2).requests` - number of requests by TCF version - `privacy.tcf.(v1,v2).unknown-geo` - number of requests received from unknown geo region with consent string of particular version - `privacy.tcf.(v1,v2).in-geo` - number of requests received from TCF-concerned geo region with consent string of particular version - `privacy.tcf.(v1,v2).out-geo` - number of requests received outside of TCF-concerned geo region with consent string of particular version - `privacy.tcf.(v1,v2).vendorlist.(missing|ok|err|fallback)` - number of processed vendor lists of particular version - `privacy.usp.specified` - number of requests with a valid US Privacy string (CCPA) - `privacy.usp.opt-out` - number of requests that required privacy enforcement according to CCPA rules +- `privacy.lmt` - number of requests that required privacy enforcement according to LMT flag +- `privacy.coppa` - number of requests that required privacy enforcement according to COPPA rules diff --git a/docs/pbs-java-and-go-features-review.md b/docs/pbs-java-and-go-features-review.md deleted file mode 100644 index 44eeae86428..00000000000 --- a/docs/pbs-java-and-go-features-review.md +++ /dev/null @@ -1,42 +0,0 @@ -# Feature Differences Overview - -[Detailed Differences Description](differenceBetweenPBSGo-and-Java.md) - - Feature | Java | Go -| --- | :---: | :---:| -GDPR TCF1.1 |+|+ -GDPR TCF2 |+|+ -Geo location (used for GDPR) |+|- -COPPA |+|+ -CCPA |+|+ -AMP |+|+ -Stored Requests |+|+ -Stored Responses |+|- -PBJS First Party data |+|- -Currency Conversion** |+|+ -Multiple root schains |+|- -Price Granularity |+|+ -Price Granularity per MediaType|+|- -User ID Module support |+|+ -Account exclude list |+|+ -Event Notification endpoint |+|- -Video ad support |+|+ -Long-form video endpoint |-|+ -IAB advertiser category mapping |-|+ -Aliases |+|+ -Video Impression Tracking endpoint |+|- -Cooperative Cookie Syncing |+|- -Circuit Breaker (Http, DB) |+|- -Operational metrics |+|+ -Supports both "debug" and "test" flags |+|- -All adapters ported to OpenRTB |+|- -Echo stored request video attributes in response |+|- -Accept account ID on AMP requests |+|- -Cache only-winning-bids flag |+|- -Remote File Downloader |+|- -Bidder Generator |+|- -Passing `request.ext.prebid.bidders.BIDDER` to corresponding bidder |+|- - -** -* PBS-Java Currency conversion supports finding intermediate conversion rate; -* PBS-Go Currency Conversion debug endpoint exposes more information, PBS-Java currently provides last updated time only; diff --git a/pom.xml b/pom.xml index c49b9e56288..c69db5884b4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.prebid prebid-server - 1.50.0-SNAPSHOT + 1.55.0-SNAPSHOT prebid-server Prebid Server (Server-side Header Bidding) @@ -14,7 +14,7 @@ - scm:git:git@github.com:rubicon-project/prebid-server-java.git + scm:git:git@github.com:prebid/prebid-server-java.git HEAD @@ -55,7 +55,7 @@ 2.23.4 3.8.0 2.26.3 - 9.4.34.v20201102 + 9.4.35.v20201120 3.0.6 1.4.196 @@ -235,6 +235,11 @@ metrics-core ${metrics.version} + + io.dropwizard.metrics + metrics-jvm + ${metrics.version} + io.dropwizard.metrics metrics-graphite diff --git a/src/main/java/org/prebid/server/analytics/LogAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/LogAnalyticsReporter.java index 7fc108f1d42..3ca358a9cca 100644 --- a/src/main/java/org/prebid/server/analytics/LogAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/LogAnalyticsReporter.java @@ -29,22 +29,26 @@ public LogAnalyticsReporter(JacksonMapper mapper) { @Override public void processEvent(T event) { - final String type; + final LogEvent logEvent; + if (event instanceof AuctionEvent) { - type = "/openrtb2/auction"; + logEvent = new LogEvent<>("/openrtb2/auction", ((AuctionEvent) event).getBidResponse()); } else if (event instanceof AmpEvent) { - type = "/openrtb2/amp"; + logEvent = new LogEvent<>("/openrtb2/amp", ((AmpEvent) event).getBidResponse()); } else if (event instanceof VideoEvent) { - type = "/openrtb2/video"; + logEvent = new LogEvent<>("/openrtb2/video", ((VideoEvent) event).getBidResponse()); } else if (event instanceof SetuidEvent) { - type = "/setuid"; + final SetuidEvent setuidEvent = (SetuidEvent) event; + logEvent = new LogEvent<>( + "/setuid", + setuidEvent.getBidder() + ":" + setuidEvent.getUid() + ":" + setuidEvent.getSuccess()); } else if (event instanceof CookieSyncEvent) { - type = "/cookie_sync"; + logEvent = new LogEvent<>("/cookie_sync", ((CookieSyncEvent) event).getBidderStatus()); } else { - type = "unknown"; + logEvent = new LogEvent<>("unknown", null); } - logger.debug(mapper.encode(new LogEvent<>(type, event))); + logger.debug(mapper.encode(logEvent)); } @AllArgsConstructor diff --git a/src/main/java/org/prebid/server/auction/AmpRequestFactory.java b/src/main/java/org/prebid/server/auction/AmpRequestFactory.java index 8aa778542b2..9d07d648978 100644 --- a/src/main/java/org/prebid/server/auction/AmpRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/AmpRequestFactory.java @@ -249,8 +249,7 @@ private static Integer debugFromQueryStringParam(RoutingContext context) { /** * Extracts parameters from http request and overrides corresponding attributes in {@link BidRequest}. */ - private BidRequest overrideParameters(BidRequest bidRequest, HttpServerRequest request, - List errors) { + private BidRequest overrideParameters(BidRequest bidRequest, HttpServerRequest request, List errors) { final String requestConsentParam = request.getParam(CONSENT_PARAM); final String requestGdprConsentParam = request.getParam(GDPR_CONSENT_PARAM); final String consentString = ObjectUtils.firstNonNull(requestConsentParam, requestGdprConsentParam); @@ -341,6 +340,11 @@ private Site overrideSite(Site site, HttpServerRequest request) { final Site.SiteBuilder siteBuilder = hasSite ? site.toBuilder() : Site.builder(); if (StringUtils.isNotBlank(canonicalUrl)) { siteBuilder.page(canonicalUrl); + + final String domain = HttpUtil.getDomainFromUrl(canonicalUrl); + if (StringUtils.isNotBlank(domain)) { + siteBuilder.domain(domain); + } } if (StringUtils.isNotBlank(accountId)) { final Publisher publisher = hasSite ? site.getPublisher() : null; @@ -635,11 +639,14 @@ private ExtRequestTargeting createTargetingWithDefaults(ExtRequestPrebid prebid) final boolean includeBidderKeys = isTargetingNull || targeting.getIncludebidderkeys() == null || targeting.getIncludebidderkeys(); - return ExtRequestTargeting.builder() + final Boolean includeFormat = !isTargetingNull ? targeting.getIncludeformat() : null; + + return (isTargetingNull ? ExtRequestTargeting.builder() : targeting.toBuilder()) .pricegranularity(outgoingPriceGranularityNode) .mediatypepricegranularity(mediaTypePriceGranularity) .includewinners(includeWinners) .includebidderkeys(includeBidderKeys) + .includeformat(includeFormat) .build(); } } diff --git a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java index f4eff501dd3..c191be47ba8 100644 --- a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java @@ -11,7 +11,6 @@ import com.iab.openrtb.request.Publisher; import com.iab.openrtb.request.Site; import com.iab.openrtb.request.Source; -import com.iab.openrtb.request.User; import io.netty.buffer.ByteBufInputStream; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; @@ -49,17 +48,18 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting; import org.prebid.server.proto.openrtb.ext.request.ExtSite; -import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.settings.ApplicationSettings; 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; @@ -71,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,8 +81,14 @@ public class AuctionRequestFactory { private static final ConditionalLogger EMPTY_ACCOUNT_LOGGER = new ConditionalLogger("empty_account", logger); private static final ConditionalLogger UNKNOWN_ACCOUNT_LOGGER = new ConditionalLogger("unknown_account", logger); - public static final String WEB_CHANNEL = "web"; - public static final String APP_CHANNEL = "app"; + 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; @@ -102,7 +106,7 @@ public class AuctionRequestFactory { private final TimeoutResolver timeoutResolver; private final TimeoutFactory timeoutFactory; private final ApplicationSettings applicationSettings; - private final IdGenerator idGenerator; + private final IdGenerator sourceIdGenerator; private final PrivacyEnforcementService privacyEnforcementService; private final JacksonMapper mapper; private final OrtbTypesResolver ortbTypesResolver; @@ -124,7 +128,7 @@ public AuctionRequestFactory(long maxRequestSize, TimeoutResolver timeoutResolver, TimeoutFactory timeoutFactory, ApplicationSettings applicationSettings, - IdGenerator idGenerator, + IdGenerator sourceIdGenerator, PrivacyEnforcementService privacyEnforcementService, JacksonMapper mapper) { @@ -145,7 +149,7 @@ public AuctionRequestFactory(long maxRequestSize, this.timeoutResolver = Objects.requireNonNull(timeoutResolver); this.timeoutFactory = Objects.requireNonNull(timeoutFactory); this.applicationSettings = Objects.requireNonNull(applicationSettings); - this.idGenerator = Objects.requireNonNull(idGenerator); + this.sourceIdGenerator = Objects.requireNonNull(sourceIdGenerator); this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); this.mapper = Objects.requireNonNull(mapper); } @@ -200,7 +204,7 @@ Future toAuctionContext(RoutingContext routingContext, return accountFrom(bidRequest, timeout, routingContext) .compose(account -> privacyEnforcementService.contextFromBidRequest( - bidRequest, account, requestTypeMetric, timeout) + bidRequest, account, requestTypeMetric, timeout, errors) .map(privacyContext -> AuctionContext.builder() .routingContext(routingContext) .uidsCookie(uidsCookieService.parseFromRequest(routingContext)) @@ -277,9 +281,6 @@ BidRequest fillImplicitParameters(BidRequest bidRequest, RoutingContext context, final Site site = bidRequest.getSite(); final Site populatedSite = bidRequest.getApp() != null ? null : populateSite(site, request); - final User user = bidRequest.getUser(); - final User populatedUser = populateUser(user); - final Source source = bidRequest.getSource(); final Source populatedSource = populateSource(source); @@ -299,14 +300,13 @@ BidRequest fillImplicitParameters(BidRequest bidRequest, RoutingContext context, final ExtRequest populatedExt = populateRequestExt( ext, bidRequest, ObjectUtils.defaultIfNull(populatedImps, imps)); - if (populatedDevice != null || populatedSite != null || populatedUser != null || populatedSource != null + if (populatedDevice != null || populatedSite != null || populatedSource != null || populatedImps != null || resolvedAt != null || resolvedCurrencies != null || resolvedTmax != null || populatedExt != null) { result = bidRequest.toBuilder() .device(populatedDevice != null ? populatedDevice : device) .site(populatedSite != null ? populatedSite : site) - .user(populatedUser != null ? populatedUser : user) .source(populatedSource != null ? populatedSource : source) .imp(populatedImps != null ? populatedImps : imps) .at(resolvedAt != null ? resolvedAt : at) @@ -410,71 +410,37 @@ private void logWarnIfNoIp(String resolvedIp, String resolvedIpv6) { * and the request contains necessary info (domain, page). */ private Site populateSite(Site site, HttpServerRequest request) { - Site result = null; + final String page = site != null ? StringUtils.trimToNull(site.getPage()) : null; + final String updatedPage = page == null ? paramsExtractor.refererFrom(request) : null; - final String page = site != null ? site.getPage() : null; - final String domain = site != null ? site.getDomain() : null; - final ExtSite siteExt = site != null ? site.getExt() : null; - final ObjectNode data = siteExt != null ? siteExt.getData() : null; - final boolean shouldSetExtAmp = siteExt == null || siteExt.getAmp() == null; - final ExtSite modifiedSiteExt = shouldSetExtAmp - ? ExtSite.of(0, data) + final String domain = site != null ? StringUtils.trimToNull(site.getDomain()) : null; + final String updatedDomain = domain == null + ? getDomainOrNull(ObjectUtils.defaultIfNull(updatedPage, page)) : null; - String referer = null; - String parsedDomain = null; - if (StringUtils.isBlank(page) || StringUtils.isBlank(domain)) { - referer = paramsExtractor.refererFrom(request); - if (StringUtils.isNotBlank(referer)) { - try { - parsedDomain = paramsExtractor.domainFrom(referer); - } catch (PreBidException e) { - logger.warn("Error occurred while populating bid request: {0}", e.getMessage()); - logger.debug("Error occurred while populating bid request", e); - } - } - } - final boolean shouldModifyPageOrDomain = referer != null && parsedDomain != null; - - if (shouldModifyPageOrDomain || shouldSetExtAmp) { - final Site.SiteBuilder builder = site == null ? Site.builder() : site.toBuilder(); - if (shouldModifyPageOrDomain) { - builder.domain(StringUtils.isNotBlank(domain) ? domain : parsedDomain); - builder.page(StringUtils.isNotBlank(page) ? page : referer); - } - if (shouldSetExtAmp) { - builder.ext(modifiedSiteExt); - } - result = builder.build(); - } - return result; - } - - /** - * Populates the request body's 'user' section from the incoming http request if the original is partially filled. - */ - private User populateUser(User user) { - final ExtUser ext = userExtOrNull(user); + final ExtSite siteExt = site != null ? site.getExt() : null; + final ExtSite updatedSiteExt = siteExt == null || siteExt.getAmp() == null + ? ExtSite.of(0, getIfNotNull(siteExt, ExtSite::getData)) + : null; - if (ext != null) { - return user.toBuilder().ext(ext).build(); + if (updatedPage != null || updatedDomain != null || updatedSiteExt != null) { + return (site == null ? Site.builder() : site.toBuilder()) + // do not set page if domain was not parsed successfully + .page(domain == null && updatedDomain == null ? page : ObjectUtils.defaultIfNull(updatedPage, page)) + .domain(ObjectUtils.defaultIfNull(updatedDomain, domain)) + .ext(ObjectUtils.defaultIfNull(updatedSiteExt, siteExt)) + .build(); } return null; } - /** - * Returns updated {@link ExtUser} or null if no updates needed. - */ - private ExtUser userExtOrNull(User user) { - final ExtUser extUser = user != null ? user.getExt() : null; - - final ExtUserDigiTrust digitrust = extUser != null ? extUser.getDigitrust() : null; - if (digitrust != null && digitrust.getPref() == null) { - return extUser.toBuilder() - .digitrust(ExtUserDigiTrust.of(digitrust.getId(), digitrust.getKeyv(), 0)) - .build(); + private String getDomainOrNull(String url) { + try { + return paramsExtractor.domainFrom(url); + } catch (PreBidException e) { + logger.warn("Error occurred while populating bid request: {0}", e.getMessage()); + return null; } - return null; } /** @@ -483,7 +449,7 @@ private ExtUser userExtOrNull(User user) { private Source populateSource(Source source) { final String tid = source != null ? source.getTid() : null; if (StringUtils.isEmpty(tid)) { - final String generatedId = idGenerator.generateId(); + final String generatedId = sourceIdGenerator.generateId(); if (StringUtils.isNotEmpty(generatedId)) { final Source.SourceBuilder builder = source != null ? source.toBuilder() : Source.builder(); return builder @@ -495,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; + } - 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()); + final Integer secureFromRequest = paramsExtractor.secureFrom(request); + + 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(); } /** @@ -591,21 +632,17 @@ private ExtRequestTargeting targetingOrNull(ExtRequestPrebid prebid, Set impMediaTypes) { - final JsonNode priceGranularityNode = targeting.getPricegranularity(); + private JsonNode resolvePriceGranularity(ExtRequestTargeting targeting, boolean isPriceGranularityNull, + boolean isPriceGranularityTextual, Set impMediaTypes) { final boolean hasAllMediaTypes = checkExistingMediaTypes(targeting.getMediatypepricegranularity()) .containsAll(impMediaTypes); @@ -633,6 +669,8 @@ private JsonNode populatePriceGranularity(ExtRequestTargeting targeting, boolean if (isPriceGranularityNull && !hasAllMediaTypes) { return mapper.mapper().valueToTree(ExtPriceGranularity.from(PriceGranularity.DEFAULT)); } + + final JsonNode priceGranularityNode = targeting.getPricegranularity(); if (isPriceGranularityTextual) { final PriceGranularity priceGranularity; try { @@ -642,6 +680,7 @@ private JsonNode populatePriceGranularity(ExtRequestTargeting targeting, boolean } return mapper.mapper().valueToTree(ExtPriceGranularity.from(priceGranularity)); } + return priceGranularityNode; } @@ -681,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() @@ -697,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. */ @@ -760,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; } @@ -808,7 +849,8 @@ private Future accountFrom(BidRequest bidRequest, Timeout timeout, Rout return blankAccountId ? responseForEmptyAccount(routingContext) : applicationSettings.getAccountById(accountId, timeout) - .recover(exception -> accountFallback(exception, accountId, routingContext)); + .compose(this::ensureAccountActive, + exception -> accountFallback(exception, accountId, routingContext)); } /** @@ -874,6 +916,15 @@ private Future responseForUnknownAccount(String accountId) { : Future.succeededFuture(Account.empty(accountId)); } + private Future ensureAccountActive(Account account) { + final String accountId = account.getId(); + + return account.getStatus() == AccountStatus.inactive + ? Future.failedFuture(new UnauthorizedAccountException( + String.format("Account %s is inactive", accountId), accountId)) + : Future.succeededFuture(account); + } + private BidRequest enrichBidRequestWithAccountAndPrivacyData( BidRequest bidRequest, Account account, PrivacyContext privacyContext) { diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index faef570fc6f..16a572afbc9 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -26,6 +26,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRequestCacheInfo; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.model.GeneratedBidIds; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -40,6 +41,8 @@ import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.Timeout; +import org.prebid.server.identity.IdGenerator; +import org.prebid.server.identity.IdGeneratorType; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.ExtPrebid; @@ -72,7 +75,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; @@ -81,7 +83,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -100,7 +101,8 @@ public class BidResponseCreator { private final BidderCatalog bidderCatalog; private final EventsService eventsService; private final StoredRequestProcessor storedRequestProcessor; - private final boolean generateBidId; + private final BidResponseReducer bidResponseReducer; + private final IdGenerator bidIdGenerator; private final int truncateAttrChars; private final Clock clock; private final JacksonMapper mapper; @@ -113,7 +115,8 @@ public BidResponseCreator(CacheService cacheService, BidderCatalog bidderCatalog, EventsService eventsService, StoredRequestProcessor storedRequestProcessor, - boolean generateBidId, + BidResponseReducer bidResponseReducer, + IdGenerator bidIdGenerator, int truncateAttrChars, Clock clock, JacksonMapper mapper) { @@ -122,7 +125,8 @@ public BidResponseCreator(CacheService cacheService, this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.eventsService = Objects.requireNonNull(eventsService); this.storedRequestProcessor = Objects.requireNonNull(storedRequestProcessor); - this.generateBidId = generateBidId; + this.bidResponseReducer = Objects.requireNonNull(bidResponseReducer); + this.bidIdGenerator = Objects.requireNonNull(bidIdGenerator); this.truncateAttrChars = validateTruncateAttrChars(truncateAttrChars); this.clock = Objects.requireNonNull(clock); this.mapper = Objects.requireNonNull(mapper); @@ -173,7 +177,6 @@ private static int validateTruncateAttrChars(int truncateAttrChars) { if (truncateAttrChars < 0 || truncateAttrChars > 255) { throw new IllegalArgumentException("truncateAttrChars must be between 0 and 255"); } - return truncateAttrChars; } @@ -194,20 +197,27 @@ private Future cacheBidsAndCreateResponse(List bidd final BidRequest bidRequest = auctionContext.getBidRequest(); - bidderResponses.forEach(BidResponseCreator::removeRedundantBids); + final List updatedBidderResponses = bidderResponses.stream() + .map(bidResponseReducer::removeRedundantBids) + .collect(Collectors.toList()); ExtRequestTargeting targeting = targeting(bidRequest); final Set winningBids = newOrEmptySet(targeting); final Set winningBidsByBidder = newOrEmptySet(targeting); + final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(bidderResponses, + (ignored, bid) -> bidIdGenerator.getType() != IdGeneratorType.none + ? bidIdGenerator.generateId() + : bid.getId()); + // determine winning bids only if targeting is present if (targeting != null) { - populateWinningBids(bidderResponses, winningBids, winningBidsByBidder); + populateWinningBids(updatedBidderResponses, winningBids, winningBidsByBidder); } final Set bidsToCache = cacheInfo.isShouldCacheWinningBidsOnly() ? winningBids - : bidderResponses.stream().flatMap(BidResponseCreator::getBids).collect(Collectors.toSet()); + : updatedBidderResponses.stream().flatMap(BidResponseCreator::getBids).collect(Collectors.toSet()); final EventsContext eventsContext = EventsContext.builder() .enabledForAccount(eventsEnabledForAccount(auctionContext)) @@ -217,20 +227,22 @@ private Future cacheBidsAndCreateResponse(List bidd .build(); return toBidsWithCacheIds( - bidderResponses, + updatedBidderResponses, bidsToCache, + generatedBidIds, auctionContext, cacheInfo, eventsContext) .compose(cacheResult -> videoStoredDataResult(auctionContext) .map(videoStoredDataResult -> toBidResponse( - bidderResponses, + updatedBidderResponses, auctionContext, targeting, winningBids, winningBidsByBidder, cacheInfo, cacheResult, + generatedBidIds, videoStoredDataResult, eventsContext, auctionTimestamp, @@ -278,34 +290,6 @@ private ExtBidResponse toExtBidResponse(List bidderResponses, ExtBidResponsePrebid.of(auctionTimestamp)); } - private static void removeRedundantBids(BidderResponse bidderResponse) { - final List responseBidderBids = bidderResponse.getSeatBid().getBids(); - final Map> impIdToBidderBid = responseBidderBids.stream() - .collect(Collectors.groupingBy(bidderBid -> bidderBid.getBid().getImpid())); - - final List mostValuableBids = impIdToBidderBid.values().stream() - .map(BidResponseCreator::mostValuableBid) - .collect(Collectors.toList()); - - responseBidderBids.retainAll(mostValuableBids); - } - - private static BidderBid mostValuableBid(List bidderBids) { - if (bidderBids.size() == 1) { - return bidderBids.get(0); - } - - final List dealBidderBids = bidderBids.stream() - .filter(bidderBid -> StringUtils.isNotBlank(bidderBid.getBid().getDealid())) - .collect(Collectors.toList()); - - List processedBidderBids = dealBidderBids.isEmpty() ? bidderBids : dealBidderBids; - - return processedBidderBids.stream() - .max(Comparator.comparing(bidderBid -> bidderBid.getBid().getPrice(), Comparator.naturalOrder())) - .orElse(bidderBids.get(0)); - } - /** * Returns new {@link HashSet} in case of existing keywordsCreator or empty collection if null. */ @@ -394,6 +378,7 @@ private static Stream getBids(BidderResponse bidderResponse) { */ private Future toBidsWithCacheIds(List bidderResponses, Set bidsToCache, + GeneratedBidIds generatedBidIds, AuctionContext auctionContext, BidRequestCacheInfo cacheInfo, EventsContext eventsContext) { @@ -409,22 +394,19 @@ private Future toBidsWithCacheIds(List bidde final boolean shouldCacheVideoBids = cacheInfo.isShouldCacheVideoBids(); - final Map> bidderToVideoBidIdsToModify = + final GeneratedBidIds bidderToVideoGeneratedBidIdsToModify = shouldCacheVideoBids && eventsEnabledForAccount(auctionContext) - ? getBidderAndVideoBidIdsToModify(bidderResponses, auctionContext.getBidRequest().getImp()) - : Collections.emptyMap(); - final Map> bidderToBidIds = bidderResponses.stream() - .collect(Collectors.toMap(BidderResponse::getBidder, bidderResponse -> getBids(bidderResponse) - .map(Bid::getId) - .collect(Collectors.toList()))); + ? getGeneratedVideoBidIds(bidderResponses, generatedBidIds, + auctionContext.getBidRequest().getImp()) + : GeneratedBidIds.empty(); final CacheContext cacheContext = CacheContext.builder() .cacheBidsTtl(cacheInfo.getCacheBidsTtl()) .cacheVideoBidsTtl(cacheInfo.getCacheVideoBidsTtl()) .shouldCacheBids(cacheInfo.isShouldCacheBids()) .shouldCacheVideoBids(shouldCacheVideoBids) - .bidderToVideoBidIdsToModify(bidderToVideoBidIdsToModify) - .bidderToBidIds(bidderToBidIds) + .bidderToVideoGeneratedBidIdsToModify(bidderToVideoGeneratedBidIdsToModify) + .bidderToBidsToGeneratedIds(generatedBidIds) .build(); return cacheService.cacheBidsOpenrtb(bidsValidToBeCached, auctionContext, cacheContext, eventsContext) @@ -436,22 +418,40 @@ private static boolean isValidForCaching(Bid bid) { return bid.getDealid() != null ? price.compareTo(BigDecimal.ZERO) >= 0 : price.compareTo(BigDecimal.ZERO) > 0; } - private Map> getBidderAndVideoBidIdsToModify(List bidderResponses, - List imps) { + private GeneratedBidIds getGeneratedVideoBidIds(List bidderResponses, + GeneratedBidIds generatedBidIds, + List imps) { - return bidderResponses.stream() + final List vastModifyAllowedResponses = bidderResponses.stream() .filter(bidderResponse -> bidderCatalog.isModifyingVastXmlAllowed(bidderResponse.getBidder())) - .collect(Collectors.toMap(BidderResponse::getBidder, bidderResponse -> getBids(bidderResponse) - .filter(bid -> isVideoBid(bid, imps)) - .map(Bid::getId) - .collect(Collectors.toList()))); + .map(bidderResponse -> makeVideoBidsBidderResponse(bidderResponse, imps)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + return GeneratedBidIds.of(vastModifyAllowedResponses, + (bidder, bid) -> generatedBidIds.getGeneratedId(bidder, bid.getId(), bid.getImpid())); } - private static boolean isVideoBid(Bid bid, List imps) { + private static BidderResponse makeVideoBidsBidderResponse(BidderResponse bidderResponse, List imps) { + final List videoBidderBids = bidderResponse.getSeatBid().getBids().stream() + .filter(bidderBid -> isVideoBid(bidderBid, imps)) + .collect(Collectors.toList()); + + final BidderSeatBid bidderSeatBid = bidderResponse.getSeatBid(); + + return CollectionUtils.isNotEmpty(videoBidderBids) + ? BidderResponse.of( + bidderResponse.getBidder(), + BidderSeatBid.of(videoBidderBids, bidderSeatBid.getHttpCalls(), bidderSeatBid.getErrors()), + bidderResponse.getResponseTime()) + : null; + } + + private static boolean isVideoBid(BidderBid bidderBid, List imps) { return imps.stream() .filter(imp -> imp.getVideo() != null) .map(Imp::getId) - .anyMatch(impId -> bid.getImpid().equals(impId)); + .anyMatch(impId -> bidderBid.getBid().getImpid().equals(impId)); } /** @@ -512,11 +512,10 @@ private Map> toExtBidderErrors(List CacheServiceResult cacheResult, VideoStoredDataResult videoStoredDataResult, Map> bidErrors) { - final BidRequest bidRequest = auctionContext.getBidRequest(); final Map> errors = new HashMap<>(); errors.putAll(extractBidderErrors(bidderResponses)); - errors.putAll(extractDeprecatedBiddersErrors(bidRequest)); + errors.putAll(extractDeprecatedBiddersErrors(auctionContext.getBidRequest())); errors.putAll(extractPrebidErrors(videoStoredDataResult, auctionContext)); errors.putAll(extractCacheErrors(cacheResult)); if (MapUtils.isNotEmpty(bidErrors)) { @@ -656,6 +655,7 @@ private BidResponse toBidResponse(List bidderResponses, Set winningBidsByBidder, BidRequestCacheInfo requestCacheInfo, CacheServiceResult cacheResult, + GeneratedBidIds generatedBidIds, VideoStoredDataResult videoStoredDataResult, EventsContext eventsContext, long auctionTimestamp, @@ -675,6 +675,7 @@ private BidResponse toBidResponse(List bidderResponses, winningBidsByBidder, requestCacheInfo, cacheResult.getCacheBids(), + generatedBidIds, videoStoredDataResult, account, bidErrors, @@ -751,6 +752,7 @@ private SeatBid toSeatBid(BidderResponse bidderResponse, Set winningBidsByBidder, BidRequestCacheInfo requestCacheInfo, Map bidToCacheInfo, + GeneratedBidIds generatedBidIds, VideoStoredDataResult videoStoredDataResult, Account account, Map> bidErrors, @@ -768,6 +770,7 @@ private SeatBid toSeatBid(BidderResponse bidderResponse, winningBidsByBidder, requestCacheInfo, bidToCacheInfo, + generatedBidIds, videoStoredDataResult.getImpIdToStoredVideo(), account, eventsContext, @@ -793,6 +796,7 @@ private Bid toBid(BidderBid bidderBid, Set winningBidsByBidder, BidRequestCacheInfo requestCacheInfo, Map bidsWithCacheIds, + GeneratedBidIds generatedBidIds, Map impIdToStoredVideo, Account account, EventsContext eventsContext, @@ -831,7 +835,8 @@ private Bid toBid(BidderBid bidderBid, final TargetingKeywordsCreator keywordsCreator = resolveKeywordsCreator(bidType, targeting, isApp, bidRequest, account); - targetingKeywords = keywordsCreator.makeFor(bid, bidder, isWinningBid, cacheId, videoCacheId); + targetingKeywords = keywordsCreator.makeFor(bid, bidder, isWinningBid, cacheId, bidType.getName(), + videoCacheId); } else { targetingKeywords = null; } @@ -840,14 +845,13 @@ private Bid toBid(BidderBid bidderBid, final CacheAsset vastXml = videoCacheId != null ? toCacheAsset(videoCacheId) : null; final ExtResponseCache cache = bids != null || vastXml != null ? ExtResponseCache.of(bids, vastXml) : null; - final String generatedBidId = generateBidId ? UUID.randomUUID().toString() : null; - final String eventBidId = ObjectUtils.defaultIfNull(generatedBidId, bid.getId()); + final String generatedBidId = generatedBidIds.getGeneratedId(bidder, bid.getId(), bid.getImpid()); final Video storedVideo = impIdToStoredVideo.get(bid.getImpid()); - final Events events = createEvents(bidder, account, eventBidId, eventsContext); + final Events events = createEvents(bidder, account, generatedBidId, eventsContext); final ExtBidPrebidVideo extBidPrebidVideo = getExtBidPrebidVideo(bid.getExt()); final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder() - .bidid(generatedBidId) + .bidid(bidIdGenerator.getType() != IdGeneratorType.none ? generatedBidId : null) .type(bidType) .targeting(targetingKeywords) .cache(cache) @@ -981,8 +985,9 @@ private TargetingKeywordsCreator resolveKeywordsCreator(BidType bidType, boolean isApp, BidRequest bidRequest, Account account) { - final Map keywordsCreatorByBidType = - keywordsCreatorByBidType(targeting, isApp, bidRequest, account); + + final Map keywordsCreatorByBidType = keywordsCreatorByBidType(targeting, + isApp, bidRequest, account); return keywordsCreatorByBidType.getOrDefault(bidType, keywordsCreator(targeting, isApp, bidRequest, account)); } @@ -991,8 +996,10 @@ private TargetingKeywordsCreator resolveKeywordsCreator(BidType bidType, * Extracts targeting keywords settings from the bid request and creates {@link TargetingKeywordsCreator} * instance if it is present. */ - private TargetingKeywordsCreator keywordsCreator( - ExtRequestTargeting targeting, boolean isApp, BidRequest bidRequest, Account account) { + private TargetingKeywordsCreator keywordsCreator(ExtRequestTargeting targeting, + boolean isApp, + BidRequest bidRequest, + Account account) { final JsonNode priceGranularityNode = targeting.getPricegranularity(); return priceGranularityNode == null || priceGranularityNode.isNull() @@ -1010,7 +1017,6 @@ private Map keywordsCreatorByBidType(ExtReque Account account) { final ExtMediaTypePriceGranularity mediaTypePriceGranularity = targeting.getMediatypepricegranularity(); - if (mediaTypePriceGranularity == null) { return Collections.emptyMap(); } @@ -1048,6 +1054,7 @@ private TargetingKeywordsCreator createKeywordsCreator(ExtRequestTargeting targe parsePriceGranularity(priceGranularity), targeting.getIncludewinners(), targeting.getIncludebidderkeys(), + BooleanUtils.isTrue(targeting.getIncludeformat()), isApp, resolveTruncateAttrChars(targeting, account), cacheHost, @@ -1093,7 +1100,6 @@ private CacheAsset toCacheAsset(String cacheId) { private String integrationFrom(AuctionContext auctionContext) { final ExtRequest extRequest = auctionContext.getBidRequest().getExt(); final ExtRequestPrebid prebid = extRequest == null ? null : extRequest.getPrebid(); - return prebid != null ? prebid.getIntegration() : null; } diff --git a/src/main/java/org/prebid/server/auction/BidResponseReducer.java b/src/main/java/org/prebid/server/auction/BidResponseReducer.java new file mode 100644 index 00000000000..5d1635abdd2 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/BidResponseReducer.java @@ -0,0 +1,85 @@ +package org.prebid.server.auction; + +import com.iab.openrtb.response.Bid; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderSeatBid; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Class for removing bids from response for the same bidder-imp pair. + */ +public class BidResponseReducer { + + /** + * Removes {@link Bid}s with the same impId taking into account if {@link Bid} has deal. + *

+ * Returns given list of {@link BidderResponse}s if {@link Bid}s have different impIds. + */ + public BidderResponse removeRedundantBids(BidderResponse bidderResponse) { + final List bidderBids = ListUtils.emptyIfNull(bidderResponse.getSeatBid().getBids()); + final Map> impIdToBidderBids = bidderBids.stream() + .collect(Collectors.groupingBy(bidderBid -> bidderBid.getBid().getImpid())); + + final Set updatedBidderBids = impIdToBidderBids.values().stream() + .map(BidResponseReducer::removeRedundantBidsForImp) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + if (bidderBids.size() == updatedBidderBids.size()) { + return bidderResponse; + } + + return updateBidderResponse(bidderResponse, updatedBidderBids); + } + + private static List removeRedundantBidsForImp(List bidderBids) { + return bidderBids.size() > 1 ? reduceBidsByImpId(bidderBids) : bidderBids; + } + + private static List reduceBidsByImpId(List bidderBids) { + return bidderBids.stream().anyMatch(bidderBid -> bidderBid.getBid().getDealid() != null) + ? removeRedundantDealsBids(bidderBids) + : removeRedundantForNonDealBids(bidderBids); + } + + private static List removeRedundantDealsBids(List bidderBids) { + final List dealBidderBids = bidderBids.stream() + .filter(bidderBid -> StringUtils.isNotBlank(bidderBid.getBid().getDealid())) + .collect(Collectors.toList()); + + return Collections.singletonList(getHighestPriceBid(bidderBids, dealBidderBids)); + } + + private static List removeRedundantForNonDealBids(List bidderBids) { + return Collections.singletonList(getHighestPriceBid(bidderBids, bidderBids)); + } + + private static BidderBid getHighestPriceBid(List bidderBids, List dealBidderBids) { + return dealBidderBids.stream() + .max(Comparator.comparing(bidderBid -> bidderBid.getBid().getPrice(), Comparator.naturalOrder())) + .orElse(bidderBids.get(0)); + } + + private static BidderResponse updateBidderResponse(BidderResponse bidderResponse, + Set updatedBidderBids) { + + final BidderSeatBid seatBid = bidderResponse.getSeatBid(); + final BidderSeatBid updatedSeatBid = BidderSeatBid.of( + new ArrayList<>(updatedBidderBids), + seatBid.getHttpCalls(), + seatBid.getErrors()); + + return BidderResponse.of(bidderResponse.getBidder(), updatedSeatBid, bidderResponse.getResponseTime()); + } +} diff --git a/src/main/java/org/prebid/server/auction/BidderAliases.java b/src/main/java/org/prebid/server/auction/BidderAliases.java index ed05653f02b..0d00adcc392 100644 --- a/src/main/java/org/prebid/server/auction/BidderAliases.java +++ b/src/main/java/org/prebid/server/auction/BidderAliases.java @@ -1,9 +1,9 @@ package org.prebid.server.auction; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.bidder.BidderCatalog; -import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -21,8 +21,8 @@ public class BidderAliases { private BidderAliases( Map aliasToBidder, Map aliasToVendorId, BidderCatalog bidderCatalog) { - this.aliasToBidder = ObjectUtils.firstNonNull(aliasToBidder, Collections.emptyMap()); - this.aliasToVendorId = ObjectUtils.firstNonNull(aliasToVendorId, Collections.emptyMap()); + this.aliasToBidder = MapUtils.emptyIfNull(aliasToBidder); + this.aliasToVendorId = MapUtils.emptyIfNull(aliasToVendorId); this.bidderCatalog = Objects.requireNonNull(bidderCatalog); } @@ -43,7 +43,7 @@ public boolean isAliasDefined(String alias) { public String resolveBidder(String aliasOrBidder) { return aliasToBidder.containsKey(aliasOrBidder) ? aliasToBidder.get(aliasOrBidder) - : ObjectUtils.firstNonNull(resolveBidderViaCatalog(aliasOrBidder), aliasOrBidder); + : ObjectUtils.defaultIfNull(resolveBidderViaCatalog(aliasOrBidder), aliasOrBidder); } public Integer resolveAliasVendorId(String alias) { diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index bb6dfb17646..11254451e54 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -45,7 +45,6 @@ import org.prebid.server.proto.openrtb.ext.request.ExtBidderConfigFpd; import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestCurrency; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidBidderConfig; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCache; @@ -80,10 +79,10 @@ 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 = "*"; - private static final String GENERIC_SCHAIN_KEY = "*"; private static final BigDecimal THOUSAND = BigDecimal.valueOf(1000); @@ -92,6 +91,7 @@ public class ExchangeService { private final StoredResponseProcessor storedResponseProcessor; private final PrivacyEnforcementService privacyEnforcementService; private final FpdResolver fpdResolver; + private final SchainResolver schainResolver; private final HttpBidderRequester httpBidderRequester; private final ResponseBidValidator responseBidValidator; private final CurrencyConversionService currencyService; @@ -106,6 +106,7 @@ public ExchangeService(long expectedCacheTime, StoredResponseProcessor storedResponseProcessor, PrivacyEnforcementService privacyEnforcementService, FpdResolver fpdResolver, + SchainResolver schainResolver, HttpBidderRequester httpBidderRequester, ResponseBidValidator responseBidValidator, CurrencyConversionService currencyService, @@ -123,6 +124,7 @@ public ExchangeService(long expectedCacheTime, this.storedResponseProcessor = Objects.requireNonNull(storedResponseProcessor); this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); this.fpdResolver = Objects.requireNonNull(fpdResolver); + this.schainResolver = Objects.requireNonNull(schainResolver); this.httpBidderRequester = Objects.requireNonNull(httpBidderRequester); this.responseBidValidator = Objects.requireNonNull(responseBidValidator); this.currencyService = Objects.requireNonNull(currencyService); @@ -154,18 +156,19 @@ public Future holdAuction(AuctionContext context) { .compose(impsRequiredRequest -> extractBidderRequests(context, impsRequiredRequest, aliases)) .map(bidderRequests -> updateRequestMetric( bidderRequests, uidsCookie, aliases, publisherId, context.getRequestTypeMetric())) - .compose(bidderRequests -> CompositeFuture.join(bidderRequests.stream() - .map(bidderRequest -> requestBids( - bidderRequest, - auctionTimeout(timeout, cacheInfo.isDoCaching()), - debugEnabled, - aliases)) - .collect(Collectors.toList()))) + .compose(bidderRequests -> CompositeFuture.join( + bidderRequests.stream() + .map(bidderRequest -> requestBids( + bidderRequest, + auctionTimeout(timeout, cacheInfo.isDoCaching()), + debugEnabled, + aliases)) + .collect(Collectors.toList()))) // send all the requests to the bidders and gathers results .map(CompositeFuture::list) .map(bidderResponses -> storedResponseProcessor.mergeWithBidderResponses( bidderResponses, storedResponse, bidRequest.getImp())) - .map(bidderResponses -> validateAndAdjustBids(bidRequest, bidderResponses)) + .map(bidderResponses -> validateAndAdjustBids(bidderResponses, context, aliases)) .map(bidderResponses -> updateMetricsFromResponses(bidderResponses, publisherId, aliases)) // produce response from bidder results .compose(bidderResponses -> bidResponseCreator.create( @@ -189,18 +192,6 @@ private static ExtRequestTargeting targeting(BidRequest bidRequest) { return prebid != null ? prebid.getTargeting() : null; } - private static Map> currencyRates(BidRequest bidRequest) { - final ExtRequestPrebid prebid = extRequestPrebid(bidRequest); - final ExtRequestCurrency currency = prebid != null ? prebid.getCurrency() : null; - return currency != null ? currency.getRates() : null; - } - - private static Boolean usepbsrates(BidRequest bidRequest) { - final ExtRequestPrebid prebid = extRequestPrebid(bidRequest); - final ExtRequestCurrency currency = prebid != null ? prebid.getCurrency() : null; - return currency != null ? currency.getUsepbsrates() : null; - } - /** * Creates {@link BidRequestCacheInfo} based on {@link BidRequest} model. */ @@ -279,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 @@ -301,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()); @@ -317,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. */ @@ -417,7 +411,7 @@ private Map prepareUsers(List bidders, final Map bidderToUser = new HashMap<>(); for (String bidder : bidders) { - final ExtBidderConfigFpd fpdConfig = ObjectUtils.firstNonNull(biddersToConfigs.get(bidder), + final ExtBidderConfigFpd fpdConfig = ObjectUtils.defaultIfNull(biddersToConfigs.get(bidder), biddersToConfigs.get(ALL_BIDDERS_CONFIG)); final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.contains(bidder); @@ -507,15 +501,18 @@ private List getBidderRequests(List bidderPr List imps, Map biddersToConfigs) { - final ExtRequest requestExt = bidRequest.getExt(); - final Map bidderToPrebidBidders = bidderToPrebidBidders(requestExt); - final Map bidderToPrebidSchains = bidderToPrebidSchains(requestExt); + final Map bidderToPrebidBidders = bidderToPrebidBidders(bidRequest); + final List bidderRequests = bidderPrivacyResults.stream() // for each bidder create a new request that is a copy of original request except buyerid, imp // extensions, ext.prebid.data.bidders and ext.prebid.bidders. // Also, check whether to pass user.ext.data, app.ext.data and site.ext.data or not. - .map(bidderPrivacyResult -> createBidderRequest(bidderPrivacyResult, bidRequest, imps, - biddersToConfigs, bidderToPrebidBidders, bidderToPrebidSchains)) + .map(bidderPrivacyResult -> createBidderRequest( + bidderPrivacyResult, + bidRequest, + imps, + biddersToConfigs, + bidderToPrebidBidders)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -527,7 +524,8 @@ private List getBidderRequests(List bidderPr /** * Extracts a map of bidders to their arguments from {@link ObjectNode} prebid.bidders. */ - private static Map bidderToPrebidBidders(ExtRequest requestExt) { + private static Map bidderToPrebidBidders(BidRequest bidRequest) { + final ExtRequest requestExt = bidRequest.getExt(); final ExtRequestPrebid prebid = requestExt == null ? null : requestExt.getPrebid(); final ObjectNode bidders = prebid == null ? null : prebid.getBidders(); @@ -544,34 +542,6 @@ private static Map bidderToPrebidBidders(ExtRequest requestExt return bidderToPrebidParameters; } - /** - * Extracts a map of bidders to their arguments from {@link ObjectNode} prebid.schains. - */ - private static Map bidderToPrebidSchains(ExtRequest requestExt) { - final ExtRequestPrebid prebid = requestExt == null ? null : requestExt.getPrebid(); - final List schains = prebid == null ? null : prebid.getSchains(); - - if (schains == null || schains.isEmpty()) { - return Collections.emptyMap(); - } - - final Map bidderToPrebidSchains = new HashMap<>(); - for (ExtRequestPrebidSchain schain : schains) { - final List bidders = schain.getBidders(); - if (CollectionUtils.isNotEmpty(bidders)) { - for (String bidder : bidders) { - if (bidderToPrebidSchains.containsKey(bidder)) { - bidderToPrebidSchains.remove(bidder); - logger.debug("Schain bidder {0} is rejected since it was defined more than once", bidder); - continue; - } - bidderToPrebidSchains.put(bidder, schain.getSchain()); - } - } - } - return bidderToPrebidSchains; - } - /** * Returns {@link BidderRequest} for the given bidder. */ @@ -579,8 +549,7 @@ private BidderRequest createBidderRequest(BidderPrivacyResult bidderPrivacyResul BidRequest bidRequest, List imps, Map biddersToConfigs, - Map bidderToPrebidBidders, - Map bidderToPrebidSchains) { + Map bidderToPrebidBidders) { final String bidder = bidderPrivacyResult.getRequestBidder(); if (bidderPrivacyResult.isBlockedRequestByTcf()) { @@ -590,7 +559,7 @@ private BidderRequest createBidderRequest(BidderPrivacyResult bidderPrivacyResul final List firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt()); final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.contains(bidder); - final ExtBidderConfigFpd fpdConfig = ObjectUtils.firstNonNull(biddersToConfigs.get(bidder), + final ExtBidderConfigFpd fpdConfig = ObjectUtils.defaultIfNull(biddersToConfigs.get(bidder), biddersToConfigs.get(ALL_BIDDERS_CONFIG)); final Site bidRequestSite = bidRequest.getSite(); @@ -611,7 +580,7 @@ private BidderRequest createBidderRequest(BidderPrivacyResult bidderPrivacyResul .imp(prepareImps(bidder, imps, useFirstPartyData)) .app(prepareApp(bidRequestApp, fpdApp, useFirstPartyData)) .site(prepareSite(bidRequestSite, fpdSite, useFirstPartyData)) - .source(prepareSource(bidder, bidderToPrebidSchains, bidRequest.getSource())) + .source(prepareSource(bidder, bidRequest)) .ext(prepareExt(bidder, bidderToPrebidBidders, bidRequest.getExt())) .build()); } @@ -622,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()) @@ -634,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 ObjectNode result = mapper.mapper().valueToTree(ExtPrebid.of(impExtPrebid, impExtBidder)); - final JsonNode contextNode = impExt.get(CONTEXT_EXT); - final boolean isContextNodePresent = contextNode != null && !contextNode.isNull(); - if (isContextNodePresent) { - final JsonNode contextNodeCopy = contextNode.deepCopy(); + 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; } /** @@ -719,10 +690,10 @@ private ExtSite maskExtSite(ExtSite siteExt) { /** * Returns {@link Source} with corresponding request.ext.prebid.schains. */ - private Source prepareSource(String bidder, Map bidderToSchain, - Source receivedSource) { - final ExtRequestPrebidSchainSchain defaultSchain = bidderToSchain.get(GENERIC_SCHAIN_KEY); - final ExtRequestPrebidSchainSchain bidderSchain = bidderToSchain.getOrDefault(bidder, defaultSchain); + private Source prepareSource(String bidder, BidRequest bidRequest) { + final Source receivedSource = bidRequest.getSource(); + + final ExtRequestPrebidSchainSchain bidderSchain = schainResolver.resolveForBidder(bidder, bidRequest); if (bidderSchain == null) { return receivedSource; @@ -811,8 +782,10 @@ private static BigDecimal bidAdjustmentForBidder(BidRequest bidRequest, String b * Passes the request to a corresponding bidder and wraps response in {@link BidderResponse} which also holds * recorded response time. */ - private Future requestBids( - BidderRequest bidderRequest, Timeout timeout, boolean debugEnabled, BidderAliases aliases) { + private Future requestBids(BidderRequest bidderRequest, + Timeout timeout, + boolean debugEnabled, + BidderAliases aliases) { final String bidderName = bidderRequest.getBidder(); final Bidder bidder = bidderCatalog.bidderByName(aliases.resolveBidder(bidderName)); @@ -822,10 +795,12 @@ private Future requestBids( .map(seatBid -> BidderResponse.of(bidderName, seatBid, responseTime(startTime))); } - private List validateAndAdjustBids(BidRequest bidRequest, List bidderResponses) { + private List validateAndAdjustBids( + List bidderResponses, AuctionContext auctionContext, BidderAliases aliases) { + return bidderResponses.stream() - .map(bidderResponse -> validBidderResponse(bidderResponse, bidRequest.getCur())) - .map(bidderResponse -> applyBidPriceChanges(bidderResponse, bidRequest)) + .map(bidderResponse -> validBidderResponse(bidderResponse, auctionContext, aliases)) + .map(bidderResponse -> applyBidPriceChanges(bidderResponse, auctionContext.getBidRequest())) .collect(Collectors.toList()); } @@ -836,28 +811,37 @@ private List validateAndAdjustBids(BidRequest bidRequest, List * Returns input argument as the result if no errors found or creates new {@link BidderResponse} otherwise. */ - private BidderResponse validBidderResponse(BidderResponse bidderResponse, List requestCurrencies) { - final BidderSeatBid seatBid = bidderResponse.getSeatBid(); - final List bids = seatBid.getBids(); + private BidderResponse validBidderResponse( + BidderResponse bidderResponse, AuctionContext auctionContext, BidderAliases aliases) { - final List validBids = new ArrayList<>(bids.size()); + final BidRequest bidRequest = auctionContext.getBidRequest(); + final BidderSeatBid seatBid = bidderResponse.getSeatBid(); final List errors = new ArrayList<>(seatBid.getErrors()); + final List requestCurrencies = bidRequest.getCur(); if (requestCurrencies.size() > 1) { errors.add(BidderError.badInput( String.format("Cur parameter contains more than one currency. %s will be used", requestCurrencies.get(0)))); } - for (BidderBid bid : bids) { - final ValidationResult validationResult = responseBidValidator.validate(bid); + final List bids = seatBid.getBids(); + final List validBids = new ArrayList<>(bids.size()); + + for (final BidderBid bid : bids) { + final ValidationResult validationResult = + responseBidValidator.validate(bid, bidderResponse.getBidder(), auctionContext, aliases); + + if (validationResult.hasWarnings()) { + addAsBidderErrors(validationResult.getWarnings(), errors); + } + if (validationResult.hasErrors()) { - for (String error : validationResult.getErrors()) { - errors.add(BidderError.generic(error)); - } - } else { - validBids.add(bid); + addAsBidderErrors(validationResult.getErrors(), errors); + continue; } + + validBids.add(bid); } return errors.isEmpty() @@ -865,6 +849,10 @@ private BidderResponse validBidderResponse(BidderResponse bidderResponse, List messages, List errors) { + messages.stream().map(BidderError::generic).forEach(errors::add); + } + /** * Performs changes on {@link Bid}s price depends on different between adServerCurrency and bidCurrency, * and adjustment factor. Will drop bid if currency conversion is needed but not possible. @@ -885,7 +873,6 @@ private BidderResponse applyBidPriceChanges(BidderResponse bidderResponse, BidRe final String adServerCurrency = bidRequest.getCur().get(0); final BigDecimal priceAdjustmentFactor = bidAdjustmentForBidder(bidRequest, bidderResponse.getBidder()); - final Boolean usepbsrates = usepbsrates(bidRequest); for (final BidderBid bidderBid : bidderBids) { final Bid bid = bidderBid.getBid(); @@ -893,8 +880,7 @@ private BidderResponse applyBidPriceChanges(BidderResponse bidderResponse, BidRe final BigDecimal price = bid.getPrice(); try { final BigDecimal priceInAdServerCurrency = currencyService.convertCurrency( - price, currencyRates(bidRequest), adServerCurrency, - StringUtils.stripToNull(bidCurrency), usepbsrates); + price, bidRequest, adServerCurrency, StringUtils.stripToNull(bidCurrency)); final BigDecimal adjustedPrice = adjustPrice(priceAdjustmentFactor, priceInAdServerCurrency); diff --git a/src/main/java/org/prebid/server/auction/FpdResolver.java b/src/main/java/org/prebid/server/auction/FpdResolver.java index ba6643be2b7..2eab1a9229d 100644 --- a/src/main/java/org/prebid/server/auction/FpdResolver.java +++ b/src/main/java/org/prebid/server/auction/FpdResolver.java @@ -10,6 +10,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.json.JsonMerger; import org.prebid.server.proto.openrtb.ext.request.ExtApp; import org.prebid.server.proto.openrtb.ext.request.ExtBidderConfig; import org.prebid.server.proto.openrtb.ext.request.ExtBidderConfigFpd; @@ -21,7 +22,6 @@ import org.prebid.server.proto.openrtb.ext.request.ExtSite; import org.prebid.server.proto.openrtb.ext.request.ExtUser; import org.prebid.server.proto.request.Targeting; -import org.prebid.server.util.JsonMergeUtil; import java.util.ArrayList; import java.util.Arrays; @@ -53,12 +53,11 @@ public class FpdResolver { "privacypolicy", "mobile")); private final JacksonMapper jacksonMapper; - private final JsonMergeUtil jsonMergeUtil; + private final JsonMerger jsonMerger; - public FpdResolver(JacksonMapper jacksonMapper) { + public FpdResolver(JacksonMapper jacksonMapper, JsonMerger jsonMerger) { this.jacksonMapper = Objects.requireNonNull(jacksonMapper); - - this.jsonMergeUtil = new JsonMergeUtil(jacksonMapper); + this.jsonMerger = Objects.requireNonNull(jsonMerger); } public User resolveUser(User originUser, ObjectNode fpdUser) { @@ -183,7 +182,7 @@ public ObjectNode resolveImpExt(ObjectNode impExt, ObjectNode targeting) { : null; final ObjectNode resolvedData = extImpContextData != null - ? (ObjectNode) jsonMergeUtil.merge(targeting, extImpContextData) + ? (ObjectNode) jsonMerger.merge(targeting, extImpContextData) : targeting; return extImpContext != null && extImpContext.isObject() @@ -253,7 +252,7 @@ private ObjectNode mergeExtData(JsonNode fpdData, JsonNode originData) { } if (originData != null && originData.isObject()) { - return (ObjectNode) jsonMergeUtil.merge(fpdData, originData); + return (ObjectNode) jsonMerger.merge(fpdData, originData); } return fpdData.isObject() ? (ObjectNode) fpdData : null; } diff --git a/src/main/java/org/prebid/server/auction/OrtbTypesResolver.java b/src/main/java/org/prebid/server/auction/OrtbTypesResolver.java index fbe5eaf63b5..9773811950d 100644 --- a/src/main/java/org/prebid/server/auction/OrtbTypesResolver.java +++ b/src/main/java/org/prebid/server/auction/OrtbTypesResolver.java @@ -12,7 +12,7 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.json.JacksonMapper; -import org.prebid.server.util.JsonMergeUtil; +import org.prebid.server.json.JsonMerger; import java.util.ArrayList; import java.util.Arrays; @@ -69,11 +69,11 @@ public class OrtbTypesResolver { } private final JacksonMapper jacksonMapper; - private final JsonMergeUtil jsonMergeUtil; + private final JsonMerger jsonMerger; - public OrtbTypesResolver(JacksonMapper jacksonMapper) { + public OrtbTypesResolver(JacksonMapper jacksonMapper, JsonMerger jsonMerger) { this.jacksonMapper = Objects.requireNonNull(jacksonMapper); - this.jsonMergeUtil = new JsonMergeUtil(jacksonMapper); + this.jsonMerger = Objects.requireNonNull(jsonMerger); } /** @@ -254,7 +254,7 @@ public void normalizeDataExtension(ObjectNode containerNode, String containerNam final JsonNode extData = containerNode.path(EXT).path(DATA); final JsonNode ext = containerNode.get(EXT); if (!extData.isNull() && !extData.isMissingNode()) { - final JsonNode resolvedExtData = jsonMergeUtil.merge(extData, data); + final JsonNode resolvedExtData = jsonMerger.merge(extData, data); ((ObjectNode) ext).set(DATA, resolvedExtData); } else { copyDataToExtData(containerNode, containerName, nodePrefix, warnings, data); diff --git a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java b/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java index eb7fbf023c9..ab09449e78f 100644 --- a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java +++ b/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java @@ -7,6 +7,7 @@ import com.iab.openrtb.request.User; import io.vertx.core.Future; import io.vertx.core.http.HttpServerRequest; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.BooleanUtils; import org.prebid.server.auction.model.AuctionContext; @@ -65,13 +66,15 @@ public class PrivacyEnforcementService { private final IpAddressHelper ipAddressHelper; private final Metrics metrics; private final boolean ccpaEnforce; + private final boolean lmtEnforce; public PrivacyEnforcementService(BidderCatalog bidderCatalog, PrivacyExtractor privacyExtractor, TcfDefinerService tcfDefinerService, IpAddressHelper ipAddressHelper, Metrics metrics, - boolean ccpaEnforce) { + boolean ccpaEnforce, + boolean lmtEnforce) { this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.privacyExtractor = Objects.requireNonNull(privacyExtractor); @@ -79,12 +82,13 @@ public PrivacyEnforcementService(BidderCatalog bidderCatalog, this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); this.metrics = Objects.requireNonNull(metrics); this.ccpaEnforce = ccpaEnforce; + this.lmtEnforce = lmtEnforce; } Future contextFromBidRequest( - BidRequest bidRequest, Account account, MetricName requestType, Timeout timeout) { + BidRequest bidRequest, Account account, MetricName requestType, Timeout timeout, List errors) { - final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest); + final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest, errors); final Device device = bidRequest.getDevice(); final String ipAddress = device != null ? device.getIp() : null; @@ -108,9 +112,10 @@ Future contextFromBidRequest( .map(tcfContext -> PrivacyContext.of(privacy, tcfContext, tcfContext.getIpAddress())); } - public Future contextFromLegacyRequest(PreBidRequestContext preBidRequestContext, Account account) { - final Privacy privacy = privacyExtractor.validPrivacyFrom(preBidRequestContext.getPreBidRequest()); + public Future contextFromLegacyRequest( + PreBidRequestContext preBidRequestContext, Account account) { + final Privacy privacy = privacyExtractor.validPrivacyFrom(preBidRequestContext.getPreBidRequest()); final AccountGdprConfig accountGdpr = account.getGdpr(); final String accountId = account.getId(); final RequestLogInfo requestLogInfo = requestLogInfo(MetricName.legacy, null, accountId); @@ -187,9 +192,9 @@ Future> mask(AuctionContext auctionContext, return getBidderToEnforcementAction(privacyContext.getTcfContext(), biddersToApplyTcf, aliases, account) .map(bidderToEnforcement -> updatePrivacyMetrics( - bidderToEnforcement, aliases, requestType, device)) + bidderToEnforcement, aliases, requestType, bidderToUser, device)) .map(bidderToEnforcement -> getBidderToPrivacyResult( - biddersToApplyTcf, bidderToUser, device, bidderToEnforcement)) + bidderToEnforcement, biddersToApplyTcf, bidderToUser, device)) .map(gdprResult -> merge(ccpaResult, gdprResult)); } @@ -314,7 +319,7 @@ private Future> getBidderToEnforcementActi TcfContext tcfContext, Set bidders, BidderAliases aliases, Account account) { return tcfDefinerService.resultForBidderNames( - new HashSet<>(bidders), VendorIdResolver.of(aliases), tcfContext, account.getGdpr()) + Collections.unmodifiableSet(bidders), VendorIdResolver.of(aliases), tcfContext, account.getGdpr()) .map(tcfResponse -> mapTcfResponseToEachBidder(tcfResponse, bidders)); } @@ -339,7 +344,7 @@ private Set extractCcpaEnforcedBidders(List bidders, BidRequest return ccpaEnforcedBidders; } - private Map mapTcfResponseToEachBidder( + private static Map mapTcfResponseToEachBidder( TcfResponse tcfResponse, Set bidders) { final Map bidderNameToAction = tcfResponse.getActions(); @@ -354,39 +359,78 @@ private Map updatePrivacyMetrics( Map bidderToEnforcement, BidderAliases aliases, MetricName requestType, + Map bidderToUser, Device device) { + // Metrics should represent real picture of the bidding process, so if bidder request is blocked + // by privacy then no reason to increment another metrics, like geo masked, etc. for (final Map.Entry bidderEnforcement : bidderToEnforcement.entrySet()) { - final String bidder = aliases.resolveBidder(bidderEnforcement.getKey()); + final String bidder = bidderEnforcement.getKey(); final PrivacyEnforcementAction enforcement = bidderEnforcement.getValue(); + final boolean requestBlocked = enforcement.isBlockBidderRequest(); + + final User user = bidderToUser.get(bidder); + boolean userIdRemoved = enforcement.isRemoveUserIds(); + if (requestBlocked || (userIdRemoved && !shouldMaskUser(user))) { + userIdRemoved = false; + } + + boolean geoMasked = enforcement.isMaskGeo(); + if (requestBlocked || (geoMasked && !shouldMaskGeo(user, device))) { + geoMasked = false; + } + + final boolean analyticsBlocked = !requestBlocked && enforcement.isBlockAnalyticsReport(); + metrics.updateAuctionTcfMetrics( - bidder, + aliases.resolveBidder(bidder), requestType, - enforcement.isRemoveUserIds(), - enforcement.isMaskGeo(), - enforcement.isBlockBidderRequest(), - enforcement.isBlockAnalyticsReport()); + userIdRemoved, + geoMasked, + analyticsBlocked, + requestBlocked); } - if (isLmtEnabled(device)) { + if (lmtEnforce && isLmtEnabled(device)) { metrics.updatePrivacyLmtMetric(); } return bidderToEnforcement; } + /** + * Returns true if {@link User} has sensitive privacy information that can be masked. + */ + private static boolean shouldMaskUser(User user) { + if (user == null) { + return false; + } + if (user.getId() != null || user.getBuyeruid() != null) { + return true; + } + final ExtUser extUser = user.getExt(); + return extUser != null && (CollectionUtils.isNotEmpty(extUser.getEids())); + } + + /** + * Returns true if {@link User} or {@link Device} has {@link Geo} information that can be masked. + */ + private static boolean shouldMaskGeo(User user, Device device) { + return (user != null && user.getGeo() != null) || (device != null && device.getGeo() != null); + } + /** * Returns {@link Map}<{@link String}, {@link BidderPrivacyResult}>, where bidder name mapped to masked * {@link BidderPrivacyResult}. Masking depends on GDPR and COPPA. */ private List getBidderToPrivacyResult( + Map bidderToEnforcement, Set bidders, Map bidderToUser, - Device device, - Map bidderToEnforcement) { + Device device) { - final boolean isLmtEnabled = isLmtEnabled(device); + final boolean isLmtEnabled = lmtEnforce && isLmtEnabled(device); return bidderToUser.entrySet().stream() .filter(entry -> bidders.contains(entry.getKey())) .map(bidderUserEntry -> createBidderPrivacyResult( @@ -401,11 +445,12 @@ private List getBidderToPrivacyResult( /** * Returns {@link BidderPrivacyResult} with GDPR masking. */ - private BidderPrivacyResult createBidderPrivacyResult(User user, - Device device, - String bidder, - boolean isLmtEnabled, - Map bidderToEnforcement) { + private BidderPrivacyResult createBidderPrivacyResult( + User user, + Device device, + String bidder, + boolean isLmtEnabled, + Map bidderToEnforcement) { final PrivacyEnforcementAction privacyEnforcementAction = bidderToEnforcement.get(bidder); final boolean blockBidderRequest = privacyEnforcementAction.isBlockBidderRequest(); @@ -505,11 +550,11 @@ private static Float maskGeoCoordinate(Float coordinate) { } /** - * Returns masked digitrust and eids of user ext. + * Returns masked eids of user ext. */ - private ExtUser maskUserExt(ExtUser userExt) { + private static ExtUser maskUserExt(ExtUser userExt) { return userExt != null - ? nullIfEmpty(userExt.toBuilder().eids(null).digitrust(null).build()) + ? nullIfEmpty(userExt.toBuilder().eids(null).build()) : null; } diff --git a/src/main/java/org/prebid/server/auction/SchainResolver.java b/src/main/java/org/prebid/server/auction/SchainResolver.java new file mode 100644 index 00000000000..2b06df94047 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/SchainResolver.java @@ -0,0 +1,129 @@ +package org.prebid.server.auction; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Source; +import io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchain; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchainSchain; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchainSchainNode; +import org.prebid.server.proto.openrtb.ext.request.ExtSource; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class SchainResolver { + + private static final Logger logger = LoggerFactory.getLogger(SchainResolver.class); + + private final ExtRequestPrebidSchainSchainNode globalNode; + + private SchainResolver(ExtRequestPrebidSchainSchainNode globalNode) { + this.globalNode = globalNode; + } + + public static SchainResolver create(String globalNodeString, JacksonMapper mapper) { + return new SchainResolver(globalNodeOrNull(globalNodeString, Objects.requireNonNull(mapper))); + } + + public ExtRequestPrebidSchainSchain resolveForBidder(String bidder, BidRequest bidRequest) { + final ExtRequest requestExt = bidRequest.getExt(); + final ExtRequestPrebid prebid = requestExt == null ? null : requestExt.getPrebid(); + final List schains = prebid == null ? null : prebid.getSchains(); + + ExtRequestPrebidSchainSchain bidderSchain = null; + ExtRequestPrebidSchainSchain catchAllSchain = null; + for (final ExtRequestPrebidSchain schain : ListUtils.emptyIfNull(schains)) { + catchAllSchain = existingSchainOrNull("*", catchAllSchain, schain); + bidderSchain = existingSchainOrNull(bidder, bidderSchain, schain); + } + + return enrich(ObjectUtils.defaultIfNull(bidderSchain, catchAllSchain), bidRequest); + } + + private static ExtRequestPrebidSchainSchainNode globalNodeOrNull(String globalNodeString, JacksonMapper mapper) { + if (StringUtils.isBlank(globalNodeString)) { + return null; + } + + try { + return mapper.decodeValue(globalNodeString, ExtRequestPrebidSchainSchainNode.class); + } catch (DecodeException e) { + throw new IllegalArgumentException("Exception occurred while parsing global schain node", e); + } + } + + private ExtRequestPrebidSchainSchain existingSchainOrNull(String bidder, + ExtRequestPrebidSchainSchain existingSchain, + ExtRequestPrebidSchain schainEntry) { + + if (schainEntry == null + || CollectionUtils.isEmpty(schainEntry.getBidders()) + || !schainEntry.getBidders().contains(bidder)) { + + return existingSchain; + } + + if (existingSchain != null) { + logger.debug("Schain bidder {0} is rejected since it was defined more than once", bidder); + return null; + } + + return schainEntry.getSchain(); + } + + private ExtRequestPrebidSchainSchain enrich(ExtRequestPrebidSchainSchain bidderSpecificSchain, + BidRequest bidRequest) { + + if (globalNode == null) { + return bidderSpecificSchain; + } + + if (bidderSpecificSchain != null) { + return enrichSchainWithGlobalNode(bidderSpecificSchain); + } + + final ExtRequestPrebidSchainSchain requestSchain = requestSchain(bidRequest); + if (requestSchain != null) { + return enrichSchainWithGlobalNode(requestSchain); + } + + return newSchainWithGlobalNode(); + } + + private ExtRequestPrebidSchainSchain requestSchain(BidRequest bidRequest) { + final Source source = bidRequest.getSource(); + final ExtSource extSource = source != null ? source.getExt() : null; + + return extSource != null ? extSource.getSchain() : null; + } + + private ExtRequestPrebidSchainSchain enrichSchainWithGlobalNode(ExtRequestPrebidSchainSchain requestSchain) { + return ExtRequestPrebidSchainSchain.of( + requestSchain.getVer(), + requestSchain.getComplete(), + appendGlobalNode(requestSchain.getNodes()), + requestSchain.getExt()); + } + + private List appendGlobalNode(List nodes) { + final ArrayList updatedNodes = new ArrayList<>(ListUtils.emptyIfNull(nodes)); + updatedNodes.add(globalNode); + + return updatedNodes; + } + + private ExtRequestPrebidSchainSchain newSchainWithGlobalNode() { + return ExtRequestPrebidSchainSchain.of(null, null, Collections.singletonList(globalNode), null); + } +} diff --git a/src/main/java/org/prebid/server/auction/StoredRequestProcessor.java b/src/main/java/org/prebid/server/auction/StoredRequestProcessor.java index 2b156d2b379..37260870277 100644 --- a/src/main/java/org/prebid/server/auction/StoredRequestProcessor.java +++ b/src/main/java/org/prebid/server/auction/StoredRequestProcessor.java @@ -5,12 +5,14 @@ import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Video; import io.vertx.core.Future; +import io.vertx.core.file.FileSystem; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.execution.Timeout; import org.prebid.server.execution.TimeoutFactory; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.json.JsonMerger; import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.request.ExtImp; import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid; @@ -20,7 +22,6 @@ import org.prebid.server.settings.ApplicationSettings; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.VideoStoredDataResult; -import org.prebid.server.util.JsonMergeUtil; import java.util.ArrayList; import java.util.Collections; @@ -39,25 +40,48 @@ public class StoredRequestProcessor { private final long defaultTimeout; + private final BidRequest defaultBidRequest; private final ApplicationSettings applicationSettings; private final TimeoutFactory timeoutFactory; private final Metrics metrics; private final JacksonMapper mapper; - private JsonMergeUtil jsonMergeUtil; + private final JsonMerger jsonMerger; - public StoredRequestProcessor(long defaultTimeout, - ApplicationSettings applicationSettings, - Metrics metrics, - TimeoutFactory timeoutFactory, - JacksonMapper mapper) { + private StoredRequestProcessor(long defaultTimeout, + BidRequest defaultBidRequest, + ApplicationSettings applicationSettings, + Metrics metrics, + TimeoutFactory timeoutFactory, + JacksonMapper mapper, + JsonMerger jsonMerger) { this.defaultTimeout = defaultTimeout; - this.applicationSettings = Objects.requireNonNull(applicationSettings); - this.timeoutFactory = Objects.requireNonNull(timeoutFactory); - this.metrics = Objects.requireNonNull(metrics); - this.mapper = Objects.requireNonNull(mapper); + this.defaultBidRequest = defaultBidRequest; + this.applicationSettings = applicationSettings; + this.timeoutFactory = timeoutFactory; + this.metrics = metrics; + this.mapper = mapper; + this.jsonMerger = jsonMerger; + } - jsonMergeUtil = new JsonMergeUtil(mapper); + public static StoredRequestProcessor create(long defaultTimeout, + String defaultBidRequestPath, + FileSystem fileSystem, + ApplicationSettings applicationSettings, + Metrics metrics, + TimeoutFactory timeoutFactory, + JacksonMapper mapper, + JsonMerger jsonMerger) { + + return new StoredRequestProcessor( + defaultTimeout, + readBidRequest( + defaultBidRequestPath, Objects.requireNonNull(fileSystem), Objects.requireNonNull(mapper)), + Objects.requireNonNull(applicationSettings), + Objects.requireNonNull(metrics), + Objects.requireNonNull(timeoutFactory), + Objects.requireNonNull(mapper), + Objects.requireNonNull(jsonMerger)); } /** @@ -89,25 +113,15 @@ Future processStoredRequests(String accountId, BidRequest bidRequest applicationSettings.getStoredData(accountId, requestIds, impIds, timeout(bidRequest)) .compose(storedDataResult -> updateMetrics(storedDataResult, requestIds, impIds)); - return storedRequestsToBidRequest(storedDataFuture, bidRequest, - bidRequestToStoredRequestId.get(bidRequest), impToStoredRequestId); - } - - private Future updateMetrics(StoredDataResult storedDataResult, Set requestIds, - Set impIds) { - requestIds.forEach( - id -> metrics.updateStoredRequestMetric(storedDataResult.getStoredIdToRequest().containsKey(id))); - impIds.forEach( - id -> metrics.updateStoredImpsMetric(storedDataResult.getStoredIdToImp().containsKey(id))); - - return Future.succeededFuture(storedDataResult); + return storedRequestsToBidRequest( + storedDataFuture, bidRequest, bidRequestToStoredRequestId.get(bidRequest), impToStoredRequestId); } /** * Fetches AMP request from the source. */ Future processAmpRequest(String accountId, String ampRequestId) { - final BidRequest bidRequest = BidRequest.builder().build(); + final BidRequest bidRequest = defaultBidRequest != null ? defaultBidRequest : BidRequest.builder().build(); final Future ampStoredDataFuture = applicationSettings.getAmpStoredData( accountId, Collections.singleton(ampRequestId), Collections.emptySet(), timeout(bidRequest)) @@ -132,9 +146,27 @@ Future videoStoredDataResult(String accountId, List .map(storedDataResult -> makeVideoStoredDataResult(storedDataResult, storedIdToImpId, errors)); } + private Future updateMetrics(StoredDataResult storedDataResult, Set requestIds, + Set impIds) { + requestIds.forEach( + id -> metrics.updateStoredRequestMetric(storedDataResult.getStoredIdToRequest().containsKey(id))); + impIds.forEach(id -> metrics.updateStoredImpsMetric(storedDataResult.getStoredIdToImp().containsKey(id))); + + return Future.succeededFuture(storedDataResult); + } + + private static BidRequest readBidRequest( + String defaultBidRequestPath, FileSystem fileSystem, JacksonMapper mapper) { + + return StringUtils.isNotBlank(defaultBidRequestPath) + ? mapper.decodeValue(fileSystem.readFileBlocking(defaultBidRequestPath), BidRequest.class) + : null; + } + private VideoStoredDataResult makeVideoStoredDataResult(StoredDataResult storedDataResult, Map storedIdToImpId, List errors) { + final Map storedIdToStoredImp = storedDataResult.getStoredIdToImp(); final Map impIdToStoredVideo = new HashMap<>(); @@ -172,8 +204,10 @@ private Video parseVideoFromImp(String storedJson) { } private Future storedRequestsToBidRequest(Future storedDataFuture, - BidRequest bidRequest, String storedBidRequestId, + BidRequest bidRequest, + String storedBidRequestId, Map impsToStoredRequestId) { + return storedDataFuture .recover(exception -> Future.failedFuture(new InvalidRequestException( String.format("Stored request fetching failed: %s", exception.getMessage())))) @@ -187,21 +221,31 @@ private Future storedRequestsToBidRequest(Future s /** * Runs {@link BidRequest} and {@link Imp}s merge processes. */ - private BidRequest mergeBidRequestAndImps(BidRequest bidRequest, String storedRequestId, - Map impToStoredId, StoredDataResult storedDataResult) { - return mergeBidRequestImps(mergeBidRequest(bidRequest, storedRequestId, storedDataResult), - impToStoredId, storedDataResult); + private BidRequest mergeBidRequestAndImps(BidRequest bidRequest, + String storedRequestId, + Map impToStoredId, + StoredDataResult storedDataResult) { + + return mergeBidRequestImps( + mergeBidRequest(mergeDefaultRequest(bidRequest), storedRequestId, storedDataResult), + impToStoredId, + storedDataResult); + } + + private BidRequest mergeDefaultRequest(BidRequest bidRequest) { + return jsonMerger.merge(bidRequest, defaultBidRequest, BidRequest.class); } /** * Merges original request with request from stored request source. Values from original request * has higher priority than stored request values. */ - private BidRequest mergeBidRequest(BidRequest originalRequest, String storedRequestId, - StoredDataResult storedDataResult) { + private BidRequest mergeBidRequest( + BidRequest originalRequest, String storedRequestId, StoredDataResult storedDataResult) { + final String storedRequest = storedDataResult.getStoredIdToRequest().get(storedRequestId); return StringUtils.isNotBlank(storedRequestId) - ? jsonMergeUtil.merge(originalRequest, storedRequest, storedRequestId, BidRequest.class) + ? jsonMerger.merge(originalRequest, storedRequest, storedRequestId, BidRequest.class) : originalRequest; } @@ -209,8 +253,9 @@ private BidRequest mergeBidRequest(BidRequest originalRequest, String storedRequ * Merges {@link Imp}s from original request with Imps from stored request source. Values from original request * has higher priority than stored request values. */ - private BidRequest mergeBidRequestImps(BidRequest bidRequest, Map impToStoredId, - StoredDataResult storedDataResult) { + private BidRequest mergeBidRequestImps( + BidRequest bidRequest, Map impToStoredId, StoredDataResult storedDataResult) { + if (impToStoredId.isEmpty()) { return bidRequest; } @@ -220,7 +265,7 @@ private BidRequest mergeBidRequestImps(BidRequest bidRequest, Map i final String storedRequestId = impToStoredId.get(imp); if (storedRequestId != null) { final String storedImp = storedDataResult.getStoredIdToImp().get(storedRequestId); - final Imp mergedImp = jsonMergeUtil.merge(imp, storedImp, storedRequestId, Imp.class); + final Imp mergedImp = jsonMerger.merge(imp, storedImp, storedRequestId, Imp.class); mergedImps.set(i, mergedImp); } } @@ -228,10 +273,10 @@ private BidRequest mergeBidRequestImps(BidRequest bidRequest, Map i } /** - * Maps object to its StoredRequestId if exists. If object's extension contains storedRequest field, expected that - * it includes id too, in another case error about missed id in stored request will be added to error list. - * Gathers all errors into list, and in case if it is not empty, throws {@link InvalidRequestException} with - * list of errors. + * Maps object to its StoredRequestId if exists. If object's extension contains storedRequest field, expected + * that it includes id too, in another case error about missed id in stored request will be added to error list. + * Gathers all errors into list, and in case if it is not empty, throws {@link InvalidRequestException} with list + * of errors. */ private Map mapStoredRequestHolderToStoredRequestId( List storedRequestHolders, Function storedRequestExtractor) { 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/auction/TargetingKeywordsCreator.java b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java index 8ea9ea42d92..663190052dc 100644 --- a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java +++ b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java @@ -6,6 +6,7 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity; import org.prebid.server.proto.response.Bid; +import org.prebid.server.proto.response.MediaType; import java.math.BigDecimal; import java.util.ArrayList; @@ -76,9 +77,15 @@ public class TargetingKeywordsCreator { */ private static final String HB_CACHE_PATH_KEY = "hb_cache_path"; + /** + * Stores bid's format. For example "video" or "banner". + */ + private static final String HB_FORMAT_KEY = "hb_format"; + private final PriceGranularity priceGranularity; private final boolean includeWinners; private final boolean includeBidderKeys; + private final boolean includeFormat; private final boolean isApp; private final int truncateAttrChars; private final String cacheHost; @@ -88,6 +95,7 @@ public class TargetingKeywordsCreator { private TargetingKeywordsCreator(PriceGranularity priceGranularity, boolean includeWinners, boolean includeBidderKeys, + boolean includeFormat, boolean isApp, int truncateAttrChars, String cacheHost, @@ -97,6 +105,7 @@ private TargetingKeywordsCreator(PriceGranularity priceGranularity, this.priceGranularity = priceGranularity; this.includeWinners = includeWinners; this.includeBidderKeys = includeBidderKeys; + this.includeFormat = includeFormat; this.isApp = isApp; this.truncateAttrChars = truncateAttrChars; this.cacheHost = cacheHost; @@ -110,6 +119,7 @@ private TargetingKeywordsCreator(PriceGranularity priceGranularity, public static TargetingKeywordsCreator create(ExtPriceGranularity extPriceGranularity, boolean includeWinners, boolean includeBidderKeys, + boolean includeFormat, boolean isApp, int truncateAttrChars, String cacheHost, @@ -120,6 +130,7 @@ public static TargetingKeywordsCreator create(ExtPriceGranularity extPriceGranul PriceGranularity.createFromExtPriceGranularity(extPriceGranularity), includeWinners, includeBidderKeys, + includeFormat, isApp, truncateAttrChars, cacheHost, @@ -133,6 +144,7 @@ public static TargetingKeywordsCreator create(ExtPriceGranularity extPriceGranul public static TargetingKeywordsCreator create(String stringPriceGranularity, boolean includeWinners, boolean includeBidderKeys, + boolean includeFormat, boolean isApp, int truncateAttrChars) { @@ -140,6 +152,7 @@ public static TargetingKeywordsCreator create(String stringPriceGranularity, convertToCustomPriceGranularity(stringPriceGranularity), includeWinners, includeBidderKeys, + includeFormat, isApp, truncateAttrChars, null, @@ -169,6 +182,7 @@ private static PriceGranularity convertToCustomPriceGranularity(String stringPri * Creates map of keywords for the given {@link Bid}. */ public Map makeFor(Bid bid, boolean winningBid) { + final MediaType mediaType = bid.getMediaType(); return truncateKeys(makeFor( bid.getBidder(), winningBid, @@ -178,6 +192,7 @@ public Map makeFor(Bid bid, boolean winningBid) { bid.getHeight(), bid.getCacheId(), null, + mediaType != null ? mediaType.name() : null, bid.getDealId())); } @@ -188,6 +203,7 @@ Map makeFor(com.iab.openrtb.response.Bid bid, String bidder, boolean winningBid, String cacheId, + String format, String vastCacheId) { final Map keywords = makeFor( @@ -199,6 +215,7 @@ Map makeFor(com.iab.openrtb.response.Bid bid, bid.getH(), cacheId, vastCacheId, + format, bid.getDealid()); if (resolver == null) { @@ -222,6 +239,7 @@ private Map makeFor(String bidder, Integer height, String cacheId, String vastCacheId, + String format, String dealId) { final KeywordMap keywordMap = new KeywordMap(bidder, winningBid, includeWinners, includeBidderKeys, @@ -247,6 +265,9 @@ private Map makeFor(String bidder, keywordMap.put(HB_CACHE_HOST_KEY, cacheHost); keywordMap.put(HB_CACHE_PATH_KEY, cachePath); } + if (StringUtils.isNotBlank(format) && includeFormat) { + keywordMap.put(HB_FORMAT_KEY, format); + } if (StringUtils.isNotBlank(dealId)) { keywordMap.put(HB_DEAL_KEY, dealId); } @@ -278,7 +299,8 @@ private static String sizeFrom(Integer width, Integer height) { private Map truncateKeys(Map keyValues) { return truncateAttrChars > 0 ? keyValues.entrySet().stream() - .collect(Collectors.toMap(keyValue -> truncateKey(keyValue.getKey()), Map.Entry::getValue)) + .collect(Collectors + .toMap(keyValue -> truncateKey(keyValue.getKey()), Map.Entry::getValue, (key1, key2) -> key1)) : keyValues; } diff --git a/src/main/java/org/prebid/server/auction/VideoRequestFactory.java b/src/main/java/org/prebid/server/auction/VideoRequestFactory.java index f815cf27da7..556eedcb9c8 100644 --- a/src/main/java/org/prebid/server/auction/VideoRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/VideoRequestFactory.java @@ -42,10 +42,13 @@ public class VideoRequestFactory { private final TimeoutResolver timeoutResolver; private final JacksonMapper mapper; - public VideoRequestFactory(int maxRequestSize, boolean enforceStoredRequest, + public VideoRequestFactory(int maxRequestSize, + boolean enforceStoredRequest, VideoStoredRequestProcessor storedRequestProcessor, - AuctionRequestFactory auctionRequestFactory, TimeoutResolver timeoutResolver, + AuctionRequestFactory auctionRequestFactory, + TimeoutResolver timeoutResolver, JacksonMapper mapper) { + this.enforceStoredRequest = enforceStoredRequest; this.maxRequestSize = maxRequestSize; this.storedRequestProcessor = Objects.requireNonNull(storedRequestProcessor); @@ -175,8 +178,9 @@ private Future> createBidRequest(RoutingContext routin BidRequestVideo bidRequestVideo, String storedVideoId, Set podConfigIds) { - return storedRequestProcessor.processVideoRequest(accountIdFrom(bidRequestVideo), storedVideoId, podConfigIds, - bidRequestVideo) + + return storedRequestProcessor.processVideoRequest( + accountIdFrom(bidRequestVideo), storedVideoId, podConfigIds, bidRequestVideo) .map(bidRequestToErrors -> fillImplicitParameters(routingContext, bidRequestToErrors)) .map(this::validateRequest); } diff --git a/src/main/java/org/prebid/server/auction/VideoResponseFactory.java b/src/main/java/org/prebid/server/auction/VideoResponseFactory.java index 2a9083d2385..752b632c2a1 100644 --- a/src/main/java/org/prebid/server/auction/VideoResponseFactory.java +++ b/src/main/java/org/prebid/server/auction/VideoResponseFactory.java @@ -88,7 +88,7 @@ private List adPodsWithTargetingFrom(List bids) { final List adPods = new ArrayList<>(); for (Bid bid : bids) { final Map targeting = targeting(bid); - if (targeting.get("hb_uuid") == null) { + if (findByPrefix(targeting, "hb_uuid") == null) { continue; } final String impId = bid.getImpid(); @@ -99,9 +99,9 @@ private List adPodsWithTargetingFrom(List bids) { final Integer podId = Integer.parseInt(podIdString); final ExtResponseVideoTargeting videoTargeting = ExtResponseVideoTargeting.of( - targeting.get("hb_pb"), - targeting.get("hb_pb_cat_dur"), - targeting.get("hb_uuid")); + findByPrefix(targeting, "hb_pb"), + findByPrefix(targeting, "hb_pb_cat_dur"), + findByPrefix(targeting, "hb_uuid")); ExtAdPod adPod = adPods.stream() .filter(extAdPod -> extAdPod.getPodid().equals(podId)) @@ -130,6 +130,14 @@ private Map targeting(Bid bid) { return targeting != null ? targeting : Collections.emptyMap(); } + private static String findByPrefix(Map keyToValue, String prefix) { + return keyToValue.entrySet().stream() + .filter(keyAndValue -> keyAndValue.getKey().startsWith(prefix)) + .map(Map.Entry::getValue) + .findFirst() + .orElse(null); + } + private static List adPodsWithErrors(List podErrors) { return podErrors.stream() .map(podError -> ExtAdPod.of(podError.getPodId(), null, podError.getPodErrors())) diff --git a/src/main/java/org/prebid/server/auction/VideoStoredRequestProcessor.java b/src/main/java/org/prebid/server/auction/VideoStoredRequestProcessor.java index 0158f6096fa..65f95ba330f 100644 --- a/src/main/java/org/prebid/server/auction/VideoStoredRequestProcessor.java +++ b/src/main/java/org/prebid/server/auction/VideoStoredRequestProcessor.java @@ -16,6 +16,7 @@ import com.iab.openrtb.request.video.PodError; import com.iab.openrtb.request.video.Podconfig; import io.vertx.core.Future; +import io.vertx.core.file.FileSystem; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import org.apache.commons.collections4.CollectionUtils; @@ -27,6 +28,7 @@ import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.execution.TimeoutFactory; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.json.JsonMerger; import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.ExtIncludeBrandCategory; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; @@ -36,7 +38,6 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting; import org.prebid.server.settings.ApplicationSettings; import org.prebid.server.settings.model.StoredDataResult; -import org.prebid.server.util.JsonMergeUtil; import org.prebid.server.validation.VideoRequestValidator; import java.util.ArrayList; @@ -58,37 +59,74 @@ public class VideoStoredRequestProcessor { private static final String DEFAULT_CURRENCY = "USD"; private static final String DEFAULT_BUYERUID = "appnexus"; - private final ApplicationSettings applicationSettings; - private final VideoRequestValidator validator; private final boolean enforceStoredRequest; private final List blacklistedAccounts; + private final long defaultTimeout; + private final String currency; private final BidRequest defaultBidRequest; + private final ApplicationSettings applicationSettings; + private final VideoRequestValidator validator; private final Metrics metrics; private final TimeoutResolver timeoutResolver; private final TimeoutFactory timeoutFactory; - private final long defaultTimeout; - private final String currency; private final JacksonMapper mapper; - private JsonMergeUtil jsonMergeUtil; - - public VideoStoredRequestProcessor(ApplicationSettings applicationSettings, VideoRequestValidator validator, - boolean enforceStoredRequest, List blacklistedAccounts, - BidRequest defaultBidRequest, Metrics metrics, TimeoutFactory timeoutFactory, - TimeoutResolver timeoutResolver, long defaultTimeout, String adServerCurrency, - JacksonMapper mapper) { - this.applicationSettings = Objects.requireNonNull(applicationSettings); - this.validator = Objects.requireNonNull(validator); + private final JsonMerger jsonMerger; + + private VideoStoredRequestProcessor(boolean enforceStoredRequest, + List blacklistedAccounts, + long defaultTimeout, + String adServerCurrency, + BidRequest defaultBidRequest, + ApplicationSettings applicationSettings, + VideoRequestValidator validator, + Metrics metrics, + TimeoutFactory timeoutFactory, + TimeoutResolver timeoutResolver, + JacksonMapper mapper, + JsonMerger jsonMerger) { + this.enforceStoredRequest = enforceStoredRequest; this.blacklistedAccounts = blacklistedAccounts; - this.defaultBidRequest = Objects.requireNonNull(defaultBidRequest); - this.timeoutFactory = Objects.requireNonNull(timeoutFactory); - this.timeoutResolver = Objects.requireNonNull(timeoutResolver); - this.metrics = Objects.requireNonNull(metrics); this.defaultTimeout = defaultTimeout; this.currency = StringUtils.isBlank(adServerCurrency) ? DEFAULT_CURRENCY : adServerCurrency; - this.mapper = Objects.requireNonNull(mapper); + this.defaultBidRequest = defaultBidRequest; + this.applicationSettings = applicationSettings; + this.validator = validator; + this.metrics = metrics; + this.timeoutFactory = timeoutFactory; + this.timeoutResolver = timeoutResolver; + this.mapper = mapper; + this.jsonMerger = jsonMerger; + } - jsonMergeUtil = new JsonMergeUtil(mapper); + public static VideoStoredRequestProcessor create(boolean enforceStoredRequest, + List blacklistedAccounts, + long defaultTimeout, + String adServerCurrency, + String defaultBidRequestPath, + FileSystem fileSystem, + ApplicationSettings applicationSettings, + VideoRequestValidator validator, + Metrics metrics, + TimeoutFactory timeoutFactory, + TimeoutResolver timeoutResolver, + JacksonMapper mapper, + JsonMerger jsonMerger) { + + return new VideoStoredRequestProcessor( + enforceStoredRequest, + Objects.requireNonNull(blacklistedAccounts), + defaultTimeout, + adServerCurrency, + readBidRequest( + defaultBidRequestPath, Objects.requireNonNull(fileSystem), Objects.requireNonNull(mapper)), + Objects.requireNonNull(applicationSettings), + Objects.requireNonNull(validator), + Objects.requireNonNull(metrics), + Objects.requireNonNull(timeoutFactory), + Objects.requireNonNull(timeoutResolver), + Objects.requireNonNull(mapper), + Objects.requireNonNull(jsonMerger)); } /** @@ -109,6 +147,14 @@ Future> processVideoRequest(String accountId, String s String.format("Stored request fetching failed: %s", exception.getMessage())))); } + private static BidRequest readBidRequest( + String defaultBidRequestPath, FileSystem fileSystem, JacksonMapper mapper) { + + return StringUtils.isNotBlank(defaultBidRequestPath) + ? mapper.decodeValue(fileSystem.readFileBlocking(defaultBidRequestPath), BidRequest.class) + : null; + } + private Future updateMetrics(StoredDataResult storedDataResult, Set requestIds, Set impIds) { requestIds.forEach( @@ -143,7 +189,7 @@ private BidRequestVideo mergeBidRequest(BidRequestVideo originalRequest, String } return StringUtils.isNotBlank(storedRequest) - ? jsonMergeUtil.merge(originalRequest, storedRequest, storedRequestId, BidRequestVideo.class) + ? jsonMerger.merge(originalRequest, storedRequest, storedRequestId, BidRequestVideo.class) : originalRequest; } @@ -249,7 +295,9 @@ private static Video updateVideo(Video video, Integer maxDuration, Integer minDu } private BidRequest mergeWithDefaultBidRequest(BidRequestVideo validatedVideoRequest, List imps) { - final BidRequest.BidRequestBuilder bidRequestBuilder = defaultBidRequest.toBuilder(); + final BidRequest.BidRequestBuilder bidRequestBuilder = + defaultBidRequest != null ? defaultBidRequest.toBuilder() : BidRequest.builder(); + final Site site = validatedVideoRequest.getSite(); if (site != null) { final Site.SiteBuilder siteBuilder = site.toBuilder(); diff --git a/src/main/java/org/prebid/server/auction/model/AuctionContext.java b/src/main/java/org/prebid/server/auction/model/AuctionContext.java index 1870839a7a5..6e72ea05484 100644 --- a/src/main/java/org/prebid/server/auction/model/AuctionContext.java +++ b/src/main/java/org/prebid/server/auction/model/AuctionContext.java @@ -1,6 +1,5 @@ package org.prebid.server.auction.model; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.iab.openrtb.request.BidRequest; import io.vertx.ext.web.RoutingContext; import lombok.Builder; @@ -20,15 +19,12 @@ @Value public class AuctionContext { - @JsonIgnore RoutingContext routingContext; - @JsonIgnore UidsCookie uidsCookie; BidRequest bidRequest; - @JsonIgnore Timeout timeout; Account account; diff --git a/src/main/java/org/prebid/server/auction/model/GeneratedBidIds.java b/src/main/java/org/prebid/server/auction/model/GeneratedBidIds.java new file mode 100644 index 00000000000..4eb6e2f6603 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/model/GeneratedBidIds.java @@ -0,0 +1,81 @@ +package org.prebid.server.auction.model; + +import com.iab.openrtb.response.Bid; +import lombok.AllArgsConstructor; +import org.prebid.server.bidder.model.BidderBid; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + + +/** + * Class creates and holds generated bid ids per bidder per imp. + */ +@AllArgsConstructor +public class GeneratedBidIds { + + private static final String BID_IMP_ID_PATTERN = "%s-%s"; + + private final Map> bidderToBidIds; + + /** + * Creates an object of {@link GeneratedBidIds} with {@link Map} where key is a bidder and value is another map, + * that have pair of bidId-impId to uniquely identify the bid withing the bidder and as value has bid Id generated + * by idGenerationStrategy parameter. + */ + public static GeneratedBidIds of(List bidderResponses, + BiFunction idGenerationStrategy) { + final Map> generatedBidIds = bidderResponses.stream() + .collect(Collectors.toMap(BidderResponse::getBidder, + bidderResponse -> bidderResponse.getSeatBid() + .getBids().stream() + .map(BidderBid::getBid) + .collect(Collectors.toMap(bid -> makeUniqueBidId(bid.getId(), bid.getImpid()), + bid -> idGenerationStrategy.apply(bidderResponse.getBidder(), bid))))); + return new GeneratedBidIds(generatedBidIds); + } + + /** + * Returns empty {@link GeneratedBidIds}. + */ + public static GeneratedBidIds empty() { + return new GeneratedBidIds(Collections.emptyMap()); + } + + /** + * Creates unique identifier for bid that consist from pair bidId-impId. + */ + private static String makeUniqueBidId(String bidId, String impId) { + return String.format(BID_IMP_ID_PATTERN, bidId, impId); + } + + /** + * Returns bidder for bidId and impId parameters. + */ + public Optional getBidderForBid(String bidId, String impId) { + final String uniqueBidId = makeUniqueBidId(bidId, impId); + return bidderToBidIds.entrySet().stream() + .filter(bidIdToGeneratedId -> bidIdToGeneratedId.getValue().containsKey(uniqueBidId)) + .findFirst() + .map(Map.Entry::getKey); + } + + /** + * Returns generated id for bid identified by bidder, bidId and impId parameters. + */ + public String getGeneratedId(String bidder, String bidId, String impId) { + if (bidderToBidIds.containsKey(bidder)) { + return bidderToBidIds.get(bidder).get(makeUniqueBidId(bidId, impId)); + } else { + return null; + } + } + + public Map> getBidderToBidIds() { + return bidderToBidIds; + } +} diff --git a/src/main/java/org/prebid/server/bidder/adform/AdformAdapter.java b/src/main/java/org/prebid/server/bidder/adform/AdformAdapter.java index 18b6ca38d8d..a12902ecdbd 100644 --- a/src/main/java/org/prebid/server/bidder/adform/AdformAdapter.java +++ b/src/main/java/org/prebid/server/bidder/adform/AdformAdapter.java @@ -14,7 +14,6 @@ import org.prebid.server.auction.model.PreBidRequestContext; import org.prebid.server.bidder.Adapter; import org.prebid.server.bidder.adform.model.AdformBid; -import org.prebid.server.bidder.adform.model.AdformDigitrust; import org.prebid.server.bidder.adform.model.AdformParams; import org.prebid.server.bidder.adform.model.UrlParameters; import org.prebid.server.bidder.model.AdapterHttpRequest; @@ -53,7 +52,7 @@ public AdformAdapter(String cookieFamilyName, String endpointUrl, JacksonMapper this.mapper = Objects.requireNonNull(mapper); this.requestUtil = new AdformRequestUtil(); - this.httpUtil = new AdformHttpUtil(mapper); + this.httpUtil = new AdformHttpUtil(); } /** @@ -73,7 +72,7 @@ public List> makeHttpRequests(AdapterRequest adapterReq HttpMethod.GET, getUrl(preBidRequestContext, adformParams, extUser), null, - headers(preBidRequestContext, requestUtil.getAdformDigitrust(extUser)))); + headers(preBidRequestContext))); } @Override @@ -198,14 +197,13 @@ private String getUrlFromParams(List adformParams) { /** * Creates adform headers, which stores adform request parameters */ - private MultiMap headers(PreBidRequestContext preBidRequestContext, AdformDigitrust adformDigitrust) { + private MultiMap headers(PreBidRequestContext preBidRequestContext) { return httpUtil.buildAdformHeaders( VERSION, ObjectUtils.defaultIfNull(preBidRequestContext.getUa(), ""), ObjectUtils.defaultIfNull(preBidRequestContext.getIp(), ""), preBidRequestContext.getReferer(), - preBidRequestContext.getUidsCookie().uidFrom(cookieFamilyName), - adformDigitrust); + preBidRequestContext.getUidsCookie().uidFrom(cookieFamilyName)); } /** diff --git a/src/main/java/org/prebid/server/bidder/adform/AdformBidder.java b/src/main/java/org/prebid/server/bidder/adform/AdformBidder.java index b71583b401c..548234336af 100644 --- a/src/main/java/org/prebid/server/bidder/adform/AdformBidder.java +++ b/src/main/java/org/prebid/server/bidder/adform/AdformBidder.java @@ -60,7 +60,7 @@ public AdformBidder(String endpointUrl, JacksonMapper mapper) { this.mapper = Objects.requireNonNull(mapper); this.requestUtil = new AdformRequestUtil(); - this.httpUtil = new AdformHttpUtil(mapper); + this.httpUtil = new AdformHttpUtil(); } /** @@ -98,8 +98,8 @@ public Result>> makeHttpRequests(BidRequest request) { .secure(getSecure(imps)) .gdprApplies(requestUtil.getGdprApplies(request.getRegs())) .consent(requestUtil.getConsent(extUser)) - .currency(currency) .eids(requestUtil.getEids(extUser, mapper)) + .currency(currency) .url(getUrl(extImpAdforms)) .build()); @@ -108,8 +108,7 @@ public Result>> makeHttpRequests(BidRequest request) { getUserAgent(device), getIp(device), getReferer(request.getSite()), - getUserId(user), - requestUtil.getAdformDigitrust(extUser)); + getUserId(user)); return Result.of(Collections.singletonList( HttpRequest.builder() @@ -291,24 +290,43 @@ private List toBidderBid(List adformBids, List imps) for (int i = 0; i < adformBids.size(); i++) { final AdformBid adformBid = adformBids.get(i); - if (StringUtils.isEmpty(adformBid.getBanner()) || !Objects.equals(adformBid.getResponse(), BANNER)) { + final String adm = resolveAdm(adformBid); + if (StringUtils.isBlank(adm)) { continue; } + final BidType bidType = resolveBidType(adformBid.getResponse()); final Imp imp = imps.get(i); bidderBids.add(BidderBid.of(Bid.builder() .id(imp.getId()) .impid(imp.getId()) .price(adformBid.getWinBid()) - .adm(adformBid.getBanner()) + .adm(adm) .w(adformBid.getWidth()) .h(adformBid.getHeight()) .dealid(adformBid.getDealId()) .crid(adformBid.getWinCrid()) .build(), - BidType.banner, + bidType, currency)); } return bidderBids; } + + private String resolveAdm(AdformBid adformBid) { + if (Objects.equals(adformBid.getResponse(), "banner")) { + return adformBid.getBanner(); + } + + if (Objects.equals(adformBid.getResponse(), "vast_content")) { + return adformBid.getVastContent(); + } + + return ""; + } + + private BidType resolveBidType(String response) { + return Objects.equals(response, BANNER) + ? BidType.banner : BidType.video; + } } diff --git a/src/main/java/org/prebid/server/bidder/adform/AdformHttpUtil.java b/src/main/java/org/prebid/server/bidder/adform/AdformHttpUtil.java index e57f0b28dbd..0824215ffae 100644 --- a/src/main/java/org/prebid/server/bidder/adform/AdformHttpUtil.java +++ b/src/main/java/org/prebid/server/bidder/adform/AdformHttpUtil.java @@ -4,17 +4,13 @@ import io.vertx.core.MultiMap; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.bidder.adform.model.AdformDigitrust; import org.prebid.server.bidder.adform.model.UrlParameters; -import org.prebid.server.json.EncodeException; -import org.prebid.server.json.JacksonMapper; import org.prebid.server.util.HttpUtil; import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.stream.Collectors; /** @@ -30,10 +26,7 @@ class AdformHttpUtil { private static final Locale LOCALE = Locale.US; - private final JacksonMapper mapper; - - AdformHttpUtil(JacksonMapper mapper) { - this.mapper = Objects.requireNonNull(mapper); + AdformHttpUtil() { } /** @@ -43,8 +36,7 @@ MultiMap buildAdformHeaders(String version, String userAgent, String ip, String referer, - String userId, - AdformDigitrust adformDigitrust) { + String userId) { final MultiMap headers = MultiMap.caseInsensitiveMultiMap() .add(HttpUtil.CONTENT_TYPE_HEADER, HttpUtil.APPLICATION_JSON_CONTENT_TYPE) @@ -61,18 +53,6 @@ MultiMap buildAdformHeaders(String version, cookieValues.add(String.format("uid=%s", userId)); } - if (adformDigitrust != null) { - try { - final String adformDigitrustEncoded = Base64.getUrlEncoder().withoutPadding() - .encodeToString(mapper.encode(adformDigitrust).getBytes()); - // Cookie name and structure are described here: - // https://github.com/digi-trust/dt-cdn/wiki/Cookies-for-Platforms - cookieValues.add(String.format("DigiTrust.v1.identity=%s", adformDigitrustEncoded)); - } catch (EncodeException e) { - // do not add digitrust to cookie header and just ignore this exception - } - } - if (CollectionUtils.isNotEmpty(cookieValues)) { headers.add(HttpUtil.COOKIE_HEADER, String.join(";", cookieValues)); } diff --git a/src/main/java/org/prebid/server/bidder/adform/AdformRequestUtil.java b/src/main/java/org/prebid/server/bidder/adform/AdformRequestUtil.java index c4a588da015..12b93922912 100644 --- a/src/main/java/org/prebid/server/bidder/adform/AdformRequestUtil.java +++ b/src/main/java/org/prebid/server/bidder/adform/AdformRequestUtil.java @@ -3,12 +3,9 @@ import com.iab.openrtb.request.Regs; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; -import org.prebid.server.bidder.adform.model.AdformDigitrust; -import org.prebid.server.bidder.adform.model.AdformDigitrustPrivacy; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.request.ExtRegs; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust; import org.prebid.server.proto.openrtb.ext.request.ExtUserEid; import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUid; @@ -24,8 +21,6 @@ */ class AdformRequestUtil { - private static final int DIGITRUST_VERSION = 1; - /** * Retrieves gdpr from regs.ext.gdpr and in case of any exception or invalid values returns empty string. */ @@ -44,20 +39,6 @@ String getConsent(ExtUser extUser) { return ObjectUtils.defaultIfNull(gdprConsent, ""); } - /** - * Creates {@link AdformDigitrust} from user.extUser.digitrust, if something wrong, returns null. - */ - AdformDigitrust getAdformDigitrust(ExtUser extUser) { - final ExtUserDigiTrust extUserDigiTrust = extUser != null ? extUser.getDigitrust() : null; - return extUserDigiTrust != null - ? AdformDigitrust.of( - extUserDigiTrust.getId(), - DIGITRUST_VERSION, - extUserDigiTrust.getKeyv(), - AdformDigitrustPrivacy.of(extUserDigiTrust.getPref() != 0)) - : null; - } - /** * Retrieves eids from user.ext.eids and in case of any exception or invalid values return empty collection. */ diff --git a/src/main/java/org/prebid/server/bidder/adform/model/AdformBid.java b/src/main/java/org/prebid/server/bidder/adform/model/AdformBid.java index f6fc6789f9f..64729679896 100644 --- a/src/main/java/org/prebid/server/bidder/adform/model/AdformBid.java +++ b/src/main/java/org/prebid/server/bidder/adform/model/AdformBid.java @@ -24,4 +24,6 @@ public class AdformBid { String dealId; String winCrid; + + String vastContent; } diff --git a/src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrust.java b/src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrust.java deleted file mode 100644 index d6dbe48a2a8..00000000000 --- a/src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrust.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.prebid.server.bidder.adform.model; - -import lombok.AllArgsConstructor; -import lombok.Value; - -@AllArgsConstructor(staticName = "of") -@Value -public class AdformDigitrust { - - String id; - - Integer version; - - Integer keyv; - - AdformDigitrustPrivacy privacy; -} diff --git a/src/main/java/org/prebid/server/bidder/adhese/AdheseBidder.java b/src/main/java/org/prebid/server/bidder/adhese/AdheseBidder.java index be64ac8e752..df9545801f4 100644 --- a/src/main/java/org/prebid/server/bidder/adhese/AdheseBidder.java +++ b/src/main/java/org/prebid/server/bidder/adhese/AdheseBidder.java @@ -11,7 +11,6 @@ import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; -import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; @@ -20,6 +19,7 @@ import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.adhese.model.AdheseBid; import org.prebid.server.bidder.adhese.model.AdheseOriginData; +import org.prebid.server.bidder.adhese.model.AdheseRequestBody; import org.prebid.server.bidder.adhese.model.AdheseResponseExt; import org.prebid.server.bidder.adhese.model.Cpm; import org.prebid.server.bidder.adhese.model.CpmValues; @@ -45,7 +45,6 @@ import java.util.Map; import java.util.Objects; import java.util.TreeMap; -import java.util.stream.Collectors; /** * Adhese {@link Bidder} implementation. @@ -57,9 +56,9 @@ public class AdheseBidder implements Bidder { }; private static final String ORIGIN_BID = "JERLICIA"; - private static final String GDPR_QUERY_PARAMETER = "/xt"; - private static final String REFERER_QUERY_PARAMETER = "/xf"; - private static final String IFA_QUERY_PARAMETER = "/xz"; + private static final String GDPR_QUERY_PARAMETER = "xt"; + private static final String REFERER_QUERY_PARAMETER = "xf"; + private static final String IFA_QUERY_PARAMETER = "xz"; private final String endpointUrl; private final JacksonMapper mapper; @@ -82,12 +81,13 @@ public Result>> makeHttpRequests(BidRequest request) { return Result.withError(BidderError.badInput(e.getMessage())); } - final String uri = buildUrl(request, extImpAdhese); + final String uri = getUrl(extImpAdhese); return Result.of(Collections.singletonList( HttpRequest.builder() .method(HttpMethod.POST) .uri(uri) + .body(mapper.encode(buildBody(request, extImpAdhese))) .headers(replaceHeaders(request.getDevice())) .build()), Collections.emptyList()); @@ -99,7 +99,7 @@ private MultiMap replaceHeaders(Device device) { HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER, device.getUa()); HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpHeaders.createOptimized("X-Real-IP"), device.getIp()); } - return HttpUtil.headers(); + return headers; } private ExtImpAdhese parseImpExt(Imp imp) { @@ -110,14 +110,20 @@ private ExtImpAdhese parseImpExt(Imp imp) { } } - private String buildUrl(BidRequest request, ExtImpAdhese extImpAdhese) { - return String.format("%s%s%s%s%s%s", - getUrl(extImpAdhese), - getSlotParameter(extImpAdhese), - getTargetParameters(extImpAdhese), - getGdprParameter(request.getUser()), - getRefererParameter(request.getSite()), - getIfaParameter(request.getDevice())); + private AdheseRequestBody buildBody(BidRequest request, ExtImpAdhese extImpAdhese) { + final Map> parameterMap = new TreeMap<>(); + parameterMap.putAll(getTargetParameters(extImpAdhese)); + parameterMap.putAll(getGdprParameter(request.getUser())); + parameterMap.putAll(getRefererParameter(request.getSite())); + parameterMap.putAll(getIfaParameter(request.getDevice())); + + return AdheseRequestBody.builder() + .slots(Collections.singletonList( + AdheseRequestBody.Slot.builder() + .slotname(getSlotParameter(extImpAdhese)) + .build())) + .parameters(parameterMap) + .build(); } private String getUrl(ExtImpAdhese extImpAdhese) { @@ -125,21 +131,14 @@ private String getUrl(ExtImpAdhese extImpAdhese) { } private static String getSlotParameter(ExtImpAdhese extImpAdhese) { - return String.format("/sl%s-%s", + return String.format("%s-%s", HttpUtil.encodeUrl(extImpAdhese.getLocation()), HttpUtil.encodeUrl(extImpAdhese.getFormat())); } - private String getTargetParameters(ExtImpAdhese extImpAdhese) { + private Map> getTargetParameters(ExtImpAdhese extImpAdhese) { final JsonNode targets = extImpAdhese.getTargets(); - if (targets == null || targets.isNull()) { - return ""; - } - - final Map> targetParameters = parseTargetParametersAndSort(targets); - return targetParameters.entrySet().stream() - .map(entry -> createPartOrUrl(entry.getKey(), entry.getValue())) - .collect(Collectors.joining()); + return targets == null || targets.isEmpty() ? Collections.emptyMap() : parseTargetParametersAndSort(targets); } private Map> parseTargetParametersAndSort(JsonNode targets) { @@ -148,31 +147,26 @@ private Map> parseTargetParametersAndSort(JsonNode targets) })); } - private static String createPartOrUrl(String key, List values) { - final String formattedValues = String.join(";", values); - return String.format("/%s%s", HttpUtil.encodeUrl(key), formattedValues); - } - - private static String getGdprParameter(User user) { + private static Map> getGdprParameter(User user) { final ExtUser extUser = user != null ? user.getExt() : null; final String consent = extUser != null ? extUser.getConsent() : null; return StringUtils.isNotBlank(consent) - ? String.format("%s%s", GDPR_QUERY_PARAMETER, consent) - : ""; + ? Collections.singletonMap(GDPR_QUERY_PARAMETER, Collections.singletonList(consent)) + : Collections.emptyMap(); } - private static String getRefererParameter(Site site) { + private static Map> getRefererParameter(Site site) { final String page = site != null ? site.getPage() : null; return StringUtils.isNotBlank(page) - ? String.format("%s%s", REFERER_QUERY_PARAMETER, HttpUtil.encodeUrl(page)) - : ""; + ? Collections.singletonMap(REFERER_QUERY_PARAMETER, Collections.singletonList(page)) + : Collections.emptyMap(); } - private static String getIfaParameter(Device device) { + private static Map> getIfaParameter(Device device) { final String ifa = device != null ? device.getIfa() : null; return StringUtils.isNotBlank(ifa) - ? String.format("%s%s", IFA_QUERY_PARAMETER, HttpUtil.encodeUrl(ifa)) - : ""; + ? Collections.singletonMap(IFA_QUERY_PARAMETER, Collections.singletonList(ifa)) + : Collections.emptyMap(); } @Override diff --git a/src/main/java/org/prebid/server/bidder/adhese/model/AdheseRequestBody.java b/src/main/java/org/prebid/server/bidder/adhese/model/AdheseRequestBody.java new file mode 100644 index 00000000000..608f79d905b --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/adhese/model/AdheseRequestBody.java @@ -0,0 +1,22 @@ +package org.prebid.server.bidder.adhese.model; + +import lombok.Builder; +import lombok.Value; + +import java.util.List; +import java.util.Map; + +@Builder +@Value +public class AdheseRequestBody { + + List slots; + Map> parameters; + + @Builder + @Value + public static class Slot { + + String slotname; + } +} diff --git a/src/main/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidder.java b/src/main/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidder.java index ec3c133098e..91d95e67ae2 100644 --- a/src/main/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidder.java +++ b/src/main/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidder.java @@ -12,6 +12,8 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -26,8 +28,6 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.HttpUtil; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -45,6 +45,9 @@ public class AdkernelAdnBidder implements Bidder { private static final TypeReference> ADKERNELADN_EXT_TYPE_REFERENCE = new TypeReference>() { }; + private static final String DEFAULT_DOMAIN = "tag.adkernel.com"; + private static final String URL_HOST_MACRO = "{{Host}}"; + private static final String URL_PUBLISHER_ID_MACRO = "{{PublisherID}}"; private final String endpointUrl; private final JacksonMapper mapper; @@ -56,39 +59,36 @@ public AdkernelAdnBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - final List imps = bidRequest.getImp(); - + final List validImps = bidRequest.getImp(); final List errors = new ArrayList<>(); - final List> httpRequests = new ArrayList<>(); - try { - final List impExts = getAndValidateImpExt(imps); - final Map> pubToImps = dispatchImpressions(imps, impExts); - httpRequests.addAll(buildAdapterRequests(bidRequest, pubToImps, endpointUrl)); - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - } - return Result.of(httpRequests, errors); - } + final Map impWithExts = getAndValidateImpExt(validImps, errors); + final Map> pubToImps = dispatchImpressions(impWithExts, errors); + if (MapUtils.isEmpty(pubToImps)) { + return Result.withErrors(errors); + } - private static MultiMap headers() { - return HttpUtil.headers() - .add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5"); + return Result.of(buildAdapterRequests(bidRequest, pubToImps), errors); } - private List getAndValidateImpExt(List imps) { - return imps.stream() - .map(AdkernelAdnBidder::validateImp) - .map(this::parseAndValidateAdkernelAdnExt) - .collect(Collectors.toList()); + private Map getAndValidateImpExt(List imps, List errors) { + final Map validImpsWithExts = new HashMap<>(); + for (Imp imp : imps) { + try { + validateImp(imp); + validImpsWithExts.put(imp, parseAndValidateAdkernelAdnExt(imp)); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + return validImpsWithExts; } - private static Imp validateImp(Imp imp) { + private static void validateImp(Imp imp) { if (imp.getBanner() == null && imp.getVideo() == null) { throw new PreBidException(String.format("Invalid imp with id=%s. Expected imp.banner or imp.video", imp.getId())); } - return imp; } private ExtImpAdkernelAdn parseAndValidateAdkernelAdnExt(Imp imp) { @@ -105,41 +105,42 @@ private ExtImpAdkernelAdn parseAndValidateAdkernelAdnExt(Imp imp) { return adkernelAdnExt; } - /** - * Group impressions by AdKernel-specific parameters `pubId` & `host`. - */ - private static Map> dispatchImpressions(List imps, - List impExts) { + private static Map> dispatchImpressions(Map impsWithExts, + List errors) { final Map> result = new HashMap<>(); - for (int i = 0; i < imps.size(); i++) { - final Imp imp = compatImpression(imps.get(i)); - final ExtImpAdkernelAdn impExt = impExts.get(i); + for (Imp key : impsWithExts.keySet()) { + final Imp imp; + try { + imp = compatImpression(key); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + continue; + } + final ExtImpAdkernelAdn impExt = impsWithExts.get(key); result.putIfAbsent(impExt, new ArrayList<>()); result.get(impExt).add(imp); } + return result; } - /** - * Alter impression info to comply with adkernel platform requirements. - */ private static Imp compatImpression(Imp imp) { final Imp.ImpBuilder impBuilder = imp.toBuilder(); - impBuilder.ext(null); // do not forward ext to adkernel platform + impBuilder.ext(null); final Banner banner = imp.getBanner(); if (banner != null) { - return compatBannerImpression(impBuilder, banner); + compatBannerImpression(impBuilder, banner); } - return impBuilder.audio(null) + return impBuilder + .audio(null) .xNative(null) .build(); } - private static Imp compatBannerImpression(Imp.ImpBuilder impBuilder, Banner compatBanner) { + private static void compatBannerImpression(Imp.ImpBuilder impBuilder, Banner compatBanner) { if (compatBanner.getW() == null && compatBanner.getH() == null) { - // As banner.w/h are required fields for adkernel adn platform - take the first format entry final List compatBannerFormat = compatBanner.getFormat(); if (CollectionUtils.isEmpty(compatBannerFormat)) { @@ -161,32 +162,32 @@ private static Imp compatBannerImpression(Imp.ImpBuilder impBuilder, Banner comp impBuilder.banner(bannerBuilder.build()); } - return impBuilder.video(null) - .audio(null) - .xNative(null) - .build(); + impBuilder.video(null); } private List> buildAdapterRequests(BidRequest preBidRequest, - Map> pubToImps, - String endpointUrl) { + Map> pubToImps) { final List> result = new ArrayList<>(); for (Map.Entry> entry : pubToImps.entrySet()) { - final BidRequest outgoingRequest = createBidRequest(preBidRequest, entry.getValue()); - final String body = mapper.encode(outgoingRequest); - result.add(HttpRequest.builder() - .method(HttpMethod.POST) - .uri(buildEndpoint(entry.getKey(), endpointUrl)) - .body(body) - .headers(headers()) - .payload(outgoingRequest) - .build()); + result.add(createRequest(entry.getKey(), entry.getValue(), preBidRequest)); } return result; } + private HttpRequest createRequest(ExtImpAdkernelAdn extImp, List imps, BidRequest preBidRequest) { + final BidRequest outgoingRequest = createBidRequest(preBidRequest, imps); + final String body = mapper.encode(outgoingRequest); + return HttpRequest.builder() + .method(HttpMethod.POST) + .uri(buildEndpoint(extImp)) + .body(body) + .headers(headers()) + .payload(outgoingRequest) + .build(); + } + private static BidRequest createBidRequest(BidRequest preBidRequest, List imps) { final BidRequest.BidRequestBuilder bidRequestBuilder = preBidRequest.toBuilder() .imp(imps); @@ -203,27 +204,18 @@ private static BidRequest createBidRequest(BidRequest preBidRequest, List i return bidRequestBuilder.build(); } - /** - * Builds endpoint url based on adapter-specific pub settings from imp.ext. - */ - private static String buildEndpoint(ExtImpAdkernelAdn impExt, String endpointUrl) { - final String updatedEndpointUrl; + private String buildEndpoint(ExtImpAdkernelAdn impExt) { + final String impHost = impExt.getHost(); + final String host = StringUtils.isNotBlank(impHost) ? impHost : DEFAULT_DOMAIN; - if (impExt.getHost() != null) { - final URL url; - try { - url = new URL(endpointUrl); - } catch (MalformedURLException e) { - throw new PreBidException( - String.format("Error occurred while parsing AdkernelAdn endpoint url: %s", endpointUrl), e); - } - final String currentHostAndPort = url.getHost() + (url.getPort() == -1 ? "" : ":" + url.getPort()); - updatedEndpointUrl = endpointUrl.replace(currentHostAndPort, impExt.getHost()); - } else { - updatedEndpointUrl = endpointUrl; - } + return endpointUrl + .replace(URL_HOST_MACRO, host) + .replace(URL_PUBLISHER_ID_MACRO, impExt.getPubId().toString()); + } - return String.format("%s%s", updatedEndpointUrl, impExt.getPubId()); + private static MultiMap headers() { + return HttpUtil.headers() + .add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5"); } @Override @@ -249,14 +241,13 @@ private static List extractBids(BidRequest bidRequest, BidResponse bi private static List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { return bidResponse.getSeatbid().stream() .map(SeatBid::getBid) + .filter(Objects::nonNull) .flatMap(Collection::stream) + .filter(Objects::nonNull) .map(bid -> BidderBid.of(bid, getType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) .collect(Collectors.toList()); } - /** - * Figures out which media type this bid is for. - */ private static BidType getType(String impId, List imps) { for (Imp imp : imps) { if (imp.getId().equals(impId) && imp.getBanner() != null) { diff --git a/src/main/java/org/prebid/server/bidder/adman/AdmanBidder.java b/src/main/java/org/prebid/server/bidder/adman/AdmanBidder.java index 45634bf5a4d..229d937d20c 100644 --- a/src/main/java/org/prebid/server/bidder/adman/AdmanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adman/AdmanBidder.java @@ -3,8 +3,12 @@ import com.iab.openrtb.request.Imp; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.OpenrtbBidder; +import org.prebid.server.exception.PreBidException; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.request.adman.ExtImpAdman; +import org.prebid.server.proto.openrtb.ext.response.BidType; + +import java.util.List; /** * Adman {@link Bidder} implementation. @@ -21,4 +25,17 @@ protected Imp modifyImp(Imp imp, ExtImpAdman impExt) { .tagid(impExt.getTagId()) .build(); } + + @Override + protected BidType getBidType(String impId, List imps) { + for (Imp imp : imps) { + if (imp.getId().equals(impId)) { + if (imp.getBanner() == null && imp.getVideo() != null) { + return BidType.video; + } + return BidType.banner; + } + } + throw new PreBidException(String.format("Failed to find impression %s", impId)); + } } diff --git a/src/main/java/org/prebid/server/bidder/admixer/AdmixerBidder.java b/src/main/java/org/prebid/server/bidder/admixer/AdmixerBidder.java index eb2d1a8bda1..0ab9489dc9c 100644 --- a/src/main/java/org/prebid/server/bidder/admixer/AdmixerBidder.java +++ b/src/main/java/org/prebid/server/bidder/admixer/AdmixerBidder.java @@ -9,6 +9,7 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -54,11 +55,6 @@ public Result>> makeHttpRequests(BidRequest request final List errors = new ArrayList<>(); final List validImps = new ArrayList<>(); - if (CollectionUtils.isEmpty(request.getImp())) { - errors.add(BidderError.badInput("No valid impressions in the bid request")); - return Result.withErrors(errors); - } - for (Imp imp : request.getImp()) { try { final ExtImpAdmixer extImp = parseImpExt(imp); @@ -83,21 +79,25 @@ public Result>> makeHttpRequests(BidRequest request } private ExtImpAdmixer parseImpExt(Imp imp) { + final ExtImpAdmixer extImpAdmixer; try { - return mapper.mapper().convertValue(imp.getExt(), ADMIXER_EXT_TYPE_REFERENCE).getBidder(); + extImpAdmixer = mapper.mapper().convertValue(imp.getExt(), ADMIXER_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); + throw new PreBidException(String.format("Wrong Admixer bidder ext in imp with id : %s", imp.getId())); } - } - - private Imp processImp(Imp imp, ExtImpAdmixer extImpAdmixer) { - if (extImpAdmixer.getZone().length() != 36) { + if (StringUtils.length(extImpAdmixer.getZone()) != 36) { throw new PreBidException("ZoneId must be UUID/GUID"); } + return extImpAdmixer; + } + + private Imp processImp(Imp imp, ExtImpAdmixer extImpAdmixer) { + final Double extImpFloor = extImpAdmixer.getCustomFloor(); + final BigDecimal customFloor = extImpFloor != null ? BigDecimal.valueOf(extImpFloor) : BigDecimal.ZERO; return imp.toBuilder() .tagid(extImpAdmixer.getZone()) - .bidfloor(BigDecimal.valueOf(extImpAdmixer.getCustomFloor())) + .bidfloor(customFloor) .ext(makeImpExt(extImpAdmixer.getCustomParams())) .build(); } @@ -117,7 +117,8 @@ public Result> makeBids(HttpCall httpCall, BidReques return Result.withError(BidderError.badServerResponse(e.getMessage())); } - if (CollectionUtils.isEmpty(bidResponse.getSeatbid()) + if (bidResponse == null + || CollectionUtils.isEmpty(bidResponse.getSeatbid()) || CollectionUtils.isEmpty(bidResponse.getSeatbid().get(0).getBid())) { return Result.empty(); } 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 b7f25f11ba7..b8842478526 100644 --- a/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java +++ b/src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java @@ -44,6 +44,7 @@ public class AdopplerBidder implements Bidder { private static final TypeReference> ADOPPLER_EXT_TYPE_REFERENCE = new TypeReference>() { }; + private static final String DEFAULT_CLIENT = "app"; private final String endpointTemplate; private final JacksonMapper mapper; @@ -63,9 +64,9 @@ public Result>> makeHttpRequests(BidRequest request final ExtImpAdoppler validExtImp = parseAndValidateImpExt(imp); final String updateRequestId = request.getId() + "-" + validExtImp.getAdunit(); final BidRequest updateRequest = request.toBuilder().id(updateRequestId).build(); - final String uri = String.format("%s/processHeaderBid/%s", endpointTemplate, - HttpUtil.encodeUrl(validExtImp.getAdunit())); - result.add(createSingleRequest(imp, updateRequest, uri)); + final String url = resolveUrl(validExtImp); + + result.add(createSingleRequest(imp, updateRequest, url)); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); } @@ -78,14 +79,30 @@ private ExtImpAdoppler parseAndValidateImpExt(Imp imp) { try { extImpAdoppler = mapper.mapper().convertValue(imp.getExt(), ADOPPLER_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); + 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; } + private String resolveUrl(ExtImpAdoppler extImp) { + final String client = extImp.getClient(); + + try { + final String accountIdMacro = StringUtils.isBlank(client) + ? DEFAULT_CLIENT + : HttpUtil.encodeUrl(client); + + return endpointTemplate + .replace("{{AccountID}}", accountIdMacro) + .replace("{{AdUnit}}", HttpUtil.encodeUrl(extImp.getAdunit())); + } catch (Exception e) { + throw new PreBidException(e.getMessage()); + } + } + private HttpRequest createSingleRequest(Imp imp, BidRequest request, String url) { final BidRequest outgoingRequest = request.toBuilder().imp(Collections.singletonList(imp)).build(); final String body = mapper.encode(outgoingRequest); @@ -121,24 +138,25 @@ private BidResponse decodeBodyToBidResponse(HttpCall httpCall) { try { return mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); } catch (DecodeException e) { - throw new PreBidException(e.getMessage(), e); + throw new PreBidException(String.format("invalid body: %s", e.getMessage())); } } private Map getImpTypes(BidRequest bidRequest) { final Map impTypes = new HashMap<>(); for (Imp imp : bidRequest.getImp()) { - if (impTypes.get(imp.getId()) != null) { - throw new PreBidException(String.format("duplicate $.imp.id %s", imp.getId())); + final String impId = imp.getId(); + if (impTypes.get(impId) != null) { + throw new PreBidException(String.format("duplicate $.imp.id %s", impId)); } if (imp.getBanner() != null) { - impTypes.put(imp.getId(), BidType.banner); + impTypes.put(impId, BidType.banner); } else if (imp.getVideo() != null) { - impTypes.put(imp.getId(), BidType.video); + impTypes.put(impId, BidType.video); } else if (imp.getAudio() != null) { - impTypes.put(imp.getId(), BidType.audio); + impTypes.put(impId, BidType.audio); } else if (imp.getXNative() != null) { - impTypes.put(imp.getId(), BidType.xNative); + impTypes.put(impId, BidType.xNative); } else { throw new PreBidException("one of $.imp.banner, $.imp.video, $.imp.audio " + "and $.imp.native field required"); @@ -149,7 +167,7 @@ private Map getImpTypes(BidRequest bidRequest) { private BidderBid createBid(Bid bid, Map impTypes, String currency) { if (impTypes.get(bid.getImpid()) == null) { - throw new PreBidException(String.format("unknown impid: %s", bid.getImpid())); + throw new PreBidException(String.format("unknown impId: %s", bid.getImpid())); } validateResponseVideoExt(bid, impTypes); return BidderBid.of(bid, impTypes.get(bid.getImpid()), currency); diff --git a/src/main/java/org/prebid/server/bidder/amx/AmxBidder.java b/src/main/java/org/prebid/server/bidder/amx/AmxBidder.java new file mode 100644 index 00000000000..602f47ab5d6 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/amx/AmxBidder.java @@ -0,0 +1,247 @@ +package org.prebid.server.bidder.amx; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Publisher; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.utils.URIBuilder; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.amx.model.AmxBidExt; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.amx.ExtImpAmx; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * AMX {@link Bidder} implementation. + */ +public class AmxBidder implements Bidder { + + private static final TypeReference> AMX_EXT_TYPE_REFERENCE = + new TypeReference>() { + }; + + private static final String ADAPTER_VERSION = "pbs1.0"; + private static final String VERSION_PARAM = "v"; + private static final String VAST_SEARCH_POINT = ""; + private static final String VAST_IMPRESSION_FORMAT = ""; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public AmxBidder(String endpointUrl, JacksonMapper mapper) { + this.mapper = Objects.requireNonNull(mapper); + this.endpointUrl = new URIBuilder() + .setPath(HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl))) + .addParameter(VERSION_PARAM, ADAPTER_VERSION) + .toString(); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + final List modifiedImps = new ArrayList<>(); + final List errors = new ArrayList<>(); + String publisherId = null; + for (Imp imp : request.getImp()) { + try { + final ExtImpAmx extImpAmx = parseImpExt(imp); + final String tagId = extImpAmx.getTagId(); + if (StringUtils.isNotBlank(tagId)) { + publisherId = tagId; + } + + final String adUnitId = extImpAmx.getAdUnitId(); + if (StringUtils.isNotBlank(adUnitId)) { + modifiedImps.add(imp.toBuilder().tagid(adUnitId).build()); + } + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + + final BidRequest outgoingRequest = createOutgoingRequest(request, publisherId, modifiedImps); + + return Result.of(Collections.singletonList( + HttpRequest.builder() + .method(HttpMethod.POST) + .uri(endpointUrl) + .headers(HttpUtil.headers()) + .payload(outgoingRequest) + .body(mapper.encode(outgoingRequest)) + .build()), errors); + } + + private ExtImpAmx parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), AMX_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private BidRequest createOutgoingRequest(BidRequest request, String publisherId, List imps) { + final BidRequest.BidRequestBuilder outgoingRequest = request.toBuilder(); + + if (StringUtils.isNotBlank(publisherId)) { + final App app = request.getApp(); + if (app != null) { + outgoingRequest + .app(app.toBuilder() + .publisher(resolvePublisher(app.getPublisher(), publisherId)) + .build()); + } + + final Site site = request.getSite(); + if (site != null) { + outgoingRequest + .site(site.toBuilder() + .publisher(resolvePublisher(site.getPublisher(), publisherId)) + .build()); + } + } + + return outgoingRequest.imp(imps).build(); + } + + private Publisher resolvePublisher(Publisher publisher, String publisherId) { + return publisher != null + ? publisher.toBuilder().id(publisherId).build() + : Publisher.builder().id(publisherId).build(); + } + + @Override + public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + final List errors = new ArrayList<>(); + return Result.of(extractBids(bidResponse, errors), errors); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidResponse bidResponse, List errors) { + if (bidResponse == null || bidResponse.getSeatbid() == null) { + return Collections.emptyList(); + } + return bidsFromResponse(bidResponse, errors); + } + + private List bidsFromResponse(BidResponse bidResponse, List errors) { + return bidResponse.getSeatbid().stream() + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(bid -> createBidderBid(bid, bidResponse.getCur(), errors)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private BidderBid createBidderBid(Bid bid, String cur, List errors) { + AmxBidExt amxBidExt = null; + try { + amxBidExt = parseBidderExt(bid.getExt()); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + + final BidType bidType = getMediaType(amxBidExt); + + final Bid updatedBid; + try { + updatedBid = bidType == BidType.video ? updateVideoBid(bid, amxBidExt) : bid; + } catch (PreBidException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + + return BidderBid.of(updatedBid, bidType, cur); + } + + private AmxBidExt parseBidderExt(ObjectNode ext) { + if (ext == null || StringUtils.isBlank(ext.toPrettyString())) { + return AmxBidExt.of(null, null); + } + + try { + return mapper.mapper().convertValue(ext, AmxBidExt.class); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private static BidType getMediaType(AmxBidExt bidExt) { + return StringUtils.isNotBlank(bidExt.getStartDelay()) + ? BidType.video + : BidType.banner; + } + + private static Bid updateVideoBid(Bid bid, AmxBidExt bidExt) { + final String adm = bid.getAdm(); + final String bidId = bid.getId(); + validateAdm(adm, bidId); + return bid.toBuilder() + .nurl("") + .adm(updateAdm(bidExt, bid.getNurl(), adm, bidId)) + .build(); + } + + private static void validateAdm(String adm, String bidId) { + if (StringUtils.isBlank(adm)) { + throw new PreBidException(String.format("Adm should not be blank in bidder: %s", bidId)); + } + + if (!adm.contains(VAST_SEARCH_POINT)) { + throw new PreBidException(String.format("Adm should contain vast search point in bidder: %s", bidId)); + } + } + + private static String updateAdm(AmxBidExt bidExt, String nurl, String adm, String bidId) { + final StringBuilder updatedAdm = new StringBuilder(); + validateAdm(adm, bidId); + + int lastInd = adm.lastIndexOf(VAST_SEARCH_POINT); + + updatedAdm.append(adm, 0, lastInd + VAST_SEARCH_POINT.length()); + addValueIfNotEmpty(nurl, updatedAdm); + + for (String himp : bidExt.getHimp()) { + addValueIfNotEmpty(himp, updatedAdm); + } + + return updatedAdm + .append(adm.substring(lastInd + VAST_SEARCH_POINT.length())) + .toString(); + } + + private static void addValueIfNotEmpty(String value, StringBuilder dest) { + if (StringUtils.isNotBlank(value)) { + dest.append(String.format(VAST_IMPRESSION_FORMAT, value)); + } + } +} + diff --git a/src/main/java/org/prebid/server/bidder/amx/model/AmxBidExt.java b/src/main/java/org/prebid/server/bidder/amx/model/AmxBidExt.java new file mode 100644 index 00000000000..1e00531db78 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/amx/model/AmxBidExt.java @@ -0,0 +1,21 @@ +package org.prebid.server.bidder.amx.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +import java.util.List; + +@AllArgsConstructor(staticName = "of") +@Value +public class AmxBidExt { + + @JsonProperty("himp") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + List himp; + + @JsonProperty("startdelay") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + String startDelay; +} diff --git a/src/main/java/org/prebid/server/bidder/appnexus/AppnexusBidder.java b/src/main/java/org/prebid/server/bidder/appnexus/AppnexusBidder.java index 085b5e40312..52568155086 100644 --- a/src/main/java/org/prebid/server/bidder/appnexus/AppnexusBidder.java +++ b/src/main/java/org/prebid/server/bidder/appnexus/AppnexusBidder.java @@ -256,7 +256,7 @@ private static boolean isIncludeBrandCategory(ExtRequest extRequest) { final ExtIncludeBrandCategory includebrandcategory = targeting != null ? targeting.getIncludebrandcategory() : null; - return includebrandcategory != null && includebrandcategory.getPrimaryAdserver() != 0; + return includebrandcategory != null; } private List> splitHttpRequests(BidRequest outgoingRequest, List processedImps, diff --git a/src/main/java/org/prebid/server/bidder/conversant/ConversantBidder.java b/src/main/java/org/prebid/server/bidder/conversant/ConversantBidder.java index cbf86bbddbe..3e3a1b65049 100644 --- a/src/main/java/org/prebid/server/bidder/conversant/ConversantBidder.java +++ b/src/main/java/org/prebid/server/bidder/conversant/ConversantBidder.java @@ -7,11 +7,11 @@ import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Site; import com.iab.openrtb.request.Video; +import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; @@ -27,13 +27,12 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.HttpUtil; -import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -42,8 +41,8 @@ */ public class ConversantBidder implements Bidder { - private static final TypeReference> CONVERSANT_EXT_TYPE_REFERENCE = new - TypeReference>() { + private static final TypeReference> CONVERSANT_EXT_TYPE_REFERENCE = + new TypeReference>() { }; // List of API frameworks supported by the publisher @@ -56,200 +55,189 @@ public class ConversantBidder implements Bidder { private static final Set AD_POSITIONS = IntStream.range(0, 8).boxed().collect(Collectors.toSet()); private static final String DISPLAY_MANAGER = "prebid-s2s"; - private static final String DISPLAY_MANAGER_VER = "1.0.1"; + private static final String DISPLAY_MANAGER_VER = "2.0.0"; private final String endpointUrl; + private final boolean generateBidId; private final JacksonMapper mapper; - public ConversantBidder(String endpointUrl, JacksonMapper mapper) { + public ConversantBidder(String endpointUrl, boolean generateBidId, JacksonMapper mapper) { this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.generateBidId = generateBidId; this.mapper = Objects.requireNonNull(mapper); } @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - final List errors = new ArrayList<>(); final BidRequest outgoingRequest; try { - outgoingRequest = createBidRequest(bidRequest, errors); + outgoingRequest = createOutgoingRequest(bidRequest); } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - return Result.withErrors(errors); + return Result.withError(BidderError.badInput(e.getMessage())); } - final String body = mapper.encode(outgoingRequest); - return Result.of(Collections.singletonList( HttpRequest.builder() .method(HttpMethod.POST) .uri(endpointUrl) - .body(body) .headers(HttpUtil.headers()) + .body(mapper.encode(outgoingRequest)) .payload(outgoingRequest) .build()), - errors); + Collections.emptyList()); } - private BidRequest createBidRequest(BidRequest bidRequest, List errors) { + private BidRequest createOutgoingRequest(BidRequest bidRequest) { final List modifiedImps = new ArrayList<>(); - Integer extMobile = null; - String extSiteId = null; - - for (Imp imp : bidRequest.getImp()) { - try { - validateImp(imp); - final ExtImpConversant impExt = parseImpExt(imp); - modifiedImps.add(modifyImp(imp, impExt)); - if (StringUtils.isNotEmpty(impExt.getSiteId())) { - extSiteId = impExt.getSiteId(); - } - if (impExt.getMobile() != null) { - extMobile = impExt.getMobile(); - } - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - } - } - if (modifiedImps.isEmpty()) { - throw new PreBidException("No valid impressions"); + final List requestImps = bidRequest.getImp(); + for (int i = 0; i < requestImps.size(); i++) { + final Imp imp = requestImps.get(i); + final ExtImpConversant impExt = parseImpExt(imp, i); + modifiedImps.add(modifyImp(imp, impExt)); } - final Site site = bidRequest.getSite(); - final App app = bidRequest.getApp(); - validateSiteAppId(extSiteId, site, app); - - final BidRequest.BidRequestBuilder requestBuilder = bidRequest.toBuilder() - .imp(modifiedImps); - - if (site != null) { - if (StringUtils.isEmpty(site.getId()) || extMobile != null) { - final String siteId = ObjectUtils.defaultIfNull(site.getId(), extSiteId); - requestBuilder.site(site.toBuilder().id(siteId).mobile(extMobile).build()); - } - } else if (app != null) { - if (StringUtils.isEmpty(app.getId())) { - requestBuilder.app(app.toBuilder().id(extSiteId).build()); - } - } + final Imp firstImp = requestImps.get(0); + final ExtImpConversant extImp = parseImpExt(firstImp, 0); + final String siteId = extImp.getSiteId(); + final Site requestSite = bidRequest.getSite(); + final App requestApp = bidRequest.getApp(); - return requestBuilder.build(); + return bidRequest.toBuilder() + .site(updateSite(requestSite, siteId)) + .app(requestSite == null ? updateApp(requestApp, siteId) : requestApp) + .imp(modifiedImps) + .build(); } - private void validateSiteAppId(String extSiteId, Site site, App app) { - if (site != null && StringUtils.isEmpty(site.getId()) && StringUtils.isEmpty(extSiteId)) { - throw new PreBidException("Missing site id"); + private ExtImpConversant parseImpExt(Imp imp, int impIndex) { + final ExtImpConversant extImp; + try { + extImp = mapper.mapper().convertValue(imp.getExt(), CONVERSANT_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(String.format("Impression[%d] missing ext.bidder object", impIndex)); } - if (app != null && StringUtils.isEmpty(app.getId()) && StringUtils.isEmpty(extSiteId)) { - throw new PreBidException("Missing app id"); + if (StringUtils.isEmpty(extImp.getSiteId())) { + throw new PreBidException(String.format("Impression[%d] requires ext.bidder.site_id", impIndex)); } + return extImp; } - private static void validateImp(Imp imp) { - if (imp.getBanner() == null && imp.getVideo() == null) { - throw new PreBidException(String.format("Invalid MediaType. Conversant supports only Banner and Video. " - + "Ignoring ImpID=%s", imp.getId())); - } + private static Site updateSite(Site site, String siteId) { + return site == null ? null : site.toBuilder().id(siteId).build(); } - private ExtImpConversant parseImpExt(Imp imp) { - try { - return mapper.mapper().convertValue(imp.getExt(), CONVERSANT_EXT_TYPE_REFERENCE).getBidder(); - } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); - } + private static App updateApp(App app, String siteId) { + return app == null ? null : app.toBuilder().id(siteId).build(); } private static Imp modifyImp(Imp imp, ExtImpConversant impExt) { - final BigDecimal extBidfloor = impExt.getBidfloor(); - final String extTagId = impExt.getTagId(); - final Integer extSecure = impExt.getSecure(); - final boolean shouldChangeSecure = extSecure != null && (imp.getSecure() == null || imp.getSecure() == 0); - final Banner impBanner = imp.getBanner(); - final Integer extPosition = impExt.getPosition(); - final Video impVideo = imp.getVideo(); - - final Imp.ImpBuilder impBuilder = imp.toBuilder(); - - if (impBanner != null && extPosition != null) { - impBuilder.banner(impBanner.toBuilder().pos(extPosition).build()); - } + final Banner banner = imp.getBanner(); + final Video video = imp.getVideo(); - return impBuilder + return imp.toBuilder() .displaymanager(DISPLAY_MANAGER) .displaymanagerver(DISPLAY_MANAGER_VER) - .bidfloor(extBidfloor != null ? extBidfloor : imp.getBidfloor()) - .tagid(extTagId != null ? extTagId : imp.getTagid()) - .secure(shouldChangeSecure ? extSecure : imp.getSecure()) - .video(impVideo != null ? modifyVideo(impVideo, impExt) : null) + .bidfloor(impExt.getBidfloor()) + .tagid(impExt.getTagId()) + .secure(getSecure(imp, impExt)) + .banner(modifyBanner(banner, impExt.getPosition())) + .video(video != null && banner == null ? modifyVideo(video, impExt) : video) + .build(); + } + + private static Integer getSecure(Imp imp, ExtImpConversant impExt) { + final Integer extSecure = impExt.getSecure(); + final Integer impSecure = imp.getSecure(); + + return extSecure != null && (impSecure == null || impSecure == 0) ? extSecure : impSecure; + } + + private static Banner modifyBanner(Banner impBanner, Integer extPosition) { + return impBanner == null + ? null + : impBanner.toBuilder() + .pos(isValidPosition(extPosition) ? extPosition : null) .build(); } private static Video modifyVideo(Video video, ExtImpConversant impExt) { final List extMimes = impExt.getMimes(); - final Integer extMaxduration = impExt.getMaxduration(); + final Integer extMaxDuration = impExt.getMaxduration(); final Integer extPosition = impExt.getPosition(); - final List extProtocols = impExt.getProtocols(); - final List extApi = impExt.getApi(); return video.toBuilder() - .mimes(extMimes != null ? extMimes : video.getMimes()) - .maxduration(extMaxduration != null ? extMaxduration : video.getMaxduration()) - .pos(makePosition(extPosition, video.getPos())) - .api(makeApi(extApi, video.getApi())) - .protocols(makeProtocols(extProtocols, video.getProtocols())) + .mimes(CollectionUtils.isNotEmpty(extMimes) ? extMimes : video.getMimes()) + .maxduration(extMaxDuration != null ? extMaxDuration : video.getMaxduration()) + .pos(isValidPosition(extPosition) ? extPosition : null) + .api(makeApi(impExt.getApi(), video.getApi())) + .protocols(makeProtocols(impExt.getProtocols(), video.getProtocols())) .build(); } - private static Integer makePosition(Integer position, Integer videoPos) { - return isValidPosition(position) ? position : isValidPosition(videoPos) ? videoPos : null; - } - private static boolean isValidPosition(Integer position) { return position != null && AD_POSITIONS.contains(position); } private static List makeApi(List extApi, List videoApi) { - final List protocols = CollectionUtils.isNotEmpty(extApi) ? extApi : videoApi; - return CollectionUtils.isNotEmpty(protocols) - ? protocols.stream().filter(APIS::contains).collect(Collectors.toList()) : videoApi; + final List api = CollectionUtils.isNotEmpty(extApi) ? extApi : videoApi; + return CollectionUtils.isNotEmpty(api) + ? api.stream().filter(APIS::contains).collect(Collectors.toList()) + : videoApi; } private static List makeProtocols(List extProtocols, List videoProtocols) { final List protocols = CollectionUtils.isNotEmpty(extProtocols) ? extProtocols : videoProtocols; return CollectionUtils.isNotEmpty(protocols) - ? protocols.stream().filter(PROTOCOLS::contains).collect(Collectors.toList()) : videoProtocols; + ? protocols.stream().filter(PROTOCOLS::contains).collect(Collectors.toList()) + : videoProtocols; } @Override public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { try { - final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.withValues(extractBids(httpCall.getRequest().getPayload(), bidResponse)); - } catch (DecodeException | PreBidException e) { + return Result.withValues(extractBids(httpCall)); + } catch (PreBidException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private static List extractBids(BidRequest bidRequest, BidResponse bidResponse) { - return bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid()) - ? Collections.emptyList() - : bidsFromResponse(bidRequest, bidResponse); + private List extractBids(HttpCall httpCall) { + final BidResponse bidResponse; + try { + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + } catch (DecodeException e) { + throw new PreBidException(e.getMessage()); + } + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + throw new PreBidException("Empty bid request"); + } + return bidsFromResponse(httpCall.getRequest().getPayload(), bidResponse); } - private static List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { - return bidResponse.getSeatbid().stream() - .filter(Objects::nonNull) - .map(SeatBid::getBid) + private List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { + final SeatBid firstSeatBid = bidResponse.getSeatbid().get(0); + final List bids = firstSeatBid.getBid(); + + if (CollectionUtils.isEmpty(bids)) { + throw new PreBidException("Empty bids array"); + } + return bids.stream() .filter(Objects::nonNull) - .flatMap(Collection::stream) - .map(bid -> BidderBid.of(bid, getType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) + .map(bid -> BidderBid.of(updateBidWithId(bid), getType(bid.getImpid(), + bidRequest.getImp()), bidResponse.getCur())) .collect(Collectors.toList()); } + private Bid updateBidWithId(Bid bid) { + return generateBidId + ? bid.toBuilder().id(UUID.randomUUID().toString()).build() + : bid; + } + private static BidType getType(String impId, List imps) { for (Imp imp : imps) { - if (imp.getId().equals(impId) && imp.getVideo() != null) { - return BidType.video; + if (imp.getId().equals(impId)) { + return imp.getVideo() != null ? BidType.video : BidType.banner; } } return BidType.banner; diff --git a/src/main/java/org/prebid/server/bidder/deepintent/DeepintentBidder.java b/src/main/java/org/prebid/server/bidder/deepintent/DeepintentBidder.java index 321a220c25b..493db8e78ee 100644 --- a/src/main/java/org/prebid/server/bidder/deepintent/DeepintentBidder.java +++ b/src/main/java/org/prebid/server/bidder/deepintent/DeepintentBidder.java @@ -1,7 +1,9 @@ package org.prebid.server.bidder.deepintent; import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Format; import com.iab.openrtb.request.Imp; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; @@ -55,8 +57,9 @@ public Result>> makeHttpRequests(BidRequest request for (Imp imp : request.getImp()) { try { + final Banner updatedBanner = buildImpBanner(imp.getBanner(), imp.getId()); final ExtImpDeepintent extImpDeepintent = parseImpExt(imp); - modifiedImps.add(modifyImp(imp, extImpDeepintent.getTagId())); + modifiedImps.add(modifyImp(imp, extImpDeepintent.getTagId(), updatedBanner)); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); } @@ -69,16 +72,35 @@ public Result>> makeHttpRequests(BidRequest request return Result.of(requests, errors); } + private Banner buildImpBanner(Banner banner, String impId) { + if (banner == null) { + throw new PreBidException(String.format("We need a Banner Object in " + + "the request, imp : %s", impId)); + } + + if (banner.getW() == null && banner.getH() == null) { + final List bannerFormats = banner.getFormat(); + if (CollectionUtils.isEmpty(banner.getFormat())) { + throw new PreBidException(String.format("At least one size is required, imp : %s", impId)); + } + final Format format = bannerFormats.get(0); + return banner.toBuilder().w(format.getW()).h(format.getH()).build(); + } + + return banner; + } + private ExtImpDeepintent parseImpExt(Imp imp) { try { return mapper.mapper().convertValue(imp.getExt(), DEEPINTENT_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage()); + throw new PreBidException(String.format("Impression id=%s, has invalid Ext", imp.getId())); } } - private Imp modifyImp(Imp imp, String tagId) { + private Imp modifyImp(Imp imp, String tagId, Banner banner) { return imp.toBuilder() + .banner(banner) .tagid(tagId) .displaymanager(DISPLAY_MANAGER) .displaymanagerver(DISPLAY_MANAGER_VER) @@ -102,20 +124,23 @@ private HttpRequest createRequest(BidRequest request, Imp imp) { @Override public final Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { try { - final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.of(extractBids(httpCall.getRequest().getPayload(), bidResponse), Collections.emptyList()); - } catch (DecodeException e) { - return Result.withError(BidderError.badServerResponse(e.getMessage())); + return Result.of(extractBids(httpCall), Collections.emptyList()); } catch (PreBidException e) { - return Result.withError(BidderError.badInput(e.getMessage())); + return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { + private List extractBids(HttpCall httpCall) { + final BidResponse bidResponse; + try { + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + } catch (DecodeException e) { + throw new PreBidException(e.getMessage()); + } if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { return Collections.emptyList(); } - return bidsFromResponse(bidRequest, bidResponse); + return bidsFromResponse(httpCall.getRequest().getPayload(), bidResponse); } private List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { @@ -131,9 +156,6 @@ private List bidsFromResponse(BidRequest bidRequest, BidResponse bidR private BidType getBidType(String impId, List imps) { for (Imp imp : imps) { if (imp.getId().equals(impId)) { - if (imp.getVideo() != null && imp.getBanner() == null) { - return BidType.video; - } return BidType.banner; } } diff --git a/src/main/java/org/prebid/server/bidder/dmx/DmxBidder.java b/src/main/java/org/prebid/server/bidder/dmx/DmxBidder.java index dad5b15c175..92d7c8b7a44 100644 --- a/src/main/java/org/prebid/server/bidder/dmx/DmxBidder.java +++ b/src/main/java/org/prebid/server/bidder/dmx/DmxBidder.java @@ -26,7 +26,6 @@ import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust; import org.prebid.server.proto.openrtb.ext.request.dmx.ExtImpDmx; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.HttpUtil; @@ -156,20 +155,16 @@ private static Imp fetchParams(Imp imp, ExtImpDmx extImp) { final String tagId = extImp.getTagId(); if (StringUtils.isNotBlank(tagId)) { - updatedImp = Imp.builder() - .id(imp.getId()) + updatedImp = imp.toBuilder() .tagid(tagId) - .ext(imp.getExt()) .secure(SECURE) .build(); } final String dmxId = extImp.getDmxId(); if (StringUtils.isNotBlank(dmxId)) { - updatedImp = Imp.builder() - .id(imp.getId()) + updatedImp = imp.toBuilder() .tagid(dmxId) - .ext(imp.getExt()) .secure(SECURE) .build(); } @@ -202,9 +197,7 @@ private static void checkIfHasId(App app, User user) { } final ExtUser userExt = user.getExt(); if (userExt != null) { - final ExtUserDigiTrust digitrust = userExt.getDigitrust(); - if (CollectionUtils.isNotEmpty(userExt.getEids()) || (digitrust != null - && StringUtils.isNotBlank(digitrust.getId()))) { + if (CollectionUtils.isNotEmpty(userExt.getEids())) { anyHasId = true; } } diff --git a/src/main/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidder.java b/src/main/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidder.java index a569edbd0cb..f9f2bcc8a9a 100644 --- a/src/main/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidder.java +++ b/src/main/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidder.java @@ -1,12 +1,14 @@ package org.prebid.server.bidder.emxdigital; import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.App; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Format; import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.Video; import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; @@ -41,7 +43,8 @@ public class EmxDigitalBidder implements Bidder { - private static final String DEFAULT_BID_CURRENCY = "USD"; + private static final String USD_CURRENCY = "USD"; + private static final Integer PROTOCOL_VAST_40 = 7; private static final TypeReference> EMXDIGITAL_EXT_TYPE_REFERENCE = new TypeReference>() { @@ -79,7 +82,7 @@ public Result>> makeHttpRequests(BidRequest request // Handle request errors and formatting to be sent to EMX private BidRequest makeBidRequest(BidRequest request) { - final boolean isSecure = isSecure(request.getSite()); + final boolean isSecure = resolveUrl(request).startsWith("https"); final List modifiedImps = request.getImp().stream() .map(imp -> modifyImp(imp, isSecure, unpackImpExt(imp))) @@ -90,10 +93,21 @@ private BidRequest makeBidRequest(BidRequest request) { .build(); } - private static boolean isSecure(Site site) { - return site != null - && StringUtils.isNotBlank(site.getPage()) - && site.getPage().startsWith("https"); + private static String resolveUrl(BidRequest request) { + final Site site = request.getSite(); + final String page = site != null ? site.getPage() : null; + if (StringUtils.isNotBlank(page)) { + return page; + } + final App app = request.getApp(); + if (app != null) { + if (StringUtils.isNotBlank(app.getDomain())) { + return app.getDomain(); + } else if (StringUtils.isNotBlank(app.getStoreurl())) { + return app.getStoreurl(); + } + } + return ""; } private ExtImpEmxDigital unpackImpExt(Imp imp) { @@ -122,13 +136,16 @@ private ExtImpEmxDigital unpackImpExt(Imp imp) { } private static Imp modifyImp(Imp imp, boolean isSecure, ExtImpEmxDigital extImpEmxDigital) { - final Banner banner = modifyImpBanner(imp.getBanner()); final Imp.ImpBuilder impBuilder = imp.toBuilder() .tagid(extImpEmxDigital.getTagid()) - .secure(BooleanUtils.toInteger(isSecure)) - .banner(banner) - .ext(null); + .secure(BooleanUtils.toInteger(isSecure)); + final Video video = imp.getVideo(); + if (video != null) { + impBuilder.video(modifyImpVideo(video)); + } else { + impBuilder.banner(modifyImpBanner(imp.getBanner())); + } final String stringBidfloor = extImpEmxDigital.getBidfloor(); if (StringUtils.isBlank(stringBidfloor)) { @@ -144,10 +161,36 @@ private static Imp modifyImp(Imp imp, boolean isSecure, ExtImpEmxDigital extImpE return impBuilder .bidfloor(bidfloor) - .bidfloorcur(DEFAULT_BID_CURRENCY) + .bidfloorcur(USD_CURRENCY) .build(); } + private static Video modifyImpVideo(Video video) { + if (CollectionUtils.isEmpty(video.getMimes())) { + throw new PreBidException("Video: missing required field mimes"); + } + if (isNotPresentSize(video.getH()) && isNotPresentSize(video.getW())) { + throw new PreBidException("Video: Need at least one size to build request"); + } + if (CollectionUtils.isNotEmpty(video.getProtocols())) { + final List updatedProtocols = removeVast40Protocols(video.getProtocols()); + return video.toBuilder().protocols(updatedProtocols).build(); + } + + return video; + } + + private static boolean isNotPresentSize(Integer size) { + return Objects.isNull(size) || size == 0; + } + + // not supporting VAST protocol 7 (VAST 4.0); + private static List removeVast40Protocols(List protocols) { + return protocols.stream() + .filter(protocol -> !protocol.equals(PROTOCOL_VAST_40)) + .collect(Collectors.toList()); + } + private static Banner modifyImpBanner(Banner banner) { if (banner == null) { throw new PreBidException("Request needs to include a Banner object"); @@ -186,8 +229,9 @@ private static MultiMap makeHeaders(BidRequest request) { } final Site site = request.getSite(); - if (site != null && StringUtils.isNotBlank(site.getPage())) { - HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, site.getPage()); + final String page = site != null ? site.getPage() : null; + if (StringUtils.isNotBlank(page)) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, page); } return headers; @@ -241,10 +285,15 @@ private static List bidsFromResponse(BidResponse bidResponse) { .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> BidderBid.of(modifyBid(bid), BidType.banner, bidResponse.getCur())) + .map(bid -> BidderBid.of(modifyBid(bid), getBidType(bid.getAdm()), bidResponse.getCur())) .collect(Collectors.toList()); } + private static BidType getBidType(String bidAdm) { + return StringUtils.containsAny(bidAdm, " { private static final TypeReference> GUMGUM_EXT_TYPE_REFERENCE = @@ -53,27 +58,20 @@ public GumgumBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { final List errors = new ArrayList<>(); + final BidRequest outgoingRequest; try { outgoingRequest = createBidRequest(bidRequest, errors); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); - return Result.of(Collections.emptyList(), errors); - } - - String body; - try { - body = mapper.encode(outgoingRequest); - } catch (EncodeException e) { - errors.add(BidderError.badInput(String.format("Failed to encode request body, error: %s", e.getMessage()))); - return Result.of(Collections.emptyList(), errors); + return Result.withErrors(errors); } return Result.of(Collections.singletonList( HttpRequest.builder() .method(HttpMethod.POST) .uri(endpointUrl) - .body(body) + .body(mapper.encode(outgoingRequest)) .headers(HttpUtil.headers()) .payload(outgoingRequest) .build()), @@ -118,23 +116,29 @@ private ExtImpGumgum parseImpExt(Imp imp) { try { return mapper.mapper().convertValue(imp.getExt(), GUMGUM_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); + throw new PreBidException(e.getMessage()); } } private static Imp modifyImp(Imp imp) { - final Banner banner = imp.getBanner(); - final List format = banner.getFormat(); - if (banner.getH() == null && banner.getW() == null && CollectionUtils.isNotEmpty(format)) { - final Format firstFormat = format.get(0); - final Banner modifiedBanner = banner.toBuilder().w(firstFormat.getW()).h(firstFormat.getH()).build(); + final Banner resolvedBanner = resolveBanner(imp.getBanner()); + if (resolvedBanner != null) { return imp.toBuilder() - .banner(modifiedBanner) + .banner(resolvedBanner) .build(); } return imp; } + private static Banner resolveBanner(Banner banner) { + final List format = banner.getFormat(); + if (banner.getH() == null && banner.getW() == null && CollectionUtils.isNotEmpty(format)) { + final Format firstFormat = format.get(0); + return banner.toBuilder().w(firstFormat.getW()).h(firstFormat.getH()).build(); + } + return null; + } + private void validateVideoParams(Video video) { if (anyOfNull( video.getW(), @@ -156,7 +160,7 @@ private static boolean isNullOrZero(Integer number) { } private static Site modifySite(Site site, String trackingId) { - return site != null ? site.toBuilder().id(trackingId).build() : null; + return site != null ? site.toBuilder().id(ObjectUtils.defaultIfNull(trackingId, "")).build() : null; } @Override @@ -177,8 +181,10 @@ private static List extractBids(BidResponse bidResponse, BidRequest b private static List bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) { return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) .map(SeatBid::getBid) .flatMap(Collection::stream) + .filter(Objects::nonNull) .map(bid -> toBidderBid(bid, bidRequest, bidResponse.getCur())) .collect(Collectors.toList()); } @@ -186,17 +192,21 @@ private static List bidsFromResponse(BidResponse bidResponse, BidRequ private static BidderBid toBidderBid(Bid bid, BidRequest bidRequest, String currency) { final BidType bidType = getMediaType(bid.getImpid(), bidRequest.getImp()); final Bid updatedBid = bidType == BidType.video - ? bid.toBuilder().adm(bid.getAdm().replace("${AUCTION_PRICE}", String.valueOf(bid.getPrice()))).build() + ? bid.toBuilder().adm(resolveAdm(bid.getAdm(), bid.getPrice())).build() : bid; return BidderBid.of(updatedBid, bidType, currency); } private static BidType getMediaType(String impId, List requestImps) { for (Imp imp : requestImps) { - if (imp.getId().equals(impId) && imp.getBanner() != null) { - return BidType.banner; + if (imp.getId().equals(impId)) { + return imp.getBanner() != null ? BidType.banner : BidType.video; } } return BidType.video; } + + private static String resolveAdm(String bidAdm, BigDecimal price) { + return StringUtils.isNotBlank(bidAdm) ? bidAdm.replace("${AUCTION_PRICE}", String.valueOf(price)) : bidAdm; + } } diff --git a/src/main/java/org/prebid/server/bidder/ix/IxAdapter.java b/src/main/java/org/prebid/server/bidder/ix/IxAdapter.java index fe7b84265fb..b8972f8aa15 100644 --- a/src/main/java/org/prebid/server/bidder/ix/IxAdapter.java +++ b/src/main/java/org/prebid/server/bidder/ix/IxAdapter.java @@ -55,8 +55,6 @@ public IxAdapter(String cookieFamilyName, String endpointUrl, JacksonMapper mapp @Override public List> makeHttpRequests(AdapterRequest adapterRequest, PreBidRequestContext preBidRequestContext) { - validatePreBidRequest(preBidRequestContext.getPreBidRequest()); - final List adUnitBids = adapterRequest.getAdUnitBids(); validateAdUnitBidsMediaTypes(adUnitBids, ALLOWED_MEDIA_TYPES); @@ -70,12 +68,6 @@ public List> makeHttpRequests(AdapterRequest adap .collect(Collectors.toList()); } - private static void validatePreBidRequest(PreBidRequest preBidRequest) { - if (preBidRequest.getApp() != null) { - throw new PreBidException("ix doesn't support apps"); - } - } - private List makeRequests(List adUnitBids, PreBidRequestContext preBidRequestContext) { final List prioritizedRequests = new ArrayList<>(); final List regularRequests = new ArrayList<>(); @@ -127,7 +119,9 @@ private IxParams parseAndValidateParams(AdUnitBid adUnitBid) { return params; } - private BidRequest createBidRequest(AdUnitBid adUnitBid, IxParams ixParams, Format size, + private BidRequest createBidRequest(AdUnitBid adUnitBid, + IxParams ixParams, + Format size, PreBidRequestContext preBidRequestContext) { final Imp imp = makeImp(copyAdUnitBidWithSingleSize(adUnitBid, size), preBidRequestContext); diff --git a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java index f06d50d5f7c..aa222cc82d9 100644 --- a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java +++ b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java @@ -12,7 +12,6 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -57,28 +56,34 @@ public IxBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest bidRequest) { - if (bidRequest.getApp() != null) { - return Result.withError(BidderError.badInput("ix doesn't support apps")); - } final List errors = new ArrayList<>(); + + // First Banner.Format in every Imp have priority final List prioritizedRequests = new ArrayList<>(); - final List regularRequests = new ArrayList<>(); + final List multiSizeRequests = new ArrayList<>(); + for (Imp imp : bidRequest.getImp()) { - if (prioritizedRequests.size() == REQUEST_LIMIT) { - break; - } try { - validateImp(imp); - makeRequests(bidRequest, imp, prioritizedRequests, regularRequests); + final ExtImpIx extImpIx = parseImpExt(imp); + final List bidRequests = makeBidRequests(bidRequest, imp, extImpIx); + + prioritizedRequests.add(bidRequests.get(0)); + multiSizeRequests.addAll(bidRequests.subList(1, bidRequests.size())); + + if (prioritizedRequests.size() == REQUEST_LIMIT) { + break; + } } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); } } - final List modifiedRequests = Stream.concat(prioritizedRequests.stream(), regularRequests.stream()) + final List modifiedRequests = Stream.concat( + prioritizedRequests.stream(), multiSizeRequests.stream()) .limit(REQUEST_LIMIT) .collect(Collectors.toList()); + if (modifiedRequests.isEmpty()) { errors.add(BidderError.badInput("No valid impressions in the bid request")); return Result.withErrors(errors); @@ -97,84 +102,85 @@ public Result>> makeHttpRequests(BidRequest bidRequ return Result.of(httpRequests, errors); } - private static void validateImp(Imp imp) { - if (imp.getBanner() == null) { - throw new PreBidException(String.format("Invalid MediaType. Ix supports only Banner type. " - + "Ignoring ImpID=%s", imp.getId())); + private ExtImpIx parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), IX_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage(), e); } } - private void makeRequests(BidRequest bidRequest, Imp imp, List prioritizedRequests, - List regularRequests) { - List formats = imp.getBanner().getFormat(); - if (CollectionUtils.isEmpty(formats)) { - formats = makeFormatFromBannerWidthAndHeight(imp); - } - final ExtImpIx extImpIx = parseAndValidateImpExt(imp); - final List modifiedBidRequests = createBidRequest(bidRequest, imp, formats, extImpIx); - if (!modifiedBidRequests.isEmpty()) { - if (modifiedBidRequests.size() == 1) { - prioritizedRequests.addAll(modifiedBidRequests); - } else { - prioritizedRequests.add(modifiedBidRequests.get(0)); - regularRequests.addAll(modifiedBidRequests.subList(1, modifiedBidRequests.size())); - } + private static List makeBidRequests(BidRequest bidRequest, + Imp imp, + ExtImpIx extImpIx) { + final ArrayList modifiedBidRequests = new ArrayList<>(); + final Site modifiedSite = modifySite(bidRequest.getSite(), extImpIx); + + final List modifiedImps = modifyImps(imp); + for (Imp modifiedImp : modifiedImps) { + final BidRequest modifiedBidRequest = bidRequest.toBuilder() + .site(modifiedSite) + .imp(Collections.singletonList(modifiedImp)) + .build(); + modifiedBidRequests.add(modifiedBidRequest); } + + return modifiedBidRequests; } - private static List makeFormatFromBannerWidthAndHeight(Imp imp) { - final Banner banner = imp.getBanner(); - return Collections.singletonList( - Format.builder().w(banner.getW()).h(banner.getH()).build()); + private static Site modifySite(Site site, ExtImpIx extImpIx) { + return site == null + ? null + : site.toBuilder() + .publisher(modifyPublisher(site.getPublisher(), extImpIx.getSiteId())) + .build(); } - private ExtImpIx parseAndValidateImpExt(Imp imp) { - final ExtImpIx extImpIx; - try { - extImpIx = mapper.mapper().convertValue(imp.getExt(), - IX_EXT_TYPE_REFERENCE).getBidder(); - } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); - } + private static Publisher modifyPublisher(Publisher publisher, String siteId) { + return publisher == null + ? Publisher.builder().id(siteId).build() + : publisher.toBuilder().id(siteId).build(); + } - if (StringUtils.isBlank(extImpIx.getSiteId())) { - throw new PreBidException("Missing siteId param"); + private static List modifyImps(Imp imp) { + final Banner impBanner = imp.getBanner(); + if (impBanner == null) { + return Collections.singletonList(imp); } - return extImpIx; - } - private static List createBidRequest(BidRequest bidRequest, Imp imp, - List formats, ExtImpIx extImpIx) { - final List limitedFormats = formats.size() > REQUEST_LIMIT - ? formats.subList(0, REQUEST_LIMIT) - : formats; + return modifyBanners(impBanner).stream() + .map(banner -> imp.toBuilder().banner(banner).build()) + .collect(Collectors.toList()); + } - final List requests = new ArrayList<>(); - for (Format format : limitedFormats) { - final Banner updatedBanner = imp.getBanner().toBuilder() + private static List modifyBanners(Banner banner) { + final ArrayList modifiedBanners = new ArrayList<>(); + final List formats = getFormats(banner); + for (Format format : formats) { + final Banner modifiedBanner = banner.toBuilder() .format(Collections.singletonList(format)) .w(format.getW()) .h(format.getH()) .build(); - - final Imp updatedImp = imp.toBuilder() - .banner(updatedBanner) - .tagid(imp.getId()) - .build(); - - requests.add(bidRequest.toBuilder() - .site(modifySite(bidRequest, extImpIx)) - .imp(Collections.singletonList(updatedImp)) - .build()); + modifiedBanners.add(modifiedBanner); } - return requests; + + return modifiedBanners; } - private static Site modifySite(BidRequest bidRequest, ExtImpIx extImpIx) { - final Site site = bidRequest.getSite(); - final Site.SiteBuilder siteBuilder = site == null ? Site.builder() : site.toBuilder(); - return siteBuilder.publisher(Publisher.builder() - .id(extImpIx.getSiteId()).build()).build(); + // Cant be empty because of request validation + private static List getFormats(Banner banner) { + final Integer bannerW = banner.getW(); + final Integer bannerH = banner.getH(); + final List bannerFormats = banner.getFormat(); + if (CollectionUtils.isEmpty(bannerFormats) && bannerW != null && bannerH != null) { + final Format format = Format.builder() + .w(bannerW) + .h(bannerH) + .build(); + return Collections.singletonList(format); + } + return bannerFormats; } @Override @@ -199,14 +205,31 @@ private static List bidsFromResponse(BidResponse bidResponse, BidRequ .map(SeatBid::getBid) .flatMap(Collection::stream) .map(bid -> prepareBid(bid, bidRequest)) - .map(bid -> BidderBid.of(bid, BidType.banner, bidResponse.getCur())) + .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) .collect(Collectors.toList()); } + private static BidType getBidType(String impId, List imps) { + for (Imp imp : imps) { + if (imp.getId().equals(impId)) { + if (imp.getBanner() != null) { + return BidType.banner; + } else if (imp.getVideo() != null) { + return BidType.video; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } else if (imp.getAudio() != null) { + return BidType.audio; + } + } + } + throw new PreBidException(String.format("Unmatched impression id %s", impId)); + } + private static Bid prepareBid(Bid bid, BidRequest bidRequest) { - if (bid.getH() == null || bid.getW() == null) { - // Current implementation ensure that we have one imp with banner - final Banner banner = bidRequest.getImp().get(0).getBanner(); + // Current implementation ensure that we have one imp + final Banner banner = bidRequest.getImp().get(0).getBanner(); + if (bid.getH() == null || bid.getW() == null && banner != null) { return bid.toBuilder() .w(banner.getW()) .h(banner.getH()) diff --git a/src/main/java/org/prebid/server/bidder/nobid/NobidBidder.java b/src/main/java/org/prebid/server/bidder/nobid/NobidBidder.java new file mode 100644 index 00000000000..e373d77c5e1 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/nobid/NobidBidder.java @@ -0,0 +1,106 @@ +package org.prebid.server.bidder.nobid; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Nobid {@link Bidder} implementation. + */ +public class NobidBidder implements Bidder { + + private final String endpointUrl; + private final JacksonMapper mapper; + + public NobidBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + + return Result.of(Collections.singletonList( + HttpRequest.builder() + .method(HttpMethod.POST) + .uri(endpointUrl) + .headers(HttpUtil.headers()) + .payload(request) + .body(mapper.encode(request)) + .build()), + Collections.emptyList()); + } + + @Override + public final Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + final List errors = new ArrayList<>(); + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.of(extractBids(httpCall.getRequest().getPayload(), bidResponse, errors), errors); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidRequest bidRequest, BidResponse bidResponse, List errors) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + return bidsFromResponse(bidRequest, bidResponse, errors); + } + + private List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse, List errors) { + + return bidResponse.getSeatbid().stream() + .map(SeatBid::getBid) + .flatMap(Collection::stream) + .map(bid -> mapToBidderBid(bid, bidRequest.getImp(), bidResponse.getCur(), errors)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static BidderBid mapToBidderBid(Bid bid, List imps, String currency, List errors) { + final BidType bidType; + try { + bidType = getBidType(bid.getImpid(), imps); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + return null; + } + return BidderBid.of(bid, bidType, currency); + } + + private static BidType getBidType(String impId, List imps) { + for (Imp imp : imps) { + if (imp.getId().equals(impId)) { + if (imp.getBanner() == null && imp.getVideo() != null) { + return BidType.video; + } + return BidType.banner; + } + } + throw new PreBidException(String.format("Failed to find impression %s", impId)); + } +} diff --git a/src/main/java/org/prebid/server/bidder/pubnative/PubnativeBidder.java b/src/main/java/org/prebid/server/bidder/pubnative/PubnativeBidder.java index 8ed8c94d3b3..830b7892428 100644 --- a/src/main/java/org/prebid/server/bidder/pubnative/PubnativeBidder.java +++ b/src/main/java/org/prebid/server/bidder/pubnative/PubnativeBidder.java @@ -7,6 +7,7 @@ import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Format; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; @@ -145,21 +146,68 @@ private static List bidsFromResponse(List seatbid, List .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> BidderBid.of(bid, resolveBidType(bid.getImpid(), imps), currency)) + .map(bid -> createBidderBid(imps, currency, bid)) .collect(Collectors.toList()); } - private static BidType resolveBidType(String impid, List imps) { - for (Imp imp : imps) { - if (imp.getId().equals(impid)) { - if (imp.getVideo() != null) { - return BidType.video; - } - if (imp.getXNative() != null) { - return BidType.xNative; - } - } + private static BidderBid createBidderBid(List imps, String currency, Bid bid) { + final Imp imp = findImpById(bid.getImpid(), imps); + return BidderBid.of(updateBidWithSize(bid, imp), resolveBidType(imp), currency); + } + + private static Bid updateBidWithSize(Bid bid, Imp imp) { + if (bid.getW() != null && bid.getH() != null) { + return bid; + } + + final Format format = imp != null && imp.getBanner() != null + ? resolveBidSizeFromBanner(imp.getBanner()) + : null; + return format != null + ? bid.toBuilder().w(format.getW()).h(format.getH()).build() + : bid; + } + + private static Imp findImpById(String impId, List imps) { + return imps.stream() + .filter(imp -> imp.getId().equals(impId)) + .findFirst() + .orElse(null); + } + + private static BidType resolveBidType(Imp imp) { + if (imp == null) { + return BidType.banner; + } else if (imp.getVideo() != null) { + return BidType.video; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } else { + return BidType.banner; + } + } + + private static Format resolveBidSizeFromBanner(Banner banner) { + Format result = null; + final Integer width = banner.getW(); + final Integer height = banner.getH(); + + final List formats = banner.getFormat(); + if (width != null && height != null) { + result = isOnlyOneSize(width, height, formats) + ? Format.builder().w(width).h(height).build() + : null; + } else if (formats.size() == 1) { + result = formats.get(0); } - return BidType.banner; + return result; + } + + private static boolean isOnlyOneSize(Integer width, Integer height, List formats) { + return CollectionUtils.isEmpty(formats) || (formats.size() == 1 && isSameFormat(width, height, formats.get(0))); + } + + private static boolean isSameFormat(Integer width, Integer height, Format format) { + return width.equals(format.getW()) && height.equals(format.getH()); } } diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconAdapter.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconAdapter.java index fc33a44f472..fb35f696966 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconAdapter.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconAdapter.java @@ -55,7 +55,6 @@ import org.prebid.server.proto.openrtb.ext.request.ExtPublisher; import org.prebid.server.proto.openrtb.ext.request.ExtSite; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust; import org.prebid.server.proto.openrtb.ext.request.rubicon.RubiconVideoParams; import org.prebid.server.proto.request.PreBidRequest; import org.prebid.server.proto.request.Sdk; @@ -344,19 +343,17 @@ private User makeUser(RubiconParams rubiconParams, PreBidRequestContext preBidRe } private ExtUser makeUserExt(RubiconParams rubiconParams, ExtUser extUser) { - final ExtUserDigiTrust digiTrust = extUser != null ? extUser.getDigitrust() : null; // will be removed final JsonNode visitorNode = rubiconParams.getVisitor(); final JsonNode visitor = visitorNode != null && !visitorNode.isNull() && visitorNode.size() != 0 ? visitorNode : null; - final boolean makeRp = visitor != null; - if (digiTrust != null || visitor != null) { + if (visitor != null) { final ExtUser userExt = extUser != null ? ExtUser.builder().consent(extUser.getConsent()).eids(extUser.getEids()).build() : ExtUser.builder().build(); final RubiconUserExt rubiconUserExt = RubiconUserExt.builder() - .rp(makeRp ? RubiconUserExtRp.of(visitor) : null) + .rp(RubiconUserExtRp.of(visitor)) .build(); return mapper.fillExtension(userExt, rubiconUserExt); } 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 85eca8aa4bf..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; @@ -65,6 +62,7 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.log.ConditionalLogger; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtApp; import org.prebid.server.proto.openrtb.ext.request.ExtDevice; @@ -80,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; @@ -111,10 +110,11 @@ public class RubiconBidder implements Bidder { private static final Logger logger = LoggerFactory.getLogger(RubiconBidder.class); + private static final ConditionalLogger MISSING_VIDEO_SIZE_LOGGER = + new ConditionalLogger("missing_video_size", logger); 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"; @@ -170,8 +170,14 @@ public Result>> makeHttpRequests(BidRequest bidRequ final List> httpRequests = new ArrayList<>(); final List errors = new ArrayList<>(); + final List imps = extractValidImps(bidRequest, errors); + if (CollectionUtils.isEmpty(imps)) { + errors.add(BidderError.of("There are no valid impressions to create bid request to rubicon bidder", + BidderError.Type.bad_input)); + return Result.of(Collections.emptyList(), errors); + } final Map> impToImpExt = - parseRubiconImpExts(bidRequest.getImp(), errors); + parseRubiconImpExts(imps, errors); final String impLanguage = firstImpExtLanguage(impToImpExt.values()); for (Map.Entry> impToExt : impToImpExt.entrySet()) { @@ -226,6 +232,44 @@ public Map extractTargeting(ObjectNode extBidBidder) { : Collections.emptyMap(); } + private List extractValidImps(BidRequest bidRequest, List errors) { + final Map> isValidToImps = bidRequest.getImp().stream() + .collect(Collectors.groupingBy(RubiconBidder::isValidType)); + + isValidToImps.getOrDefault(false, Collections.emptyList()).stream() + .map(this::impTypeErrorMessage) + .forEach(errors::add); + + return isValidToImps.getOrDefault(true, Collections.emptyList()); + } + + private static boolean isValidType(Imp imp) { + return imp.getVideo() != null || imp.getBanner() != null; + } + + private BidderError impTypeErrorMessage(Imp imp) { + final BidType type = resolveExpectedBidType(imp); + return BidderError.of( + String.format("Impression with id %s rejected with invalid type `%s`." + " Allowed types are banner and" + + " video.", imp.getId(), type != null ? type.name() : "unknown"), BidderError.Type.bad_input); + } + + private static BidType resolveExpectedBidType(Imp imp) { + if (imp.getBanner() != null) { + return BidType.banner; + } + if (imp.getVideo() != null) { + return BidType.video; + } + if (imp.getAudio() != null) { + return BidType.audio; + } + if (imp.getXNative() != null) { + return BidType.xNative; + } + return null; + } + private static MultiMap headers(String xapiUsername, String xapiPassword) { return MultiMap.caseInsensitiveMultiMap() .add(HttpUtil.AUTHORIZATION_HEADER, authHeader(xapiUsername, xapiPassword)) @@ -339,7 +383,7 @@ private Imp makeImp(Imp imp, ExtImpPrebid extPrebid, ExtImpRubicon extRubicon, S if (isVideo(imp)) { builder .banner(null) - .video(makeVideo(imp.getVideo(), extRubicon.getVideo(), extPrebid)); + .video(makeVideo(imp, extRubicon.getVideo(), extPrebid, referer(site))); } else { builder .banner(makeBanner(imp, overriddenSizes(extRubicon))) @@ -609,23 +653,61 @@ private static boolean isFullyPopulatedVideo(Video video) { && video.getLinearity() != null && video.getApi() != null; } - private Video makeVideo(Video video, RubiconVideoParams rubiconVideoParams, ExtImpPrebid prebidImpExt) { + private static String referer(Site site) { + return site != null ? site.getPage() : null; + } + + private Video makeVideo(Imp imp, RubiconVideoParams rubiconVideoParams, ExtImpPrebid prebidImpExt, String referer) { + final Video video = imp.getVideo(); + + final Integer skip = rubiconVideoParams != null ? rubiconVideoParams.getSkip() : null; + final Integer skipDelay = rubiconVideoParams != null ? rubiconVideoParams.getSkipdelay() : null; + final Integer sizeId = rubiconVideoParams != null ? rubiconVideoParams.getSizeId() : null; + + final Integer resolvedSizeId = sizeId == null || sizeId == 0 + ? resolveVideoSizeId(video.getPlacement(), imp.getInstl()) + : sizeId; + validateVideoSizeId(resolvedSizeId, referer, imp.getId()); + final String videoType = prebidImpExt != null && prebidImpExt.getIsRewardedInventory() != null && prebidImpExt.getIsRewardedInventory() == 1 ? "rewarded" : null; - if (rubiconVideoParams == null && videoType == null) { + // optimization for empty ext params + if (skip == null && skipDelay == null && resolvedSizeId == null && videoType == null) { return video; } - final Integer skip = rubiconVideoParams != null ? rubiconVideoParams.getSkip() : null; - final Integer skipDelay = rubiconVideoParams != null ? rubiconVideoParams.getSkipdelay() : null; - final Integer sizeId = rubiconVideoParams != null ? rubiconVideoParams.getSizeId() : null; return video.toBuilder() .ext(mapper.mapper().valueToTree( - RubiconVideoExt.of(skip, skipDelay, RubiconVideoExtRp.of(sizeId), videoType))) + RubiconVideoExt.of(skip, skipDelay, RubiconVideoExtRp.of(resolvedSizeId), videoType))) .build(); } + private static void validateVideoSizeId(Integer resolvedSizeId, String referer, String impId) { + // log only 1% of cases to monitor how often video impressions does not have size id + if (resolvedSizeId == null) { + MISSING_VIDEO_SIZE_LOGGER.warn(String.format("RP adapter: video request with no size_id. Referrer URL = %s," + + " impId = %s", referer, impId), 0.01d); + } + } + + private static Integer resolveVideoSizeId(Integer placement, Integer instl) { + if (placement != null) { + if (placement == 1) { + return 201; + } + if (placement == 3) { + return 203; + } + } + + if (instl != null && instl == 1) { + return 202; + } + + return null; + } + private static List overriddenSizes(ExtImpRubicon rubiconImpExt) { final List overriddenSizes; @@ -716,7 +798,7 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { final ExtUser userExt = extUser != null ? ExtUser.builder() .consent(extUser.getConsent()) - .digitrust(extUser.getDigitrust()) + .eids(extUser.getEids()) .eids(resolvedExtUserEids) .build() : ExtUser.builder().build(); @@ -903,14 +985,11 @@ private void mergeFirstPartyDataFromUser(User user, ObjectNode result) { private static String extractLiverampId(Map> sourceToUserEidExt) { final List liverampEids = MapUtils.emptyIfNull(sourceToUserEidExt).get(LIVERAMP_EID); for (ExtUserEid extUserEid : CollectionUtils.emptyIfNull(liverampEids)) { - final ExtUserEidUid eidUid = extUserEid != null - ? CollectionUtils.emptyIfNull(extUserEid.getUids()).stream().findFirst().orElse(null) - : null; - - final String id = eidUid != null ? eidUid.getId() : null; - if (StringUtils.isNotEmpty(id)) { - return id; - } + return extUserEid.getUids().stream() + .map(ExtUserEidUid::getId) + .filter(StringUtils::isNotEmpty) + .findFirst() + .orElse(null); } return null; } @@ -1048,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; - } + final ExtImpRubiconDebug debug = parseRubiconExt(imp).getBidder().getDebug(); - 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); - } - } - - 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/RubiconSize.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconSize.java index d8b9d674bdd..63a52ee1500 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconSize.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconSize.java @@ -101,6 +101,7 @@ final class RubiconSize { SIZES.put(size(320, 500), 278); SIZES.put(size(320, 400), 282); SIZES.put(size(640, 380), 288); + SIZES.put(size(500, 1000), 548); } private final Integer w; 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/bidder/silvermob/SilvermobBidder.java b/src/main/java/org/prebid/server/bidder/silvermob/SilvermobBidder.java new file mode 100644 index 00000000000..bf7d1021f4e --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/silvermob/SilvermobBidder.java @@ -0,0 +1,167 @@ +package org.prebid.server.bidder.silvermob; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.silvermob.ExtImpSilvermob; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Silvermob {@link Bidder} implementation. + */ +public class SilvermobBidder implements Bidder { + + private static final TypeReference> SILVERMOB_EXT_TYPE_REFERENCE = + new TypeReference>() { + }; + + private static final String URL_HOST_MACRO = "{{Host}}"; + private static final String URL_ZONE_ID_MACRO = "{{ZoneID}}"; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public SilvermobBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + final List errors = new ArrayList<>(); + final List> requests = new ArrayList<>(); + + for (Imp imp : request.getImp()) { + try { + requests.add(createRequestForImp(imp, request)); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + + return Result.of(requests, errors); + } + + private HttpRequest createRequestForImp(Imp imp, BidRequest request) { + final ExtImpSilvermob extImp = parseImpExt(imp); + + final BidRequest outgoingRequest = request.toBuilder() + .imp(Collections.singletonList(imp)) + .build(); + return HttpRequest.builder() + .method(HttpMethod.POST) + .uri(resolveEndpoint(extImp)) + .headers(resolveHeaders(request.getDevice())) + .payload(outgoingRequest) + .body(mapper.encode(outgoingRequest)) + .build(); + } + + private ExtImpSilvermob parseImpExt(Imp imp) { + final ExtImpSilvermob extImp; + try { + extImp = mapper.mapper().convertValue(imp.getExt(), SILVERMOB_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(String.format("error unmarshalling imp.ext.bidder: %s", e.getMessage())); + } + if (StringUtils.isBlank(extImp.getHost())) { + throw new PreBidException("host is a required silvermob ext.imp param"); + } + + if (StringUtils.isBlank(extImp.getZoneId())) { + throw new PreBidException("zoneId is a required silvermob ext.imp param"); + } + return extImp; + } + + private String resolveEndpoint(ExtImpSilvermob extImp) { + return endpointUrl + .replace(URL_HOST_MACRO, extImp.getHost()) + .replace(URL_ZONE_ID_MACRO, HttpUtil.encodeUrl(extImp.getZoneId())); + } + + private static MultiMap resolveHeaders(Device device) { + final MultiMap headers = HttpUtil.headers() + .add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5"); + + if (device != null) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER, device.getUa()); + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, device.getIpv6()); + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, device.getIp()); + } + + return headers; + } + + @Override + public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + try { + return Result.of(extractBids(httpCall), Collections.emptyList()); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(HttpCall httpCall) { + final BidResponse bidResponse; + try { + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + } catch (DecodeException e) { + throw new PreBidException(String.format("Error unmarshalling server Response: %s", e.getMessage())); + } + if (bidResponse == null) { + throw new PreBidException("Response in not present"); + } + if (CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + throw new PreBidException("Empty SeatBid array"); + } + return bidsFromResponse(bidResponse, httpCall.getRequest().getPayload()); + } + + private static List bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) { + return bidResponse.getSeatbid().stream() + .map(SeatBid::getBid) + .flatMap(Collection::stream) + .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) + .collect(Collectors.toList()); + } + + private static BidType getBidType(String impId, List imps) { + for (Imp imp : imps) { + if (imp.getId().equals(impId)) { + if (imp.getVideo() != null) { + return BidType.video; + } + if (imp.getXNative() != null) { + return BidType.xNative; + } + } + } + return BidType.banner; + } +} diff --git a/src/main/java/org/prebid/server/bidder/smaato/SmaatoBidder.java b/src/main/java/org/prebid/server/bidder/smaato/SmaatoBidder.java index aa903fd7d2b..4b4b3569fe1 100644 --- a/src/main/java/org/prebid/server/bidder/smaato/SmaatoBidder.java +++ b/src/main/java/org/prebid/server/bidder/smaato/SmaatoBidder.java @@ -15,6 +15,7 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; @@ -50,6 +51,9 @@ import java.util.function.Function; import java.util.stream.Collectors; +/** + * Smaato {@link Bidder} implementation. + */ public class SmaatoBidder implements Bidder { private static final TypeReference> SMAATO_EXT_TYPE_REFERENCE = @@ -57,6 +61,7 @@ public class SmaatoBidder implements Bidder { }; private static final String CLIENT_VERSION = "prebid_server_0.1"; + private static final String SMT_ADTYPE_HEADER = "X-SMT-ADTYPE"; private static final String SMT_AD_TYPE_IMG = "Img"; private static final String SMT_ADTYPE_RICHMEDIA = "Richmedia"; private static final String SMT_ADTYPE_VIDEO = "Video"; @@ -86,23 +91,19 @@ public Result>> makeHttpRequests(BidRequest request } } - final Site modifiedSite; - final User modifiedUser; + final BidRequest outgoingRequest; try { - modifiedSite = request.getSite() != null ? modifySite(request.getSite(), firstPublisherId) : null; - modifiedUser = request.getUser() != null ? modifyUser(request.getUser()) : null; + outgoingRequest = request.toBuilder() + .imp(imps) + .site(modifySite(request.getSite(), firstPublisherId)) + .user(modifyUser(request.getUser())) + .ext(mapper.fillExtension(ExtRequest.empty(), SmaatoBidRequestExt.of(CLIENT_VERSION))) + .build(); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); return Result.withErrors(errors); } - final BidRequest outgoingRequest = request.toBuilder() - .imp(imps) - .site(modifiedSite) - .user(modifiedUser) - .ext(mapper.fillExtension(ExtRequest.empty(), SmaatoBidRequestExt.of(CLIENT_VERSION))) - .build(); - return Result.of(Collections.singletonList( HttpRequest.builder() .method(HttpMethod.POST) @@ -148,6 +149,10 @@ private static Banner modifyBanner(Banner banner) { } private Site modifySite(Site site, String firstPublisherId) { + if (site == null) { + return null; + } + final Site.SiteBuilder siteBuilder = site.toBuilder() .publisher(Publisher.builder().id(firstPublisherId).build()); @@ -162,37 +167,37 @@ private Site modifySite(Site site, String firstPublisherId) { } private User modifyUser(User user) { - final ExtUser ext = user.getExt(); - if (ext == null) { - return user; + if (user == null) { + return null; } - final ObjectNode extDataNode = ext.getData(); - if (extDataNode == null) { + final ExtUser userExt = user.getExt(); + final ObjectNode extDataNode = userExt != null ? userExt.getData() : null; + if (extDataNode == null || extDataNode.isEmpty()) { return user; } + final SmaatoUserExtData smaatoUserExtData = convertExt(extDataNode, SmaatoUserExtData.class); final User.UserBuilder userBuilder = user.toBuilder(); - final SmaatoUserExtData data = convertExt(extDataNode, SmaatoUserExtData.class); - final String gender = data != null ? data.getGender() : null; + final String gender = smaatoUserExtData.getGender(); if (StringUtils.isNotBlank(gender)) { userBuilder.gender(gender); } - final Integer yob = data != null ? data.getYob() : null; + final Integer yob = smaatoUserExtData.getYob(); if (yob != null && yob != 0) { userBuilder.yob(yob); } - final String keywords = data != null ? data.getKeywords() : null; + final String keywords = smaatoUserExtData.getKeywords(); if (StringUtils.isNotBlank(keywords)) { userBuilder.keywords(keywords); } - userBuilder.ext(ext.toBuilder().data(null).build()); - - return userBuilder.build(); + return userBuilder + .ext(userExt.toBuilder().data(null).build()) + .build(); } private T convertExt(ObjectNode ext, Class className) { @@ -210,6 +215,8 @@ public Result> makeBids(HttpCall httpCall, BidReques return extractBids(bidResponse, httpCall.getResponse().getHeaders()); } catch (DecodeException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); + } catch (PreBidException e) { + return Result.withError(BidderError.badInput(e.getMessage())); } } @@ -218,31 +225,29 @@ private Result> extractBids(BidResponse bidResponse, MultiMap he return Result.empty(); } - final List errors = new ArrayList<>(); - final List bidderBids = bidResponse.getSeatbid().stream() + return Result.withValues(bidResponse.getSeatbid().stream() .filter(Objects::nonNull) .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> bidderBid(bid, bidResponse.getCur(), headers, errors)) + .map(bid -> bidderBid(bid, bidResponse.getCur(), headers)) .filter(Objects::nonNull) - .collect(Collectors.toList()); - return Result.of(bidderBids, errors); + .collect(Collectors.toList())); } - private BidderBid bidderBid(Bid bid, String currency, MultiMap headers, List errors) { - try { - final String markupType = getAdMarkupType(headers, bid.getAdm()); - final Bid updateBid = bid.toBuilder().adm(renderAdMarkup(markupType, bid.getAdm())).build(); - return BidderBid.of(updateBid, getBidType(markupType), currency); - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - return null; + private BidderBid bidderBid(Bid bid, String currency, MultiMap headers) { + final String bidAdm = bid.getAdm(); + if (StringUtils.isBlank(bidAdm)) { + throw new PreBidException(String.format("Empty ad markup in bid with id: %s", bid.getId())); } + + final String markupType = getAdMarkupType(headers, bidAdm); + final Bid updateBid = bid.toBuilder().adm(renderAdMarkup(markupType, bidAdm)).build(); + return BidderBid.of(updateBid, getBidType(markupType), currency); } private static String getAdMarkupType(MultiMap headers, String adm) { - final String adMarkupType = headers.get("X-SMT-ADTYPE"); + final String adMarkupType = headers.get(SMT_ADTYPE_HEADER); if (StringUtils.isNotBlank(adMarkupType)) { return adMarkupType; } @@ -271,30 +276,18 @@ private String renderAdMarkup(String markupType, String adm) { } } - private static BidType getBidType(String markupType) { - switch (markupType) { - case SMT_AD_TYPE_IMG: - case SMT_ADTYPE_RICHMEDIA: - return BidType.banner; - case SMT_ADTYPE_VIDEO: - return BidType.video; - default: - throw new PreBidException(String.format("Invalid markupType %s", markupType)); - } - } - private String extractAdmImage(String adm) { - final SmaatoImageAd imageAd = admToAd(adm, SmaatoImageAd.class); + final SmaatoImageAd imageAd = convertAdmToAd(adm, SmaatoImageAd.class); final SmaatoImage image = imageAd.getImage(); if (image == null) { - return adm; + throw new PreBidException("bid.adm.image is empty"); } final StringBuilder clickEvent = new StringBuilder(); CollectionUtils.emptyIfNull(image.getClickTrackers()) .forEach(tracker -> clickEvent.append(String.format( "fetch(decodeURIComponent('%s'.replace(/\\+/g, ' ')), {cache: 'no-cache'});", - HttpUtil.encodeUrl(tracker)))); + HttpUtil.encodeUrl(StringUtils.stripToEmpty(tracker))))); final StringBuilder impressionTracker = new StringBuilder(); CollectionUtils.emptyIfNull(image.getImpressionTrackers()) @@ -302,35 +295,33 @@ private String extractAdmImage(String adm) { String.format("\"\"", tracker))); final SmaatoImg img = image.getImg(); - return String.format("
%s
", clickEvent.toString(), HttpUtil.encodeUrl(StringUtils.stripToEmpty(getIfNotNull(img, SmaatoImg::getCtaurl))), StringUtils.stripToEmpty(getIfNotNull(img, SmaatoImg::getUrl)), - getIfNotNull(img, SmaatoImg::getW), - getIfNotNull(img, SmaatoImg::getH), + stripToZero(getIfNotNull(img, SmaatoImg::getW)), + stripToZero(getIfNotNull(img, SmaatoImg::getH)), impressionTracker.toString()); } private String extractAdmRichMedia(String adm) { - final SmaatoRichMediaAd richMediaAd = admToAd(adm, SmaatoRichMediaAd.class); + final SmaatoRichMediaAd richMediaAd = convertAdmToAd(adm, SmaatoRichMediaAd.class); final SmaatoRichmedia richmedia = richMediaAd.getRichmedia(); if (richmedia == null) { - return adm; + throw new PreBidException("bid.adm.richmedia is empty"); } final StringBuilder clickEvent = new StringBuilder(); CollectionUtils.emptyIfNull(richmedia.getClickTrackers()) .forEach(tracker -> clickEvent.append( String.format("fetch(decodeURIComponent('%s'), {cache: 'no-cache'});", - HttpUtil.encodeUrl(tracker)))); + HttpUtil.encodeUrl(StringUtils.stripToEmpty(tracker))))); final StringBuilder impressionTracker = new StringBuilder(); CollectionUtils.emptyIfNull(richmedia.getImpressionTrackers()) .forEach(tracker -> impressionTracker.append( - String.format("\"\"", - tracker))); + String.format("\"\"", tracker))); return String.format("
%s%s
", clickEvent.toString(), @@ -338,7 +329,7 @@ private String extractAdmRichMedia(String adm) { impressionTracker.toString()); } - private T admToAd(String value, Class className) { + private T convertAdmToAd(String value, Class className) { try { return mapper.decodeValue(value, className); } catch (DecodeException e) { @@ -346,7 +337,23 @@ private T admToAd(String value, Class className) { } } + private static BidType getBidType(String markupType) { + switch (markupType) { + case SMT_AD_TYPE_IMG: + case SMT_ADTYPE_RICHMEDIA: + return BidType.banner; + case SMT_ADTYPE_VIDEO: + return BidType.video; + default: + throw new PreBidException(String.format("Invalid markupType %s", markupType)); + } + } + private static R getIfNotNull(T target, Function getter) { return target != null ? getter.apply(target) : null; } + + private static int stripToZero(Integer target) { + return ObjectUtils.defaultIfNull(target, 0); + } } diff --git a/src/main/java/org/prebid/server/bidder/smartadserver/SmartadserverBidder.java b/src/main/java/org/prebid/server/bidder/smartadserver/SmartadserverBidder.java index 432f0ee3ef6..c5764ca7fc3 100644 --- a/src/main/java/org/prebid/server/bidder/smartadserver/SmartadserverBidder.java +++ b/src/main/java/org/prebid/server/bidder/smartadserver/SmartadserverBidder.java @@ -5,11 +5,11 @@ import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Publisher; import com.iab.openrtb.request.Site; -import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; @@ -25,6 +25,8 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.util.HttpUtil; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -52,11 +54,12 @@ public SmartadserverBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest request) { final List> result = new ArrayList<>(); + final List errors = new ArrayList<>(); for (Imp imp : request.getImp()) { try { final ExtImpSmartadserver extImpSmartadserver = parseImpExt(imp); - final BidRequest updateRequest = request.toBuilder() + final BidRequest updatedRequest = request.toBuilder() .imp(Collections.singletonList(imp)) .site(Site.builder() .publisher(Publisher.builder() @@ -64,42 +67,46 @@ public Result>> makeHttpRequests(BidRequest request .build()) .build()) .build(); - result.add(createSingleRequest(updateRequest, getUri())); + result.add(createSingleRequest(updatedRequest)); } catch (PreBidException e) { - return Result.withError(BidderError.badInput(e.getMessage())); + errors.add(BidderError.badInput(e.getMessage())); } } - return Result.withValues(result); + return Result.of(result, errors); } private ExtImpSmartadserver parseImpExt(Imp imp) { try { return mapper.mapper().convertValue(imp.getExt(), SMARTADSERVER_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { - throw new PreBidException(e.getMessage(), e); + throw new PreBidException("Error parsing smartadserverExt parameters"); } } - private String getUri() { - final URIBuilder uriBuilder = new URIBuilder() - .setPath(String.join("api/bid", endpointUrl)) - .addParameter("callerId", "5"); - - return uriBuilder.toString(); - } + private HttpRequest createSingleRequest(BidRequest request) { - private HttpRequest createSingleRequest(BidRequest request, String url) { - final BidRequest outgoingRequest = request.toBuilder().build(); - final String body = mapper.encode(outgoingRequest); return HttpRequest.builder() .method(HttpMethod.POST) - .uri(url) + .uri(getUri()) .headers(HttpUtil.headers()) - .body(body) - .payload(outgoingRequest) + .body(mapper.encode(request)) + .payload(request) .build(); } + private String getUri() { + final URI uri; + try { + uri = new URI(endpointUrl); + } catch (URISyntaxException e) { + throw new PreBidException(String.format("Malformed URL: %s.", endpointUrl)); + } + return new URIBuilder(uri) + .setPath(String.format("%s/api/bid", StringUtils.removeEnd(uri.getPath(), "/"))) + .addParameter("callerId", "5") + .toString(); + } + @Override public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { try { @@ -120,22 +127,11 @@ private Result> extractBids(BidRequest bidRequest, BidResponse b .map(SeatBid::getBid) .filter(Objects::nonNull) .flatMap(Collection::stream) - .map(bid -> toBidderBid(bidRequest, bid, bidResponse.getCur(), errors)) - .filter(Objects::nonNull) + .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) .collect(Collectors.toList()); return Result.of(bidderBids, errors); } - private BidderBid toBidderBid(BidRequest bidRequest, Bid bid, String currency, List errors) { - try { - final BidType bidType = getBidType(bid.getImpid(), bidRequest.getImp()); - return BidderBid.of(bid, bidType, currency); - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - return null; - } - } - private static BidType getBidType(String impId, List imps) { for (Imp imp : imps) { if (imp.getId().equals(impId)) { diff --git a/src/main/java/org/prebid/server/bidder/smartyads/SmartyAdsBidder.java b/src/main/java/org/prebid/server/bidder/smartyads/SmartyAdsBidder.java index 910d39bac0e..25a4637cd08 100644 --- a/src/main/java/org/prebid/server/bidder/smartyads/SmartyAdsBidder.java +++ b/src/main/java/org/prebid/server/bidder/smartyads/SmartyAdsBidder.java @@ -4,11 +4,13 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -29,6 +31,9 @@ import java.util.Objects; import java.util.stream.Collectors; +/** + * Between {@link Bidder} implementation. + */ public class SmartyAdsBidder implements Bidder { private static final TypeReference> SMARTYADS_EXT_TYPE_REFERENCE = @@ -37,6 +42,7 @@ public class SmartyAdsBidder implements Bidder { private static final String URL_HOST_MACRO = "{{Host}}"; private static final String URL_SOURCE_ID_MACRO = "{{SourceId}}"; private static final String URL_ACCOUNT_ID_MACRO = "{{AccountID}}"; + private static final int FIRST_SEAT_BID_INDEX = 0; private final String endpointUrl; private final JacksonMapper mapper; @@ -48,7 +54,6 @@ public SmartyAdsBidder(String endpointUrl, JacksonMapper mapper) { @Override public Result>> makeHttpRequests(BidRequest request) { - final List errors = new ArrayList<>(); final List validImps = new ArrayList<>(); ExtImpSmartyAds extImpSmartyAds = null; @@ -63,22 +68,33 @@ public Result>> makeHttpRequests(BidRequest request final BidRequest outgoingRequest = request.toBuilder().imp(validImps).build(); - return Result.of(Collections.singletonList( + return Result.withValues(Collections.singletonList( HttpRequest.builder() .method(HttpMethod.POST) .uri(resolveUrl(extImpSmartyAds)) .headers(resolveHeaders(request.getDevice())) .payload(outgoingRequest) .body(mapper.encode(outgoingRequest)) - .build()), errors); + .build())); } private ExtImpSmartyAds parseImpExt(Imp imp) { + final ExtImpSmartyAds extImpSmartyAds; try { - return mapper.mapper().convertValue(imp.getExt(), SMARTYADS_EXT_TYPE_REFERENCE).getBidder(); + extImpSmartyAds = mapper.mapper().convertValue(imp.getExt(), SMARTYADS_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { throw new PreBidException("ext.bidder not provided"); } + if (StringUtils.isBlank(extImpSmartyAds.getHost())) { + throw new PreBidException("host is a required ext.bidder param"); + } + if (StringUtils.isBlank(extImpSmartyAds.getAccountId())) { + throw new PreBidException("accountId is a required ext.bidder param"); + } + if (StringUtils.isBlank(extImpSmartyAds.getSourceId())) { + throw new PreBidException("sourceId is a required ext.bidder param"); + } + return extImpSmartyAds; } private static Imp updateImp(Imp imp) { @@ -88,11 +104,11 @@ private static Imp updateImp(Imp imp) { private String resolveUrl(ExtImpSmartyAds extImp) { return endpointUrl .replace(URL_HOST_MACRO, extImp.getHost()) - .replace(URL_SOURCE_ID_MACRO, extImp.getSourceId()) - .replace(URL_ACCOUNT_ID_MACRO, extImp.getAccountId()); + .replace(URL_SOURCE_ID_MACRO, HttpUtil.encodeUrl(extImp.getSourceId())) + .replace(URL_ACCOUNT_ID_MACRO, HttpUtil.encodeUrl(extImp.getAccountId())); } - private MultiMap resolveHeaders(Device device) { + private static MultiMap resolveHeaders(Device device) { final MultiMap headers = HttpUtil.headers(); headers.add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5"); @@ -112,29 +128,42 @@ private MultiMap resolveHeaders(Device device) { @Override public final Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { try { - final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - return Result.of(extractBids(httpCall.getRequest().getPayload(), bidResponse), Collections.emptyList()); - } catch (DecodeException | PreBidException e) { + return Result.of(extractBids(httpCall), Collections.emptyList()); + } catch (PreBidException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } } - private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { - if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { - return Collections.emptyList(); + private List extractBids(HttpCall httpCall) { + final BidResponse bidResponse; + try { + bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + } catch (DecodeException e) { + throw new PreBidException("Bad Server Response"); } - return bidsFromResponse(bidRequest, bidResponse); + if (bidResponse == null) { + throw new PreBidException("Bad Server Response"); + } + if (CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + throw new PreBidException("Empty SeatBid array"); + } + return bidsFromResponse(httpCall.getRequest().getPayload(), bidResponse); } - private List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { - final SeatBid firstSeatBid = bidResponse.getSeatbid().get(0); - return firstSeatBid.getBid().stream() + private static List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { + final SeatBid firstSeatBid = bidResponse.getSeatbid().get(FIRST_SEAT_BID_INDEX); + final List bidsFromSeat = firstSeatBid.getBid(); + if (CollectionUtils.isEmpty(bidsFromSeat)) { + return Collections.emptyList(); + } + + return bidsFromSeat.stream() .filter(Objects::nonNull) .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) .collect(Collectors.toList()); } - protected BidType getBidType(String impId, List imps) { + private static BidType getBidType(String impId, List imps) { for (Imp imp : imps) { if (imp.getId().equals(impId)) { if (imp.getVideo() != null) { diff --git a/src/main/java/org/prebid/server/bidder/triplelift/TripleliftBidder.java b/src/main/java/org/prebid/server/bidder/triplelift/TripleliftBidder.java index 3f8022e0191..ad099079b3e 100644 --- a/src/main/java/org/prebid/server/bidder/triplelift/TripleliftBidder.java +++ b/src/main/java/org/prebid/server/bidder/triplelift/TripleliftBidder.java @@ -10,6 +10,7 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.http.HttpMethod; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.model.BidderBid; import org.prebid.server.bidder.model.BidderError; @@ -31,6 +32,9 @@ import java.util.List; import java.util.Objects; +/** + * Triplelift {@link Bidder} implementation. + */ public class TripleliftBidder implements Bidder { private static final TypeReference> TRIPLELIFT_EXT_TYPE_REFERENCE = @@ -82,19 +86,16 @@ private Imp modifyImp(Imp imp) throws PreBidException { throw new PreBidException("neither Banner nor Video object specified"); } - final ExtImpTriplelift impExt = parseExtImpTriplelift(imp); - final Imp.ImpBuilder impBuilder = imp.toBuilder().tagid(impExt.getInventoryCode()); - if (impExt.getFloor() != null) { - impBuilder.bidfloor(impExt.getFloor()); - } - - return impBuilder.build(); + final ExtImpTriplelift impExt = parseImpExt(imp); + return imp.toBuilder() + .tagid(impExt.getInventoryCode()) + .bidfloor(ObjectUtils.defaultIfNull(impExt.getFloor(), imp.getBidfloor())) + .build(); } - private ExtImpTriplelift parseExtImpTriplelift(Imp imp) { + private ExtImpTriplelift parseImpExt(Imp imp) { try { - return mapper.mapper().convertValue(imp.getExt(), - TRIPLELIFT_EXT_TYPE_REFERENCE).getBidder(); + return mapper.mapper().convertValue(imp.getExt(), TRIPLELIFT_EXT_TYPE_REFERENCE).getBidder(); } catch (IllegalArgumentException e) { throw new PreBidException(e.getMessage(), e); } @@ -104,7 +105,7 @@ private ExtImpTriplelift parseExtImpTriplelift(Imp imp) { public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { final BidResponse bidResponse; try { - bidResponse = decodeBodyToBidResponse(httpCall); + bidResponse = decodeBody(httpCall); } catch (PreBidException e) { return Result.withError(BidderError.badServerResponse(e.getMessage())); } @@ -117,17 +118,18 @@ public Result> makeBids(HttpCall httpCall, BidReques final List bidderBids = new ArrayList<>(); for (SeatBid seatBid : bidResponse.getSeatbid()) { for (Bid bid : seatBid.getBid()) { - final ObjectNode ext = bid.getExt(); - if (ext == null) { + final ObjectNode bidExt = bid.getExt(); + if (bidExt == null) { errors.add(BidderError.badServerResponse(String.format("Empty ext in bid %s", bid.getId()))); break; } + try { - final TripleliftResponseExt tripleliftResponseExt = mapper.mapper().treeToValue(ext, + final TripleliftResponseExt tripleliftResponseExt = mapper.mapper().treeToValue(bidExt, TripleliftResponseExt.class); - final BidderBid bidderBid = BidderBid.of(bid, getBidType(tripleliftResponseExt), - bidResponse.getCur()); - bidderBids.add(bidderBid); + final BidType bidType = getBidType(tripleliftResponseExt); + + bidderBids.add(BidderBid.of(bid, bidType, bidResponse.getCur())); } catch (JsonProcessingException e) { errors.add(BidderError.badServerResponse(e.getMessage())); } @@ -136,7 +138,7 @@ public Result> makeBids(HttpCall httpCall, BidReques return Result.of(bidderBids, errors); } - private BidResponse decodeBodyToBidResponse(HttpCall httpCall) { + private BidResponse decodeBody(HttpCall httpCall) { try { return mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); } catch (DecodeException e) { @@ -149,9 +151,8 @@ private static BidType getBidType(TripleliftResponseExt tripleliftResponseExt) { ? tripleliftResponseExt.getTripleliftPb() : null; - if (tripleliftInnerExt != null && tripleliftInnerExt.getFormat() == 11) { - return BidType.video; - } - return BidType.banner; + return tripleliftInnerExt != null && Objects.equals(tripleliftInnerExt.getFormat(), 11) + ? BidType.video + : BidType.banner; } } diff --git a/src/main/java/org/prebid/server/bidder/ttx/TtxBidder.java b/src/main/java/org/prebid/server/bidder/ttx/TtxBidder.java index 968b73166e3..23ad4fdaf01 100644 --- a/src/main/java/org/prebid/server/bidder/ttx/TtxBidder.java +++ b/src/main/java/org/prebid/server/bidder/ttx/TtxBidder.java @@ -1,64 +1,197 @@ package org.prebid.server.bidder.ttx; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.Video; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.bidder.OpenrtbBidder; -import org.prebid.server.bidder.model.ImpWithExt; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; import org.prebid.server.bidder.ttx.proto.TtxImpExt; import org.prebid.server.bidder.ttx.proto.TtxImpExtTtx; +import org.prebid.server.bidder.ttx.response.TtxBidExt; +import org.prebid.server.bidder.ttx.response.TtxBidExtTtx; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ttx.ExtImpTtx; import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; -public class TtxBidder extends OpenrtbBidder { +/** + * 33across {@link Bidder} implementation. + */ +public class TtxBidder implements Bidder { + + private static final TypeReference> TTX_EXT_TYPE_REFERENCE = + new TypeReference>() { + }; + + private final String endpointUrl; + private final JacksonMapper mapper; public TtxBidder(String endpointUrl, JacksonMapper mapper) { - super(endpointUrl, RequestCreationStrategy.SINGLE_REQUEST, ExtImpTtx.class, mapper); + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); } @Override - protected void modifyRequest(BidRequest bidRequest, BidRequest.BidRequestBuilder requestBuilder, - List> impsWithExts) { - final List modifiedImps = impsWithExts.stream() - .map(ImpWithExt::getImp) - .collect(Collectors.toList()); - final Imp firstImp = modifiedImps.get(0); - final ExtImpTtx firstImpExt = impsWithExts.get(0).getImpExt(); + public Result>> makeHttpRequests(BidRequest request) { + final List errors = new ArrayList<>(); + final List> requests = new ArrayList<>(); - final String zoneId = firstImpExt.getZoneId(); - final TtxImpExt ttxImpExt = TtxImpExt.of( - TtxImpExtTtx.of(firstImpExt.getProductId(), StringUtils.isNotBlank(zoneId) ? zoneId : null)); - - final Imp modifiedFirstImp = firstImp.toBuilder().ext(mapper.mapper().valueToTree(ttxImpExt)).build(); - - if (modifiedImps.size() == 1) { - requestBuilder.imp(Collections.singletonList(modifiedFirstImp)); - } else { - final List subList = modifiedImps.subList(1, modifiedImps.size()); - final List finalizedImps = new ArrayList<>(subList.size() + 1); - finalizedImps.add(modifiedFirstImp); - finalizedImps.addAll(subList); - requestBuilder.imp(finalizedImps); + for (Imp imp : request.getImp()) { + try { + validateImp(imp); + final ExtImpTtx extImpTtx = parseImpExt(imp); + final Imp updatedImp = updateImp(imp, extImpTtx); + requests.add(createRequest(request, updatedImp)); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + return Result.of(requests, errors); + } + + private void validateImp(Imp imp) { + if (imp.getBanner() == null && imp.getVideo() == null) { + throw new PreBidException( + String.format("Imp ID %s must have at least one of [Banner, Video] defined", imp.getId())); + } + } + + private ExtImpTtx parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), TTX_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private Imp updateImp(Imp imp, ExtImpTtx extImpTtx) { + final String productId = extImpTtx.getProductId(); + return imp.toBuilder() + .video(updatedVideo(imp.getVideo(), productId)) + .ext(createImpExt(productId, extImpTtx.getZoneId(), extImpTtx.getSiteId())) + .build(); + } + + private static Video updatedVideo(Video video, String productId) { + if (video == null) { + return null; + } + if (isZeroOrNullInteger(video.getW()) + || isZeroOrNullInteger(video.getH()) + || CollectionUtils.isEmpty(video.getProtocols()) + || CollectionUtils.isEmpty(video.getMimes()) + || CollectionUtils.isEmpty(video.getPlaybackmethod())) { + throw new PreBidException("One or more invalid or missing video field(s) " + + "w, h, protocols, mimes, playbackmethod"); + } + final Integer videoPlacement = video.getPlacement(); + + return video.toBuilder() + .startdelay(resolveStartDelay(video.getStartdelay(), productId)) + .placement(resolvePlacement(videoPlacement, productId)) + .build(); + } + + private static boolean isZeroOrNullInteger(Integer integer) { + return integer == null || integer == 0; + } + + private static Integer resolveStartDelay(Integer startDelay, String productId) { + return Objects.equals(productId, "instream") ? Integer.valueOf(0) : startDelay; + } + + private static Integer resolvePlacement(Integer videoPlacement, String productId) { + if (Objects.equals(productId, "instream")) { + return 1; + } + if (isZeroOrNullInteger(videoPlacement)) { + return 2; } - requestBuilder.site(modifySite(bidRequest.getSite(), firstImpExt.getSiteId())); + return videoPlacement; } - private static Site modifySite(Site site, String siteId) { - final Site.SiteBuilder siteBuilder = site == null ? Site.builder() : site.toBuilder(); - return siteBuilder - .id(siteId) + private ObjectNode createImpExt(String productId, String zoneId, String siteId) { + final TtxImpExt ttxImpExt = TtxImpExt.of( + TtxImpExtTtx.of(productId, StringUtils.isNotEmpty(zoneId) ? zoneId : siteId)); + return mapper.mapper().valueToTree(ttxImpExt); + } + + private HttpRequest createRequest(BidRequest request, Imp requestImp) { + final BidRequest modifiedRequest = request.toBuilder() + .imp(Collections.singletonList(requestImp)) + .build(); + + return HttpRequest.builder() + .method(HttpMethod.POST) + .uri(endpointUrl) + .headers(HttpUtil.headers()) + .payload(modifiedRequest) + .body(mapper.encode(modifiedRequest)) .build(); } @Override - protected BidType getBidType(String impId, List imps) { - return BidType.banner; + public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.of(extractBids(bidResponse), Collections.emptyList()); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidResponse bidResponse) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + return bidsFromResponse(bidResponse); + } + + private List bidsFromResponse(BidResponse bidResponse) { + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur())) + .collect(Collectors.toList()); + } + + private BidType getBidType(Bid + bid) { + try { + final TtxBidExt ttxBidExt = mapper.mapper().convertValue(bid.getExt(), TtxBidExt.class); + return ttxBidExt != null ? getBidTypeByTtx(ttxBidExt.getTtx()) : BidType.banner; + } catch (IllegalArgumentException e) { + return BidType.banner; + } + } + + private static BidType getBidTypeByTtx(TtxBidExtTtx bidExt) { + return bidExt != null && Objects.equals(bidExt.getMediaType(), "video") + ? BidType.video + : BidType.banner; } } diff --git a/src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrustPrivacy.java b/src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExt.java similarity index 50% rename from src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrustPrivacy.java rename to src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExt.java index e654da1e065..6f4ba1b5702 100644 --- a/src/main/java/org/prebid/server/bidder/adform/model/AdformDigitrustPrivacy.java +++ b/src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExt.java @@ -1,11 +1,11 @@ -package org.prebid.server.bidder.adform.model; +package org.prebid.server.bidder.ttx.response; import lombok.AllArgsConstructor; import lombok.Value; @AllArgsConstructor(staticName = "of") @Value -public class AdformDigitrustPrivacy { +public class TtxBidExt { - Boolean optout; + TtxBidExtTtx ttx; } diff --git a/src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExtTtx.java b/src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExtTtx.java new file mode 100644 index 00000000000..6a66b3322f7 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/ttx/response/TtxBidExtTtx.java @@ -0,0 +1,15 @@ +package org.prebid.server.bidder.ttx.response; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +@AllArgsConstructor(staticName = "of") +@Value +public class TtxBidExtTtx { + + @JsonProperty("mediaType") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + String mediaType; +} diff --git a/src/main/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidder.java b/src/main/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidder.java index f7604f55b56..54257780b4e 100644 --- a/src/main/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidder.java +++ b/src/main/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidder.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -110,12 +111,18 @@ private static BidRequest modifyRequest(BidRequest request, Imp imp, ExtImpVeriz impBuilder.banner(modifyBanner(banner)); } + final BidRequest.BidRequestBuilder requestBuilder = request.toBuilder(); + final Site site = request.getSite(); - final Site.SiteBuilder siteBuilder = site == null ? Site.builder() : site.toBuilder(); + final App app = request.getApp(); + if (site != null) { + requestBuilder.site(site.toBuilder().id(extImpVerizonmedia.getDcn()).build()); + } else if (app != null) { + requestBuilder.app(app.toBuilder().id(extImpVerizonmedia.getDcn()).build()); + } - return request.toBuilder() + return requestBuilder .imp(Collections.singletonList(impBuilder.build())) - .site(siteBuilder.id(extImpVerizonmedia.getDcn()).build()) .build(); } diff --git a/src/main/java/org/prebid/server/bidder/vrtcal/VrtcalBidder.java b/src/main/java/org/prebid/server/bidder/vrtcal/VrtcalBidder.java index 5c436cbb863..c60f7cd8715 100644 --- a/src/main/java/org/prebid/server/bidder/vrtcal/VrtcalBidder.java +++ b/src/main/java/org/prebid/server/bidder/vrtcal/VrtcalBidder.java @@ -1,6 +1,7 @@ package org.prebid.server.bidder.vrtcal; import com.iab.openrtb.request.Imp; +import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.OpenrtbBidder; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.request.vrtcal.ExtImpVrtcal; @@ -8,6 +9,9 @@ import java.util.List; +/** + * Vrtcal {@link Bidder} implementation. + */ public class VrtcalBidder extends OpenrtbBidder { public VrtcalBidder(String endpointUrl, JacksonMapper mapper) { diff --git a/src/main/java/org/prebid/server/bidder/yieldmo/YieldmoBidder.java b/src/main/java/org/prebid/server/bidder/yieldmo/YieldmoBidder.java index 4d6a237d217..393f1685dc8 100644 --- a/src/main/java/org/prebid/server/bidder/yieldmo/YieldmoBidder.java +++ b/src/main/java/org/prebid/server/bidder/yieldmo/YieldmoBidder.java @@ -1,6 +1,7 @@ package org.prebid.server.bidder.yieldmo; import com.iab.openrtb.request.Imp; +import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.OpenrtbBidder; import org.prebid.server.bidder.yieldmo.proto.YieldmoImpExt; import org.prebid.server.exception.PreBidException; @@ -10,6 +11,9 @@ import java.util.List; +/** + * Yieldmo {@link Bidder} implementation. + */ public class YieldmoBidder extends OpenrtbBidder { public YieldmoBidder(String endpointUrl, JacksonMapper mapper) { diff --git a/src/main/java/org/prebid/server/cache/CacheService.java b/src/main/java/org/prebid/server/cache/CacheService.java index f28903dd638..c6a06a4bd40 100644 --- a/src/main/java/org/prebid/server/cache/CacheService.java +++ b/src/main/java/org/prebid/server/cache/CacheService.java @@ -11,6 +11,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.GeneratedBidIds; import org.prebid.server.cache.model.CacheBid; import org.prebid.server.cache.model.CacheContext; import org.prebid.server.cache.model.CacheHttpRequest; @@ -48,7 +49,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeoutException; import java.util.function.Function; @@ -274,8 +274,8 @@ public Future cacheBidsOpenrtb(List doCacheOpenrtb(List bids, List videoBids, AuctionContext auctionContext, - Map> bidderToVideoBidIdsToModify, - Map> biddersToCacheBidIds, + GeneratedBidIds bidderToVideoBidIdsToModify, + GeneratedBidIds biddersToCacheBidIds, EventsContext eventsContext) { final Account account = auctionContext.getAccount(); @@ -488,7 +488,7 @@ private static CachedCreative createPutObjectVideoOnly(Bid bid) { * Used for OpenRTB auction request. Also, adds win url to result object if events are enabled. */ private CachedCreative createJsonPutObjectOpenrtb(CacheBid cacheBid, - Map> biddersToCacheBidIds, + GeneratedBidIds biddersToCacheBidIds, Account account, EventsContext eventsContext) { @@ -513,7 +513,7 @@ private CachedCreative createJsonPutObjectOpenrtb(CacheBid cacheBid, * Makes XML type {@link PutObject} from {@link com.iab.openrtb.response.Bid}. Used for OpenRTB auction request. */ private CachedCreative createXmlPutObjectOpenrtb(CacheBid cacheBid, - Map> bidderToVideoBidIdsToModify, + GeneratedBidIds bidderToVideoBidIdsToModify, Account account, EventsContext eventsContext) { @@ -544,16 +544,17 @@ private static String resolveVastXmlFrom(com.iab.openrtb.response.Bid bid) { return bid.getAdm(); } - private String generateWinUrl(Map> biddersToCacheBidIds, + private String generateWinUrl(GeneratedBidIds biddersToCacheBidIds, com.iab.openrtb.response.Bid bid, Account account, EventsContext eventsContext) { if (eventsContext.isEnabledForAccount() && eventsContext.isEnabledForRequest()) { final String bidId = bid.getId(); - return findBidderForBidId(biddersToCacheBidIds, bidId) + final String impId = bid.getImpid(); + return biddersToCacheBidIds.getBidderForBid(bidId, impId) .map(bidder -> eventsService.winUrl( - bidId, + biddersToCacheBidIds.getGeneratedId(bidder, bidId, impId), bidder, account.getId(), eventsContext.getAuctionTimestamp(), @@ -564,16 +565,17 @@ private String generateWinUrl(Map> biddersToCacheBidIds, return null; } - private String generateVastUrlTracking(Map> bidderToVideoBidIdsToModify, + private String generateVastUrlTracking(GeneratedBidIds bidderToVideoBidIdsToModify, com.iab.openrtb.response.Bid bid, Account account, EventsContext eventsContext) { if (eventsContext.isEnabledForAccount()) { final String bidId = bid.getId(); - return findBidderForBidId(bidderToVideoBidIdsToModify, bidId) + final String impId = bid.getImpid(); + return bidderToVideoBidIdsToModify.getBidderForBid(bidId, impId) .map(bidder -> eventsService.vastUrlTracking( - bidId, + bidderToVideoBidIdsToModify.getGeneratedId(bidder, bidId, impId), bidder, account.getId(), eventsContext.getAuctionTimestamp(), @@ -584,13 +586,6 @@ private String generateVastUrlTracking(Map> bidderToVideoBi return null; } - private static Optional findBidderForBidId(Map> biddersToCacheBidIds, String bidId) { - return biddersToCacheBidIds.entrySet().stream() - .filter(biddersAndBidIds -> biddersAndBidIds.getValue().contains(bidId)) - .findFirst() - .map(Map.Entry::getKey); - } - private String appendTrackingUrlToVastXml(String vastXml, String vastUrlTracking) { final String closeTag = ""; final int closeTagIndex = vastXml.indexOf(closeTag); diff --git a/src/main/java/org/prebid/server/cache/model/CacheContext.java b/src/main/java/org/prebid/server/cache/model/CacheContext.java index f26c975c212..7c418808387 100644 --- a/src/main/java/org/prebid/server/cache/model/CacheContext.java +++ b/src/main/java/org/prebid/server/cache/model/CacheContext.java @@ -2,9 +2,7 @@ import lombok.Builder; import lombok.Value; - -import java.util.List; -import java.util.Map; +import org.prebid.server.auction.model.GeneratedBidIds; /** * Holds the state needed to perform caching response bids. @@ -21,7 +19,7 @@ public class CacheContext { Integer cacheVideoBidsTtl; - Map> bidderToVideoBidIdsToModify; + GeneratedBidIds bidderToVideoGeneratedBidIdsToModify; - Map> bidderToBidIds; + GeneratedBidIds bidderToBidsToGeneratedIds; } diff --git a/src/main/java/org/prebid/server/currency/CurrencyConversionService.java b/src/main/java/org/prebid/server/currency/CurrencyConversionService.java index 8645c86d458..21ebcb07a9a 100644 --- a/src/main/java/org/prebid/server/currency/CurrencyConversionService.java +++ b/src/main/java/org/prebid/server/currency/CurrencyConversionService.java @@ -1,5 +1,6 @@ package org.prebid.server.currency; +import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.logging.Logger; @@ -10,6 +11,9 @@ import org.prebid.server.currency.proto.CurrencyConversionRates; import org.prebid.server.exception.PreBidException; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestCurrency; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; import org.prebid.server.spring.config.model.ExternalConversionProperties; import org.prebid.server.util.HttpUtil; import org.prebid.server.vertx.Initializable; @@ -19,7 +23,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; -import java.time.Clock; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; @@ -64,14 +68,16 @@ public CurrencyConversionService(ExternalConversionProperties externalConversion @Override public void initialize() { if (externalConversionProperties != null) { - final Long refreshPeriod = externalConversionProperties.getRefreshPeriod(); - final Long defaultTimeout = externalConversionProperties.getDefaultTimeout(); + final Long refreshPeriod = externalConversionProperties.getRefreshPeriodMs(); + final Long defaultTimeout = externalConversionProperties.getDefaultTimeoutMs(); final HttpClient httpClient = externalConversionProperties.getHttpClient(); final Vertx vertx = externalConversionProperties.getVertx(); vertx.setPeriodic(refreshPeriod, ignored -> populatesLatestCurrencyRates(currencyServerUrl, defaultTimeout, httpClient)); populatesLatestCurrencyRates(currencyServerUrl, defaultTimeout, httpClient); + + externalConversionProperties.getMetrics().createCurrencyRatesGauge(this::isRatesStale); } } @@ -82,7 +88,7 @@ private void populatesLatestCurrencyRates(String currencyServerUrl, Long default httpClient.get(currencyServerUrl, defaultTimeout) .map(this::processResponse) .map(this::updateCurrencyRates) - .recover(CurrencyConversionService::failResponse); + .otherwise(this::handleErrorResponse); } /** @@ -104,21 +110,37 @@ private CurrencyConversionRates processResponse(HttpClientResponse response) { } } - private CurrencyConversionRates updateCurrencyRates(CurrencyConversionRates currencyConversionRates) { + private Void updateCurrencyRates(CurrencyConversionRates currencyConversionRates) { final Map> receivedCurrencyRates = currencyConversionRates.getConversions(); if (receivedCurrencyRates != null) { externalCurrencyRates = receivedCurrencyRates; - lastUpdated = ZonedDateTime.now(Clock.systemUTC()); + lastUpdated = now(); } - return currencyConversionRates; + + return null; } /** * Handles errors occurred while HTTP request or response processing. */ - private static Future failResponse(Throwable exception) { + private Void handleErrorResponse(Throwable exception) { logger.warn("Error occurred while request to currency service", exception); - return Future.failedFuture(exception); + + if (externalRatesAreStale()) { + externalCurrencyRates = null; + } + + return null; + } + + private boolean externalRatesAreStale() { + final Long stalePeriodMs = externalConversionProperties.getStalePeriodMs(); + + return stalePeriodMs != null && Duration.between(lastUpdated, now()).toMillis() > stalePeriodMs; + } + + private ZonedDateTime now() { + return ZonedDateTime.now(externalConversionProperties.getClock()); } public boolean isExternalRatesActive() { @@ -130,7 +152,7 @@ public String getCurrencyServerUrl() { } public Long getRefreshPeriod() { - return externalConversionProperties != null ? externalConversionProperties.getRefreshPeriod() : null; + return externalConversionProperties != null ? externalConversionProperties.getRefreshPeriodMs() : null; } public ZonedDateTime getLastUpdated() { @@ -141,13 +163,23 @@ public Map> getExternalCurrencyRates() { return externalCurrencyRates; } + /** + * Converts price from fromCurrency to toCurrency using rates from {@link BidRequest} or external currency service. + * If bidrequest.prebid.currecy.usepbsrates is true it takes rates from prebid server, if false from request. + * Default value of usepbsrates is true. + * Throws {@link PreBidException} in case conversion is not possible. + */ + public BigDecimal convertCurrency(BigDecimal price, BidRequest bidRequest, String fromCurrency, String toCurrency) { + return convertCurrency(price, currencyRates(bidRequest), fromCurrency, toCurrency, usepbsrates(bidRequest)); + } + /** * Converts price from bidCurrency to adServerCurrency using rates and usepbsrates flag defined in request. * If usepbsrates is true it takes rates from prebid server, if false from request. Default value of usepbsrates * is true. * Throws {@link PreBidException} in case conversion is not possible. */ - public BigDecimal convertCurrency(BigDecimal price, Map> requestCurrencyRates, + private BigDecimal convertCurrency(BigDecimal price, Map> requestCurrencyRates, String adServerCurrency, String bidCurrency, Boolean usepbsrates) { // use Default USD currency if bidder left this field empty. After, when bidder will implement multi currency // support it will be changed to throwing PrebidException. @@ -180,6 +212,23 @@ public BigDecimal convertCurrency(BigDecimal price, Map> currencyRates(BidRequest bidRequest) { + final ExtRequestPrebid prebid = extRequestPrebid(bidRequest); + final ExtRequestCurrency currency = prebid != null ? prebid.getCurrency() : null; + return currency != null ? currency.getRates() : null; + } + + private static ExtRequestPrebid extRequestPrebid(BidRequest bidRequest) { + final ExtRequest requestExt = bidRequest.getExt(); + return requestExt != null ? requestExt.getPrebid() : null; + } + + private static Boolean usepbsrates(BidRequest bidRequest) { + final ExtRequestPrebid prebid = extRequestPrebid(bidRequest); + final ExtRequestCurrency currency = prebid != null ? prebid.getCurrency() : null; + return currency != null ? currency.getUsepbsrates() : null; + } + /** * Returns conversion rate from the given currency rates according to priority. */ @@ -263,4 +312,15 @@ private static BigDecimal findIntermediateConversionRate(Map } return conversionRate; } + + private boolean isRatesStale() { + if (lastUpdated == null) { + return false; + } + + final ZonedDateTime stalenessBoundary = ZonedDateTime.now(externalConversionProperties.getClock()) + .minus(Duration.ofMillis(externalConversionProperties.getStaleAfterMs())); + + return lastUpdated.isBefore(stalenessBoundary); + } } diff --git a/src/main/java/org/prebid/server/handler/AuctionHandler.java b/src/main/java/org/prebid/server/handler/AuctionHandler.java index c79c8d28545..0aeadc8d5a2 100644 --- a/src/main/java/org/prebid/server/handler/AuctionHandler.java +++ b/src/main/java/org/prebid/server/handler/AuctionHandler.java @@ -107,16 +107,11 @@ public AuctionHandler(ApplicationSettings applicationSettings, public void handle(RoutingContext context) { final long startTime = clock.millis(); - final boolean isSafari = HttpUtil.isSafari(context.request().headers().get(HttpUtil.USER_AGENT_HEADER)); - - metrics.updateSafariRequestsMetric(isSafari); - preBidRequestContextFactory.fromRequest(context) .recover(exception -> failWithInvalidRequest( String.format("Error parsing request: %s", exception.getMessage()), exception)) - .map(preBidRequestContext -> - updateAppAndNoCookieAndImpsMetrics(preBidRequestContext, isSafari)) + .map(this::updateAppAndNoCookieAndImpsMetrics) .compose(preBidRequestContext -> accountFrom(preBidRequestContext) .map(account -> Tuple2.of(preBidRequestContext, account))) @@ -147,13 +142,12 @@ public void handle(RoutingContext context) { respondWith(bidResponseOrError(preBidResponseResult), context, startTime)); } - private PreBidRequestContext updateAppAndNoCookieAndImpsMetrics(PreBidRequestContext preBidRequestContext, - boolean isSafari) { + private PreBidRequestContext updateAppAndNoCookieAndImpsMetrics(PreBidRequestContext preBidRequestContext) { final PreBidRequest preBidRequest = preBidRequestContext.getPreBidRequest(); final List adUnits = preBidRequest.getAdUnits(); metrics.updateAppAndNoCookieAndImpsRequestedMetrics(preBidRequest.getApp() != null, - !preBidRequestContext.isNoLiveUids(), isSafari, adUnits.size()); + !preBidRequestContext.isNoLiveUids(), adUnits.size()); final Map mediaTypeToCount = adUnits.stream() .map(AdUnit::getMediaTypes) @@ -400,7 +394,7 @@ private static PreBidResponse addTargetingKeywords(PreBidRequest preBidRequest, final Integer sortBids = preBidRequest.getSortBids(); if (sortBids != null && sortBids == 1) { final TargetingKeywordsCreator keywordsCreator = - TargetingKeywordsCreator.create(account.getPriceGranularity(), true, true, false, 0); + TargetingKeywordsCreator.create(account.getPriceGranularity(), true, true, false, false, 0); final Map> adUnitCodeToBids = preBidResponse.getBids().stream() .collect(Collectors.groupingBy(Bid::getCode)); diff --git a/src/main/java/org/prebid/server/handler/CookieSyncHandler.java b/src/main/java/org/prebid/server/handler/CookieSyncHandler.java index 30cdec79030..487f6e44d9a 100644 --- a/src/main/java/org/prebid/server/handler/CookieSyncHandler.java +++ b/src/main/java/org/prebid/server/handler/CookieSyncHandler.java @@ -29,6 +29,7 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.privacy.gdpr.TcfDefinerService; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.TcfContext; import org.prebid.server.privacy.gdpr.model.TcfResponse; import org.prebid.server.privacy.model.Privacy; import org.prebid.server.privacy.model.PrivacyContext; @@ -96,7 +97,7 @@ public CookieSyncHandler(String externalUrl, this.activeBidders = activeBidders(bidderCatalog); this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); - this.gdprHostVendorId = gdprHostVendorId; + this.gdprHostVendorId = validateHostVendorId(gdprHostVendorId); this.defaultCoopSync = defaultCoopSync; this.listOfCoopSyncBidders = CollectionUtils.isNotEmpty(listOfCoopSyncBidders) ? listOfCoopSyncBidders @@ -111,6 +112,13 @@ private static Set activeBidders(BidderCatalog bidderCatalog) { return bidderCatalog.names().stream().filter(bidderCatalog::isActive).collect(Collectors.toSet()); } + private static Integer validateHostVendorId(Integer gdprHostVendorId) { + if (gdprHostVendorId == null) { + logger.warn("gdpr.host-vendor-id not specified. Will skip host company GDPR checks"); + } + return gdprHostVendorId; + } + @Override public void handle(RoutingContext context) { metrics.updateCookieSyncRequestMetric(); @@ -165,9 +173,8 @@ public void handle(RoutingContext context) { .compose(account -> privacyEnforcementService.contextFromCookieSyncRequest( cookieSyncRequest, context.request(), account, timeout) .map(privacyContext -> Tuple2.of(account, privacyContext))) - .map((Tuple2 accountAndPrivacy) -> tcfDefinerService.resultForVendorIds( - vendorIds, accountAndPrivacy.getRight().getTcfContext()) - .compose(this::handleVendorIdResult) + .map((Tuple2 accountAndPrivacy) -> allowedForVendorId(vendorIds, + accountAndPrivacy.getRight().getTcfContext()) .compose(ignored -> tcfDefinerService.resultForBidderNames( biddersToSync, accountAndPrivacy.getRight().getTcfContext(), @@ -208,6 +215,17 @@ private Set biddersToSync(List requestBidders, Boolean requestCo return new HashSet<>(requestBidders); } + /** + * Returns failed future if vendor is not allowed for cookie sync. + * If host vendor id is null, host allowed to sync cookies. + */ + private Future allowedForVendorId(Set vendorIds, TcfContext tcfContext) { + return gdprHostVendorId != null + ? tcfDefinerService.resultForVendorIds(vendorIds, tcfContext) + .compose(this::handleVendorIdResult) + : Future.succeededFuture(); + } + private Set addAllCoopSyncBidders(List bidders) { final Set updatedBidders = listOfCoopSyncBidders.stream() .flatMap(Collection::stream) diff --git a/src/main/java/org/prebid/server/handler/SetuidHandler.java b/src/main/java/org/prebid/server/handler/SetuidHandler.java index 1419f2a6aa1..57ae761cb5c 100644 --- a/src/main/java/org/prebid/server/handler/SetuidHandler.java +++ b/src/main/java/org/prebid/server/handler/SetuidHandler.java @@ -72,7 +72,7 @@ public SetuidHandler(long defaultTimeout, this.applicationSettings = Objects.requireNonNull(applicationSettings); this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); - this.gdprHostVendorId = gdprHostVendorId; + this.gdprHostVendorId = validateHostVendorId(gdprHostVendorId); this.analyticsReporter = Objects.requireNonNull(analyticsReporter); this.metrics = Objects.requireNonNull(metrics); this.timeoutFactory = Objects.requireNonNull(timeoutFactory); @@ -84,6 +84,13 @@ public SetuidHandler(long defaultTimeout, .collect(Collectors.toSet()); } + private static Integer validateHostVendorId(Integer gdprHostVendorId) { + if (gdprHostVendorId == null) { + logger.warn("gdpr.host-vendor-id not specified. Will skip host company GDPR checks"); + } + return gdprHostVendorId; + } + @Override public void handle(RoutingContext context) { final UidsCookie uidsCookie = uidsCookieService.parseFromRequest(context); @@ -106,6 +113,11 @@ public void handle(RoutingContext context) { return; } + if (gdprHostVendorId == null) { + respondWithCookie(context, cookieName, uidsCookie); + return; + } + final Set vendorIds = Collections.singleton(gdprHostVendorId); final String requestAccount = context.request().getParam(ACCOUNT_PARAM); final Timeout timeout = timeoutFactory.create(defaultTimeout); diff --git a/src/main/java/org/prebid/server/handler/openrtb2/AmpHandler.java b/src/main/java/org/prebid/server/handler/openrtb2/AmpHandler.java index ad5b50fb092..9be3171ee9b 100644 --- a/src/main/java/org/prebid/server/handler/openrtb2/AmpHandler.java +++ b/src/main/java/org/prebid/server/handler/openrtb2/AmpHandler.java @@ -115,16 +115,13 @@ public void handle(RoutingContext routingContext) { // more accurately if we note the real start time, and use it to compute the auction timeout. final long startTime = clock.millis(); - final boolean isSafari = HttpUtil.isSafari(routingContext.request().headers().get(HttpUtil.USER_AGENT_HEADER)); - metrics.updateSafariRequestsMetric(isSafari); - final AmpEvent.AmpEventBuilder ampEventBuilder = AmpEvent.builder() .httpContext(HttpContext.from(routingContext)); ampRequestFactory.fromRequest(routingContext, startTime) .map(context -> addToEvent(context, ampEventBuilder::auctionContext, context)) - .map(context -> updateAppAndNoCookieAndImpsMetrics(context, isSafari)) + .map(this::updateAppAndNoCookieAndImpsMetrics) .compose(context -> exchangeService.holdAuction(context) .map(bidResponse -> Tuple2.of(bidResponse, context))) @@ -155,13 +152,13 @@ private static R addToEvent(T field, Consumer consumer, R result) { return result; } - private AuctionContext updateAppAndNoCookieAndImpsMetrics(AuctionContext context, boolean isSafari) { + private AuctionContext updateAppAndNoCookieAndImpsMetrics(AuctionContext context) { final BidRequest bidRequest = context.getBidRequest(); final UidsCookie uidsCookie = context.getUidsCookie(); final List imps = bidRequest.getImp(); metrics.updateAppAndNoCookieAndImpsRequestedMetrics(bidRequest.getApp() != null, uidsCookie.hasLiveUids(), - isSafari, imps.size()); + imps.size()); metrics.updateImpTypesMetrics(imps); diff --git a/src/main/java/org/prebid/server/handler/openrtb2/AuctionHandler.java b/src/main/java/org/prebid/server/handler/openrtb2/AuctionHandler.java index 6e0b6e553ee..86651f0bb07 100644 --- a/src/main/java/org/prebid/server/handler/openrtb2/AuctionHandler.java +++ b/src/main/java/org/prebid/server/handler/openrtb2/AuctionHandler.java @@ -74,16 +74,13 @@ public void handle(RoutingContext routingContext) { // more accurately if we note the real start time, and use it to compute the auction timeout. final long startTime = clock.millis(); - final boolean isSafari = HttpUtil.isSafari(routingContext.request().headers().get(HttpUtil.USER_AGENT_HEADER)); - metrics.updateSafariRequestsMetric(isSafari); - final AuctionEvent.AuctionEventBuilder auctionEventBuilder = AuctionEvent.builder() .httpContext(HttpContext.from(routingContext)); auctionRequestFactory.fromRequest(routingContext, startTime) .map(context -> addToEvent(context, auctionEventBuilder::auctionContext, context)) - .map(context -> updateAppAndNoCookieAndImpsMetrics(context, isSafari)) + .map(context -> updateAppAndNoCookieAndImpsMetrics(context)) .compose(context -> exchangeService.holdAuction(context) .map(bidResponse -> Tuple2.of(bidResponse, context))) @@ -97,13 +94,13 @@ private static R addToEvent(T field, Consumer consumer, R result) { return result; } - private AuctionContext updateAppAndNoCookieAndImpsMetrics(AuctionContext context, boolean isSafari) { + private AuctionContext updateAppAndNoCookieAndImpsMetrics(AuctionContext context) { final BidRequest bidRequest = context.getBidRequest(); final UidsCookie uidsCookie = context.getUidsCookie(); final List imps = bidRequest.getImp(); metrics.updateAppAndNoCookieAndImpsRequestedMetrics(bidRequest.getApp() != null, uidsCookie.hasLiveUids(), - isSafari, imps.size()); + imps.size()); metrics.updateImpTypesMetrics(imps); diff --git a/src/main/java/org/prebid/server/handler/openrtb2/VideoHandler.java b/src/main/java/org/prebid/server/handler/openrtb2/VideoHandler.java index 36218834993..d021c76df0f 100644 --- a/src/main/java/org/prebid/server/handler/openrtb2/VideoHandler.java +++ b/src/main/java/org/prebid/server/handler/openrtb2/VideoHandler.java @@ -63,8 +63,6 @@ public void handle(RoutingContext routingContext) { // more accurately if we note the real start time, and use it to compute the auction timeout. final long startTime = clock.millis(); - final boolean isSafari = HttpUtil.isSafari(routingContext.request().headers().get(HttpUtil.USER_AGENT_HEADER)); - metrics.updateSafariRequestsMetric(isSafari); final VideoEvent.VideoEventBuilder videoEventBuilder = VideoEvent.builder() .httpContext(HttpContext.from(routingContext)); diff --git a/src/main/java/org/prebid/server/identity/IdGenerator.java b/src/main/java/org/prebid/server/identity/IdGenerator.java index 4b75fe47ebc..ea34457d98c 100644 --- a/src/main/java/org/prebid/server/identity/IdGenerator.java +++ b/src/main/java/org/prebid/server/identity/IdGenerator.java @@ -6,4 +6,6 @@ public interface IdGenerator { String generateId(); + + IdGeneratorType getType(); } diff --git a/src/main/java/org/prebid/server/identity/NoneIdGenerator.java b/src/main/java/org/prebid/server/identity/NoneIdGenerator.java index 5e458bf1875..14aaba41886 100644 --- a/src/main/java/org/prebid/server/identity/NoneIdGenerator.java +++ b/src/main/java/org/prebid/server/identity/NoneIdGenerator.java @@ -9,4 +9,9 @@ public class NoneIdGenerator implements IdGenerator { public String generateId() { return null; } + + @Override + public IdGeneratorType getType() { + return IdGeneratorType.none; + } } diff --git a/src/main/java/org/prebid/server/identity/UUIDIdGenerator.java b/src/main/java/org/prebid/server/identity/UUIDIdGenerator.java index 927c0255cc5..7f6e95ef371 100644 --- a/src/main/java/org/prebid/server/identity/UUIDIdGenerator.java +++ b/src/main/java/org/prebid/server/identity/UUIDIdGenerator.java @@ -11,4 +11,9 @@ public class UUIDIdGenerator implements IdGenerator { public String generateId() { return UUID.randomUUID().toString(); } + + @Override + public IdGeneratorType getType() { + return IdGeneratorType.uuid; + } } diff --git a/src/main/java/org/prebid/server/util/JsonMergeUtil.java b/src/main/java/org/prebid/server/json/JsonMerger.java similarity index 92% rename from src/main/java/org/prebid/server/util/JsonMergeUtil.java rename to src/main/java/org/prebid/server/json/JsonMerger.java index de5b4371095..b46010eb5a8 100644 --- a/src/main/java/org/prebid/server/util/JsonMergeUtil.java +++ b/src/main/java/org/prebid/server/json/JsonMerger.java @@ -1,4 +1,4 @@ -package org.prebid.server.util; +package org.prebid.server.json; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -6,17 +6,15 @@ import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.exception.InvalidRequestException; -import org.prebid.server.json.JacksonMapper; import java.io.IOException; import java.util.Objects; -// TODO: refactor to be instance instead of util -public class JsonMergeUtil { +public class JsonMerger { private final JacksonMapper mapper; - public JsonMergeUtil(JacksonMapper mapper) { + public JsonMerger(JacksonMapper mapper) { this.mapper = Objects.requireNonNull(mapper); } @@ -50,7 +48,7 @@ public T merge(T originalObject, String storedData, String id, Class clas public T merge(T originalObject, T mergingObject, Class classToCast) { if (!ObjectUtils.allNotNull(originalObject, mergingObject)) { - return ObjectUtils.firstNonNull(originalObject, mergingObject); + return ObjectUtils.defaultIfNull(originalObject, mergingObject); } final JsonNode originJsonNode = mapper.mapper().valueToTree(originalObject); diff --git a/src/main/java/org/prebid/server/log/ConditionalLogger.java b/src/main/java/org/prebid/server/log/ConditionalLogger.java index cf375849d08..0b1f9d5c4cd 100644 --- a/src/main/java/org/prebid/server/log/ConditionalLogger.java +++ b/src/main/java/org/prebid/server/log/ConditionalLogger.java @@ -44,6 +44,10 @@ public ConditionalLogger(Logger logger) { this(null, logger); } + public void infoWithKey(String key, String message, int limit) { + log(key, limit, logger -> logger.info(message)); + } + public void info(String message, int limit) { log(message, limit, logger -> logger.info(message)); } @@ -52,6 +56,10 @@ public void info(String message, long duration, TimeUnit unit) { log(message, duration, unit, logger -> logger.info(message)); } + public void errorWithKey(String key, String message, int limit) { + log(key, limit, logger -> logger.error(message)); + } + public void error(String message, int limit) { log(message, limit, logger -> logger.error(message)); } diff --git a/src/main/java/org/prebid/server/metric/AccountMetrics.java b/src/main/java/org/prebid/server/metric/AccountMetrics.java index 2dcfbba217a..622e08f96a9 100644 --- a/src/main/java/org/prebid/server/metric/AccountMetrics.java +++ b/src/main/java/org/prebid/server/metric/AccountMetrics.java @@ -12,26 +12,26 @@ */ class AccountMetrics extends UpdatableMetrics { - private final Function adapterMetricsCreator; // not thread-safe maps are intentionally used here because it's harmless in this particular case - eventually // this all boils down to metrics lookup by underlying metric registry and that operation is guaranteed to be // thread-safe - private final Map adapterMetrics; private final Function requestTypeMetricsCreator; private final Map requestTypeMetrics; + private final AdapterMetrics adapterMetrics; private final RequestMetrics requestsMetrics; private final CacheMetrics cacheMetrics; + private final ResponseMetrics responseMetrics; AccountMetrics(MetricRegistry metricRegistry, CounterType counterType, String account) { super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), nameCreator(createPrefix(Objects.requireNonNull(account)))); - adapterMetricsCreator = adapterType -> new AdapterMetrics(metricRegistry, counterType, account, adapterType); - adapterMetrics = new HashMap<>(); requestTypeMetricsCreator = requestType -> new RequestTypeMetrics(metricRegistry, counterType, createPrefix(account), requestType); + adapterMetrics = new AdapterMetrics(metricRegistry, counterType, createPrefix(account)); requestTypeMetrics = new HashMap<>(); requestsMetrics = new RequestMetrics(metricRegistry, counterType, createPrefix(account)); cacheMetrics = new CacheMetrics(metricRegistry, counterType, createPrefix(account)); + responseMetrics = new ResponseMetrics(metricRegistry, counterType, createPrefix(account)); } private static String createPrefix(String account) { @@ -42,8 +42,8 @@ private static Function nameCreator(String prefix) { return metricName -> String.format("%s.%s", prefix, metricName.toString()); } - AdapterMetrics forAdapter(String adapterType) { - return adapterMetrics.computeIfAbsent(adapterType, adapterMetricsCreator); + AdapterMetrics adapter() { + return adapterMetrics; } RequestTypeMetrics requestType(MetricName requestType) { @@ -57,4 +57,8 @@ RequestMetrics requests() { CacheMetrics cache() { return cacheMetrics; } + + ResponseMetrics response() { + return responseMetrics; + } } diff --git a/src/main/java/org/prebid/server/metric/AdapterMetrics.java b/src/main/java/org/prebid/server/metric/AdapterMetrics.java index 80b406ec94a..96c3621e431 100644 --- a/src/main/java/org/prebid/server/metric/AdapterMetrics.java +++ b/src/main/java/org/prebid/server/metric/AdapterMetrics.java @@ -12,61 +12,27 @@ */ class AdapterMetrics extends UpdatableMetrics { - private final Function requestTypeMetricsCreator; - private final Map requestTypeMetrics; - private final RequestMetrics requestMetrics; - private final Function bidTypeMetricsCreator; - private final Map bidTypeMetrics; + private final Function adapterMetricsCreator; + private final Map adapterMetrics; - AdapterMetrics(MetricRegistry metricRegistry, CounterType counterType, String adapterType) { + AdapterMetrics(MetricRegistry metricRegistry, CounterType counterType, String accountPrefix) { super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), - nameCreator(createAdapterPrefix(Objects.requireNonNull(adapterType)))); + nameCreator(createAdapterSuffix(Objects.requireNonNull(accountPrefix)))); - bidTypeMetricsCreator = bidType -> - new BidTypeMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType), bidType); - requestTypeMetricsCreator = requestType -> - new RequestTypeMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType), requestType); - requestTypeMetrics = new HashMap<>(); - requestMetrics = new RequestMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType)); - bidTypeMetrics = new HashMap<>(); + adapterMetrics = new HashMap<>(); + adapterMetricsCreator = adapterType -> new AdapterTypeMetrics(metricRegistry, counterType, + createAdapterSuffix(Objects.requireNonNull(accountPrefix)), adapterType); } - AdapterMetrics(MetricRegistry metricRegistry, CounterType counterType, String account, String adapterType) { - super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), - nameCreator(createAccountAdapterPrefix(Objects.requireNonNull(account), - Objects.requireNonNull(adapterType)))); - - requestMetrics = new RequestMetrics(metricRegistry, counterType, - createAccountAdapterPrefix(account, adapterType)); - - // not used for account.adapter metrics - requestTypeMetricsCreator = null; - requestTypeMetrics = null; - bidTypeMetricsCreator = null; - bidTypeMetrics = null; - } - - private static String createAdapterPrefix(String adapterType) { - return String.format("adapter.%s", adapterType); - } - - private static String createAccountAdapterPrefix(String account, String adapterType) { - return String.format("account.%s.%s", account, adapterType); + private static String createAdapterSuffix(String prefix) { + return String.format("%s.adapter", prefix); } private static Function nameCreator(String prefix) { return metricName -> String.format("%s.%s", prefix, metricName.toString()); } - RequestTypeMetrics requestType(MetricName requestType) { - return requestTypeMetrics.computeIfAbsent(requestType, requestTypeMetricsCreator); - } - - RequestMetrics request() { - return requestMetrics; - } - - BidTypeMetrics forBidType(String bidType) { - return bidTypeMetrics.computeIfAbsent(bidType, bidTypeMetricsCreator); + AdapterTypeMetrics forAdapter(String adapterType) { + return adapterMetrics.computeIfAbsent(adapterType, adapterMetricsCreator); } } diff --git a/src/main/java/org/prebid/server/metric/AdapterTypeMetrics.java b/src/main/java/org/prebid/server/metric/AdapterTypeMetrics.java new file mode 100644 index 00000000000..5c4bf1ae1d2 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/AdapterTypeMetrics.java @@ -0,0 +1,82 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +/** + * AdapterType metrics support. + */ +class AdapterTypeMetrics extends UpdatableMetrics { + + private final Function requestTypeMetricsCreator; + private final Map requestTypeMetrics; + private final RequestMetrics requestMetrics; + private final Function bidTypeMetricsCreator; + private final Map bidTypeMetrics; + private final ResponseMetrics responseMetrics; + + AdapterTypeMetrics(MetricRegistry metricRegistry, CounterType counterType, String adapterType) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createAdapterPrefix(Objects.requireNonNull(adapterType)))); + + bidTypeMetricsCreator = bidType -> + new BidTypeMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType), bidType); + requestTypeMetricsCreator = requestType -> + new RequestTypeMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType), requestType); + requestTypeMetrics = new HashMap<>(); + requestMetrics = new RequestMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType)); + bidTypeMetrics = new HashMap<>(); + responseMetrics = new ResponseMetrics(metricRegistry, counterType, createAdapterPrefix(adapterType)); + } + + AdapterTypeMetrics(MetricRegistry metricRegistry, + CounterType counterType, + String accountAdapterPrefix, + String adapterType) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createAdapterPrefix(Objects.requireNonNull(accountAdapterPrefix), + Objects.requireNonNull(adapterType)))); + + requestMetrics = new RequestMetrics(metricRegistry, counterType, + createAdapterPrefix(accountAdapterPrefix, adapterType)); + + // not used for account.adapter.adapters metrics + requestTypeMetricsCreator = null; + requestTypeMetrics = null; + bidTypeMetricsCreator = null; + bidTypeMetrics = null; + responseMetrics = null; + } + + private static String createAdapterPrefix(String adapterType) { + return String.format("adapter.%s", adapterType); + } + + private static String createAdapterPrefix(String adapterPrefix, String adapterType) { + return String.format("%s.%s", adapterPrefix, adapterType); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + + RequestTypeMetrics requestType(MetricName requestType) { + return requestTypeMetrics.computeIfAbsent(requestType, requestTypeMetricsCreator); + } + + RequestMetrics request() { + return requestMetrics; + } + + BidTypeMetrics forBidType(String bidType) { + return bidTypeMetrics.computeIfAbsent(bidType, bidTypeMetricsCreator); + } + + ResponseMetrics response() { + return responseMetrics; + } +} diff --git a/src/main/java/org/prebid/server/metric/CurrencyRatesMetrics.java b/src/main/java/org/prebid/server/metric/CurrencyRatesMetrics.java new file mode 100644 index 00000000000..b85eba57d85 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/CurrencyRatesMetrics.java @@ -0,0 +1,42 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Circuit breaker metrics support. + */ +class CurrencyRatesMetrics extends UpdatableMetrics { + + private static final String SUFFIX = ".count"; + + CurrencyRatesMetrics(MetricRegistry metricRegistry, CounterType counterType) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), nameCreator()); + } + + private static Function nameCreator() { + return metricName -> String.format("currency-rates.%s%s", metricName.toString(), SUFFIX); + } + + @Override + void incCounter(MetricName metricName) { + throw new UnsupportedOperationException(); + } + + @Override + void incCounter(MetricName metricName, long value) { + throw new UnsupportedOperationException(); + } + + @Override + void updateTimer(MetricName metricName, long millis) { + throw new UnsupportedOperationException(); + } + + @Override + void updateHistogram(MetricName metricName, long value) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/prebid/server/metric/MetricName.java b/src/main/java/org/prebid/server/metric/MetricName.java index 0418dbde191..e1580af8958 100644 --- a/src/main/java/org/prebid/server/metric/MetricName.java +++ b/src/main/java/org/prebid/server/metric/MetricName.java @@ -24,8 +24,6 @@ public enum MetricName { requests, app_requests, no_cookie_requests, - safari_requests, - safari_no_cookie_requests, request_time, prices, imps_requested, @@ -62,6 +60,9 @@ public enum MetricName { err, networkerr, + // bids validation + warn, + // cookie sync cookie_sync_requests, opt_outs, @@ -101,7 +102,19 @@ public enum MetricName { creative_size, //account.*.requests. - rejected; + rejected, + + //currency rates + stale, + + // settings cache + stored_request("stored-request"), + amp_stored_request("amp-stored-request"), + account, + initialize, + update, + hit, + miss; private final String name; diff --git a/src/main/java/org/prebid/server/metric/Metrics.java b/src/main/java/org/prebid/server/metric/Metrics.java index 05d333fc31e..4eefaea1c80 100644 --- a/src/main/java/org/prebid/server/metric/Metrics.java +++ b/src/main/java/org/prebid/server/metric/Metrics.java @@ -29,15 +29,16 @@ public class Metrics extends UpdatableMetrics { private final Function requestMetricsCreator; private final Function accountMetricsCreator; - private final Function adapterMetricsCreator; + private final Function adapterMetricsCreator; private final Function bidderCardinalityMetricsCreator; private final Function circuitBreakerMetricsCreator; + private final Function settingsCacheMetricsCreator; // not thread-safe maps are intentionally used here because it's harmless in this particular case - eventually // this all boils down to metrics lookup by underlying metric registry and that operation is guaranteed to be // thread-safe private final Map requestMetrics; private final Map accountMetrics; - private final Map adapterMetrics; + private final Map adapterMetrics; private final Map bidderCardinailtyMetrics; private final UserSyncMetrics userSyncMetrics; private final CookieSyncMetrics cookieSyncMetrics; @@ -45,6 +46,8 @@ public class Metrics extends UpdatableMetrics { private final Map circuitBreakerMetrics; private final CacheMetrics cacheMetrics; private final TimeoutNotificationMetrics timeoutNotificationMetrics; + private final CurrencyRatesMetrics currencyRatesMetrics; + private final Map settingsCacheMetrics; public Metrics(MetricRegistry metricRegistry, CounterType counterType, AccountMetricsVerbosity accountMetricsVerbosity, BidderCatalog bidderCatalog) { @@ -55,10 +58,11 @@ public Metrics(MetricRegistry metricRegistry, CounterType counterType, AccountMe requestMetricsCreator = requestType -> new RequestStatusMetrics(metricRegistry, counterType, requestType); accountMetricsCreator = account -> new AccountMetrics(metricRegistry, counterType, account); - adapterMetricsCreator = adapterType -> new AdapterMetrics(metricRegistry, counterType, adapterType); + adapterMetricsCreator = adapterType -> new AdapterTypeMetrics(metricRegistry, counterType, adapterType); bidderCardinalityMetricsCreator = cardinality -> new BidderCardinalityMetrics( metricRegistry, counterType, cardinality); circuitBreakerMetricsCreator = type -> new CircuitBreakerMetrics(metricRegistry, counterType, type); + settingsCacheMetricsCreator = type -> new SettingsCacheMetrics(metricRegistry, counterType, type); requestMetrics = new EnumMap<>(MetricName.class); accountMetrics = new HashMap<>(); adapterMetrics = new HashMap<>(); @@ -69,6 +73,8 @@ public Metrics(MetricRegistry metricRegistry, CounterType counterType, AccountMe circuitBreakerMetrics = new HashMap<>(); cacheMetrics = new CacheMetrics(metricRegistry, counterType); timeoutNotificationMetrics = new TimeoutNotificationMetrics(metricRegistry, counterType); + currencyRatesMetrics = new CurrencyRatesMetrics(metricRegistry, counterType); + settingsCacheMetrics = new HashMap<>(); } RequestStatusMetrics forRequestType(MetricName requestType) { @@ -83,7 +89,7 @@ AccountMetrics forAccount(String account) { return accountMetrics.computeIfAbsent(account, accountMetricsCreator); } - AdapterMetrics forAdapter(String adapterType) { + AdapterTypeMetrics forAdapter(String adapterType) { return adapterMetrics.computeIfAbsent(adapterType, adapterMetricsCreator); } @@ -107,21 +113,19 @@ CacheMetrics cache() { return cacheMetrics; } - public void updateSafariRequestsMetric(boolean isSafari) { - if (isSafari) { - incCounter(MetricName.safari_requests); - } + CurrencyRatesMetrics currencyRates() { + return currencyRatesMetrics; + } + + SettingsCacheMetrics forSettingsCacheType(MetricName type) { + return settingsCacheMetrics.computeIfAbsent(type, settingsCacheMetricsCreator); } - public void updateAppAndNoCookieAndImpsRequestedMetrics(boolean isApp, boolean liveUidsPresent, boolean isSafari, - int numImps) { + public void updateAppAndNoCookieAndImpsRequestedMetrics(boolean isApp, boolean liveUidsPresent, int numImps) { if (isApp) { incCounter(MetricName.app_requests); } else if (!liveUidsPresent) { incCounter(MetricName.no_cookie_requests); - if (isSafari) { - incCounter(MetricName.safari_no_cookie_requests); - } } incCounter(MetricName.imps_requested, numImps); } @@ -207,22 +211,23 @@ public void updateAccountRequestRejectedMetrics(String accountId) { } public void updateAdapterRequestTypeAndNoCookieMetrics(String bidder, MetricName requestType, boolean noCookie) { - final AdapterMetrics adapterMetrics = forAdapter(resolveMetricsBidderName(bidder)); + final AdapterTypeMetrics adapterTypeMetrics = forAdapter(resolveMetricsBidderName(bidder)); - adapterMetrics.requestType(requestType).incCounter(MetricName.requests); + adapterTypeMetrics.requestType(requestType).incCounter(MetricName.requests); if (noCookie) { - adapterMetrics.incCounter(MetricName.no_cookie_requests); + adapterTypeMetrics.incCounter(MetricName.no_cookie_requests); } } public void updateAdapterResponseTime(String bidder, String accountId, int responseTime) { final String metricsBidderName = resolveMetricsBidderName(bidder); - final AdapterMetrics adapterMetrics = forAdapter(metricsBidderName); - adapterMetrics.updateTimer(MetricName.request_time, responseTime); + final AdapterTypeMetrics adapterTypeMetrics = forAdapter(metricsBidderName); + adapterTypeMetrics.updateTimer(MetricName.request_time, responseTime); if (accountMetricsVerbosity.forAccount(accountId).isAtLeast(AccountMetricsVerbosityLevel.detailed)) { - final AdapterMetrics accountAdapterMetrics = forAccount(accountId).forAdapter(metricsBidderName); + final AdapterTypeMetrics accountAdapterMetrics = + forAccount(accountId).adapter().forAdapter(metricsBidderName); accountAdapterMetrics.updateTimer(MetricName.request_time, responseTime); } } @@ -231,7 +236,7 @@ public void updateAdapterRequestNobidMetrics(String bidder, String accountId) { final String metricsBidderName = resolveMetricsBidderName(bidder); forAdapter(metricsBidderName).request().incCounter(MetricName.nobid); if (accountMetricsVerbosity.forAccount(accountId).isAtLeast(AccountMetricsVerbosityLevel.detailed)) { - forAccount(accountId).forAdapter(metricsBidderName).request().incCounter(MetricName.nobid); + forAccount(accountId).adapter().forAdapter(metricsBidderName).request().incCounter(MetricName.nobid); } } @@ -239,20 +244,21 @@ public void updateAdapterRequestGotbidsMetrics(String bidder, String accountId) final String metricsBidderName = resolveMetricsBidderName(bidder); forAdapter(metricsBidderName).request().incCounter(MetricName.gotbids); if (accountMetricsVerbosity.forAccount(accountId).isAtLeast(AccountMetricsVerbosityLevel.detailed)) { - forAccount(accountId).forAdapter(metricsBidderName).request().incCounter(MetricName.gotbids); + forAccount(accountId).adapter().forAdapter(metricsBidderName).request().incCounter(MetricName.gotbids); } } public void updateAdapterBidMetrics(String bidder, String accountId, long cpm, boolean isAdm, String bidType) { final String metricsBidderName = resolveMetricsBidderName(bidder); - final AdapterMetrics adapterMetrics = forAdapter(metricsBidderName); - adapterMetrics.updateHistogram(MetricName.prices, cpm); - adapterMetrics.incCounter(MetricName.bids_received); - adapterMetrics.forBidType(bidType) + final AdapterTypeMetrics adapterTypeMetrics = forAdapter(metricsBidderName); + adapterTypeMetrics.updateHistogram(MetricName.prices, cpm); + adapterTypeMetrics.incCounter(MetricName.bids_received); + adapterTypeMetrics.forBidType(bidType) .incCounter(isAdm ? MetricName.adm_bids_received : MetricName.nurl_bids_received); if (accountMetricsVerbosity.forAccount(accountId).isAtLeast(AccountMetricsVerbosityLevel.detailed)) { - final AdapterMetrics accountAdapterMetrics = forAccount(accountId).forAdapter(metricsBidderName); + final AdapterTypeMetrics accountAdapterMetrics = + forAccount(accountId).adapter().forAdapter(metricsBidderName); accountAdapterMetrics.updateHistogram(MetricName.prices, cpm); accountAdapterMetrics.incCounter(MetricName.bids_received); } @@ -262,6 +268,16 @@ public void updateAdapterRequestErrorMetric(String bidder, MetricName errorMetri forAdapter(resolveMetricsBidderName(bidder)).request().incCounter(errorMetric); } + public void updateSizeValidationMetrics(String bidder, String accountId, MetricName type) { + forAdapter(resolveMetricsBidderName(bidder)).response().validation().size().incCounter(type); + forAccount(accountId).response().validation().size().incCounter(type); + } + + public void updateSecureValidationMetrics(String bidder, String accountId, MetricName type) { + forAdapter(resolveMetricsBidderName(bidder)).response().validation().secure().incCounter(type); + forAccount(accountId).response().validation().secure().incCounter(type); + } + public void updateUserSyncOptoutMetric() { userSync().incCounter(MetricName.opt_outs); } @@ -296,25 +312,25 @@ public void updateCookieSyncTcfBlockedMetric(String bidder) { public void updateAuctionTcfMetrics(String bidder, MetricName requestType, - boolean useridRemoved, + boolean userIdRemoved, boolean geoMasked, - boolean requestBlocked, - boolean analyticsBlocked) { + boolean analyticsBlocked, + boolean requestBlocked) { final TcfMetrics tcf = forAdapter(resolveMetricsBidderName(bidder)).requestType(requestType).tcf(); - if (useridRemoved) { + if (userIdRemoved) { tcf.incCounter(MetricName.userid_removed); } if (geoMasked) { tcf.incCounter(MetricName.geo_masked); } - if (requestBlocked) { - tcf.incCounter(MetricName.request_blocked); - } if (analyticsBlocked) { tcf.incCounter(MetricName.analytics_blocked); } + if (requestBlocked) { + tcf.incCounter(MetricName.request_blocked); + } } public void updatePrivacyCoppaMetric() { @@ -342,6 +358,11 @@ public void updatePrivacyTcfInvalidMetric() { privacy().tcf().incCounter(MetricName.invalid); } + public void updatePrivacyTcfRequestsMetric(int version) { + final UpdatableMetrics versionMetrics = version == 2 ? privacy().tcf().v2() : privacy().tcf().v1(); + versionMetrics.incCounter(MetricName.requests); + } + public void updatePrivacyTcfGeoMetric(int version, Boolean inEea) { final UpdatableMetrics versionMetrics = version == 2 ? privacy().tcf().v2() : privacy().tcf().v1(); @@ -454,6 +475,22 @@ public void updateTimeoutNotificationMetric(boolean success) { } } + public void createCurrencyRatesGauge(BooleanSupplier stateSupplier) { + currencyRates().createGauge(MetricName.stale, () -> stateSupplier.getAsBoolean() ? 1 : 0); + } + + public void updateSettingsCacheRefreshTime(MetricName cacheType, MetricName refreshType, long timeElapsed) { + forSettingsCacheType(cacheType).forRefreshType(refreshType).updateTimer(MetricName.db_query_time, timeElapsed); + } + + public void updateSettingsCacheRefreshErrorMetric(MetricName cacheType, MetricName refreshType) { + forSettingsCacheType(cacheType).forRefreshType(refreshType).incCounter(MetricName.err); + } + + public void updateSettingsCacheEventMetric(MetricName cacheType, MetricName event) { + forSettingsCacheType(cacheType).incCounter(event); + } + private String resolveMetricsBidderName(String bidder) { return bidderCatalog.isValidName(bidder) ? bidder : METRICS_UNKNOWN_BIDDER; } diff --git a/src/main/java/org/prebid/server/metric/ResponseMetrics.java b/src/main/java/org/prebid/server/metric/ResponseMetrics.java new file mode 100644 index 00000000000..ba7e93fae81 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/ResponseMetrics.java @@ -0,0 +1,33 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Request metrics support. + */ +class ResponseMetrics extends UpdatableMetrics { + + private final ValidationMetrics validationMetrics; + + ResponseMetrics(MetricRegistry metricRegistry, CounterType counterType, String prefix) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createPrefix(Objects.requireNonNull(prefix)))); + + validationMetrics = new ValidationMetrics(metricRegistry, counterType, createPrefix(prefix)); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + + private static String createPrefix(String prefix) { + return String.format("%s.response", prefix); + } + + ValidationMetrics validation() { + return validationMetrics; + } +} diff --git a/src/main/java/org/prebid/server/metric/SettingsCacheMetrics.java b/src/main/java/org/prebid/server/metric/SettingsCacheMetrics.java new file mode 100644 index 00000000000..a78bf2223d0 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/SettingsCacheMetrics.java @@ -0,0 +1,58 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +/** + * Settings cache metrics support. + */ +class SettingsCacheMetrics extends UpdatableMetrics { + + private final Function refreshSettingsCacheMetricsCreator; + private final Map refreshSettingsCacheMetrics; + + SettingsCacheMetrics(MetricRegistry metricRegistry, CounterType counterType, MetricName type) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createPrefix(Objects.requireNonNull(type)))); + + refreshSettingsCacheMetricsCreator = refreshType -> + new RefreshSettingsCacheMetrics(metricRegistry, counterType, createPrefix(type), refreshType); + refreshSettingsCacheMetrics = new HashMap<>(); + } + + RefreshSettingsCacheMetrics forRefreshType(MetricName refreshType) { + return refreshSettingsCacheMetrics.computeIfAbsent(refreshType, refreshSettingsCacheMetricsCreator); + } + + private static String createPrefix(MetricName type) { + return String.format("settings.cache.%s", type.toString()); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + + static class RefreshSettingsCacheMetrics extends UpdatableMetrics { + + RefreshSettingsCacheMetrics(MetricRegistry metricRegistry, + CounterType counterType, + String prefix, + MetricName type) { + + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createPrefix(Objects.requireNonNull(prefix), Objects.requireNonNull(type)))); + } + + private static String createPrefix(String prefix, MetricName type) { + return String.format("%s.refresh.%s", prefix, type.toString()); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + } +} diff --git a/src/main/java/org/prebid/server/metric/SpecificValidationMetrics.java b/src/main/java/org/prebid/server/metric/SpecificValidationMetrics.java new file mode 100644 index 00000000000..4a1bc289860 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/SpecificValidationMetrics.java @@ -0,0 +1,27 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Request metrics support. + */ +class SpecificValidationMetrics extends UpdatableMetrics { + + SpecificValidationMetrics( + MetricRegistry metricRegistry, CounterType counterType, String prefix, String validation) { + + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createPrefix(Objects.requireNonNull(prefix), Objects.requireNonNull(validation)))); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + + private static String createPrefix(String prefix, String validation) { + return String.format("%s.%s", prefix, validation); + } +} diff --git a/src/main/java/org/prebid/server/metric/ValidationMetrics.java b/src/main/java/org/prebid/server/metric/ValidationMetrics.java new file mode 100644 index 00000000000..a00ebea15d2 --- /dev/null +++ b/src/main/java/org/prebid/server/metric/ValidationMetrics.java @@ -0,0 +1,41 @@ +package org.prebid.server.metric; + +import com.codahale.metrics.MetricRegistry; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Request metrics support. + */ +class ValidationMetrics extends UpdatableMetrics { + + private final SpecificValidationMetrics sizeValidationMetrics; + private final SpecificValidationMetrics secureValidationMetrics; + + ValidationMetrics(MetricRegistry metricRegistry, CounterType counterType, String prefix) { + super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType), + nameCreator(createPrefix(Objects.requireNonNull(prefix)))); + + sizeValidationMetrics = new SpecificValidationMetrics( + metricRegistry, counterType, createPrefix(prefix), "size"); + secureValidationMetrics = new SpecificValidationMetrics( + metricRegistry, counterType, createPrefix(prefix), "secure"); + } + + private static Function nameCreator(String prefix) { + return metricName -> String.format("%s.%s", prefix, metricName.toString()); + } + + private static String createPrefix(String prefix) { + return String.format("%s.validation", prefix); + } + + SpecificValidationMetrics size() { + return sizeValidationMetrics; + } + + SpecificValidationMetrics secure() { + return secureValidationMetrics; + } +} diff --git a/src/main/java/org/prebid/server/privacy/PrivacyExtractor.java b/src/main/java/org/prebid/server/privacy/PrivacyExtractor.java index cd11b584461..0ccc6b6d385 100644 --- a/src/main/java/org/prebid/server/privacy/PrivacyExtractor.java +++ b/src/main/java/org/prebid/server/privacy/PrivacyExtractor.java @@ -15,6 +15,8 @@ import org.prebid.server.proto.request.CookieSyncRequest; import org.prebid.server.proto.request.PreBidRequest; +import java.util.List; + /** * GDPR-aware utilities */ @@ -40,12 +42,13 @@ public class PrivacyExtractor { *

* And construct {@link Privacy} from them. Use default values in case of invalid value. */ - public Privacy validPrivacyFrom(BidRequest bidRequest) { - return extractPrivacy(bidRequest.getRegs(), bidRequest.getUser()); + public Privacy validPrivacyFrom(BidRequest bidRequest, List errors) { + return extractPrivacy(bidRequest.getRegs(), bidRequest.getUser(), errors); } + @Deprecated public Privacy validPrivacyFrom(PreBidRequest preBidRequest) { - return extractPrivacy(preBidRequest.getRegs(), preBidRequest.getUser()); + return extractPrivacy(preBidRequest.getRegs(), preBidRequest.getUser(), null); } public Privacy validPrivacyFrom(CookieSyncRequest request) { @@ -54,17 +57,17 @@ public Privacy validPrivacyFrom(CookieSyncRequest request) { final String gdprConsent = request.getGdprConsent(); final String usPrivacy = request.getUsPrivacy(); - return toValidPrivacy(gdpr, gdprConsent, usPrivacy, null); + return toValidPrivacy(gdpr, gdprConsent, usPrivacy, null, null); } public Privacy validPrivacyFromSetuidRequest(HttpServerRequest request) { final String gdpr = request.getParam(SETUID_GDPR_PARAM); final String gdprConsent = request.getParam(SETUID_GDPR_CONSENT_PARAM); - return toValidPrivacy(gdpr, gdprConsent, null, null); + return toValidPrivacy(gdpr, gdprConsent, null, null, null); } - private Privacy extractPrivacy(Regs regs, User user) { + private Privacy extractPrivacy(Regs regs, User user, List errors) { final ExtRegs extRegs = regs != null ? regs.getExt() : null; final ExtUser extUser = user != null ? user.getExt() : null; @@ -74,27 +77,34 @@ private Privacy extractPrivacy(Regs regs, User user) { final String usPrivacy = extRegs != null ? extRegs.getUsPrivacy() : null; final Integer coppa = regs != null ? regs.getCoppa() : null; - return toValidPrivacy(gdpr, consent, usPrivacy, coppa); + return toValidPrivacy(gdpr, consent, usPrivacy, coppa, errors); } - private static Privacy toValidPrivacy(String gdpr, String consent, String usPrivacy, Integer coppa) { + private static Privacy toValidPrivacy(String gdpr, + String consent, + String usPrivacy, + Integer coppa, + List errors) { final String validGdpr = ObjectUtils.notEqual(gdpr, "1") && ObjectUtils.notEqual(gdpr, "0") ? DEFAULT_GDPR_VALUE : gdpr; final String validConsent = consent == null ? DEFAULT_CONSENT_VALUE : consent; - final Ccpa validCcpa = usPrivacy == null ? DEFAULT_CCPA_VALUE : toValidCcpa(usPrivacy); + final Ccpa validCcpa = usPrivacy == null ? DEFAULT_CCPA_VALUE : toValidCcpa(usPrivacy, errors); final Integer validCoppa = coppa == null ? DEFAULT_COPPA_VALUE : coppa; return Privacy.of(validGdpr, validConsent, validCcpa, validCoppa); } - private static Ccpa toValidCcpa(String usPrivacy) { + private static Ccpa toValidCcpa(String usPrivacy, List errors) { try { Ccpa.validateUsPrivacy(usPrivacy); return Ccpa.of(usPrivacy); } catch (PreBidException e) { - // TODO add error to PBS response, not only in logs (See PR #758) - logger.debug("CCPA consent {0} has invalid format: {1}", usPrivacy, e.getMessage()); + final String message = String.format("CCPA consent %s has invalid format: %s", usPrivacy, e.getMessage()); + logger.debug(message); + if (errors != null) { + errors.add(message); + } return DEFAULT_CCPA_VALUE; } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java index 55b4943ce93..05b4dab71a2 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -398,6 +398,8 @@ private TCString parseConsentString(String consentString, RequestLogInfo request return TCStringEmpty.create(); } + final int version = tcString.getVersion(); + metrics.updatePrivacyTcfRequestsMetric(version); return tcString; } @@ -450,7 +452,7 @@ private static String logMessage(String consent, String type, RequestLogInfo req consent, type, requestLogInfo.getAccountId(), requestLogInfo.getRefUrl(), message); } - public static boolean isConsentValid(TCString consent) { + private static boolean isConsentValid(TCString consent) { return consent != null && !(consent instanceof TCStringEmpty); } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java b/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java index 4da89cc9a48..a5af3d00715 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java @@ -7,7 +7,7 @@ @Data public class PrivacyEnforcementAction { - boolean removeUserIds; // user.buyeruid, user.id, user.ext.eids, user.ext.digitrust + boolean removeUserIds; // user.buyeruid, user.id, user.ext.eids boolean maskGeo; // user.geo, device.geo diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java index be414332f58..17d0c890b9f 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java @@ -42,7 +42,7 @@ public PurposeStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, /** * This method represents allowance of permission that purpose should provide after full enforcement - * (can downgrade to basic if GCL failed) despite of host company or account configuration. + * (can downgrade to basic if GVL failed) despite of host company or account configuration. */ public abstract void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction); @@ -52,13 +52,18 @@ public Collection processTypePurposeStrategy( Collection vendorPermissions, boolean wasDowngraded) { - allowedByTypeStrategy(vendorConsent, purpose, vendorPermissions).stream() + final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); + final Collection vendorForPurpose = vendorPermissions.stream() + .filter(vendorPermission -> !excludedVendors.contains(vendorPermission)) + .collect(Collectors.toList()); + + allowedByTypeStrategy(vendorConsent, purpose, vendorForPurpose, excludedVendors).stream() .map(VendorPermission::getPrivacyEnforcementAction) .forEach(this::allow); final Collection naturalVendorPermission = wasDowngraded - ? allowedByBasicTypeStrategy(vendorConsent, true, vendorPermissions, Collections.emptyList()) - : allowedByFullTypeStrategy(vendorConsent, true, vendorPermissions, Collections.emptyList()); + ? allowedByBasicTypeStrategy(vendorConsent, true, vendorForPurpose, excludedVendors) + : allowedByFullTypeStrategy(vendorConsent, true, vendorForPurpose, excludedVendors); naturalVendorPermission.stream() .map(VendorPermission::getPrivacyEnforcementAction) @@ -71,12 +76,8 @@ public Collection processTypePurposeStrategy( private Collection allowedByTypeStrategy(TCString vendorConsent, Purpose purpose, - Collection vendorPermissions) { - - final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); - final Collection vendorForPurpose = vendorPermissions.stream() - .filter(vendorPermission -> !excludedVendors.contains(vendorPermission)) - .collect(Collectors.toList()); + Collection vendorForPurpose, + Collection excludedVendors) { final boolean isEnforceVendors = BooleanUtils.isNotFalse(purpose.getEnforceVendors()); final EnforcePurpose purposeType = purpose.getEnforcePurpose(); @@ -105,7 +106,7 @@ protected Collection excludedVendors(Collection - bidderNameExceptions.contains(vendorPermission.getVendorPermission().getBidderName())); + bidderNameExceptions.contains(vendorPermission.getVendorPermission().getBidderName())); } protected Collection allowedByBasicTypeStrategy( diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestTargeting.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestTargeting.java index 0099e1d9d94..b2eac627e62 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestTargeting.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestTargeting.java @@ -44,6 +44,11 @@ public class ExtRequestTargeting { */ Boolean includebidderkeys; + /** + * Defines the contract for bidrequest.ext.prebid.targeting.includeformat + */ + Boolean includeformat; + /** * Defines the contract for bidrequest.ext.prebid.targeting.truncateattrchars */ diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java index abb3da58692..c0666dcb14b 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java @@ -30,14 +30,6 @@ public class ExtUser extends FlexibleExtension { */ String consent; - /** - * DigiTrust breaks the typical Prebid Server convention of namespacing "global" options inside "ext.prebid.*" - * to match the recommendation from the broader digitrust community. - *

- * For more info, see: https://github.com/digi-trust/dt-cdn/wiki/OpenRTB-extension#openrtb-2x - */ - ExtUserDigiTrust digitrust; - /** * Standardized User IDs. */ diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserDigiTrust.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserDigiTrust.java deleted file mode 100644 index 1a1c07cc3b7..00000000000 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserDigiTrust.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.prebid.server.proto.openrtb.ext.request; - -import lombok.AllArgsConstructor; -import lombok.Value; - -/** - * Defines the contract for bidrequest.user.ext.digitrust - * More info on DigiTrust can be found here: https://github.com/digi-trust/dt-cdn/wiki/Integration-Guide - */ -@AllArgsConstructor(staticName = "of") -@Value -public class ExtUserDigiTrust { - - /** - * Unique device identifier - */ - String id; - - /** - * Key version used to encrypt id - */ - Integer keyv; - - /** - * User optout preference - */ - Integer pref; -} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/adoppler/ExtImpAdoppler.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/adoppler/ExtImpAdoppler.java index ac28d2c7bdf..b839645945b 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/adoppler/ExtImpAdoppler.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/adoppler/ExtImpAdoppler.java @@ -8,4 +8,5 @@ public class ExtImpAdoppler { String adunit; + String client; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/amx/ExtImpAmx.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/amx/ExtImpAmx.java new file mode 100644 index 00000000000..1ad50749816 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/amx/ExtImpAmx.java @@ -0,0 +1,16 @@ +package org.prebid.server.proto.openrtb.ext.request.amx; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +@AllArgsConstructor(staticName = "of") +@Value +public class ExtImpAmx { + + @JsonProperty("tagId") + String tagId; + + @JsonProperty("adUnitId") + String adUnitId; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/deepintent/ExtImpDeepintent.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/deepintent/ExtImpDeepintent.java index c313b9dd2e1..235c5e6c7f5 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/deepintent/ExtImpDeepintent.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/deepintent/ExtImpDeepintent.java @@ -8,6 +8,6 @@ @Value public class ExtImpDeepintent { - @JsonProperty("TagID") + @JsonProperty("tagId") String tagId; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/dmx/ExtImpDmx.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/dmx/ExtImpDmx.java index f056c075814..aadd640dea1 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/dmx/ExtImpDmx.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/dmx/ExtImpDmx.java @@ -20,9 +20,7 @@ public class ExtImpDmx { @JsonProperty("memberid") String memberId; - @JsonProperty("publisher_id") String publisherId; - @JsonProperty("seller_id") String sellerId; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java index 490dbd2bea9..e7a8faee424 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java @@ -4,10 +4,14 @@ import lombok.AllArgsConstructor; import lombok.Value; +import java.util.List; + @AllArgsConstructor(staticName = "of") @Value public class ExtImpIx { @JsonProperty("siteId") String siteId; + + List size; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/nobid/ExtImpNobid.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/nobid/ExtImpNobid.java new file mode 100644 index 00000000000..e2a25fc93b3 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/nobid/ExtImpNobid.java @@ -0,0 +1,16 @@ +package org.prebid.server.proto.openrtb.ext.request.nobid; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +@AllArgsConstructor(staticName = "of") +@Value +public class ExtImpNobid { + + @JsonProperty("siteId") + Integer siteId; + + @JsonProperty("placementId") + Integer placementId; +} 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/proto/openrtb/ext/request/silvermob/ExtImpSilvermob.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/silvermob/ExtImpSilvermob.java new file mode 100644 index 00000000000..53585f07445 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/silvermob/ExtImpSilvermob.java @@ -0,0 +1,18 @@ +package org.prebid.server.proto.openrtb.ext.request.silvermob; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +/** + * Defines the contract for bidRequest.imp[i].ext.silvermob + */ +@AllArgsConstructor(staticName = "of") +@Value +public class ExtImpSilvermob { + + @JsonProperty("zoneid") + String zoneId; + + String host; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/BidType.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/BidType.java index 161ec51a73a..f1143141538 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/response/BidType.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/BidType.java @@ -8,5 +8,9 @@ public enum BidType { video, audio, @JsonProperty("native") - xNative + xNative; + + public String getName() { + return this == xNative ? "native" : this.name(); + } } diff --git a/src/main/java/org/prebid/server/proto/response/BidderInfo.java b/src/main/java/org/prebid/server/proto/response/BidderInfo.java index 7e347265668..c1dcbc7ba12 100644 --- a/src/main/java/org/prebid/server/proto/response/BidderInfo.java +++ b/src/main/java/org/prebid/server/proto/response/BidderInfo.java @@ -65,8 +65,8 @@ public static class GdprInfo { /** * GDPR Vendor ID in the IAB Global Vendor List which refers to this Bidder. *

- * The Global Vendor list can be found here: https://vendorlist.consensu.org/vendorlist.json - * Bidders can register for the list here: https://register.consensu.org/ + * The Global Vendor list can be found at https://iabeurope.eu/ + * Bidders can be registered to the list at https://register.consensu.org/ *

* If you're not on the list, this should return 0. If cookie sync requests have GDPR consent info, * or the Prebid Server host company configures its deploy to be "cautious" when no GDPR info exists diff --git a/src/main/java/org/prebid/server/settings/CachingApplicationSettings.java b/src/main/java/org/prebid/server/settings/CachingApplicationSettings.java index 229faa31c1c..a4817568ce5 100644 --- a/src/main/java/org/prebid/server/settings/CachingApplicationSettings.java +++ b/src/main/java/org/prebid/server/settings/CachingApplicationSettings.java @@ -6,6 +6,8 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.Timeout; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.helper.StoredDataFetcher; import org.prebid.server.settings.helper.StoredItemResolver; import org.prebid.server.settings.model.Account; @@ -20,6 +22,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.BiFunction; +import java.util.function.Consumer; /** * Adds caching functionality for {@link ApplicationSettings} implementation. @@ -36,9 +39,16 @@ public class CachingApplicationSettings implements ApplicationSettings { private final SettingsCache cache; private final SettingsCache ampCache; private final SettingsCache videoCache; + private final Metrics metrics; + + public CachingApplicationSettings(ApplicationSettings delegate, + SettingsCache cache, + SettingsCache ampCache, + SettingsCache videoCache, + Metrics metrics, + int ttl, + int size) { - public CachingApplicationSettings(ApplicationSettings delegate, SettingsCache cache, SettingsCache ampCache, - SettingsCache videoCache, int ttl, int size) { if (ttl <= 0 || size <= 0) { throw new IllegalArgumentException("ttl and size must be positive"); } @@ -49,6 +59,7 @@ public CachingApplicationSettings(ApplicationSettings delegate, SettingsCache ca this.cache = Objects.requireNonNull(cache); this.ampCache = Objects.requireNonNull(ampCache); this.videoCache = Objects.requireNonNull(videoCache); + this.metrics = Objects.requireNonNull(metrics); } /** @@ -56,7 +67,13 @@ public CachingApplicationSettings(ApplicationSettings delegate, SettingsCache ca */ @Override public Future getAccountById(String accountId, Timeout timeout) { - return getFromCacheOrDelegate(accountCache, accountToErrorCache, accountId, timeout, delegate::getAccountById); + return getFromCacheOrDelegate( + accountCache, + accountToErrorCache, + accountId, + timeout, + delegate::getAccountById, + event -> metrics.updateSettingsCacheEventMetric(MetricName.account, event)); } /** @@ -64,16 +81,24 @@ public Future getAccountById(String accountId, Timeout timeout) { */ @Override public Future getAdUnitConfigById(String adUnitConfigId, Timeout timeout) { - return getFromCacheOrDelegate(adUnitConfigCache, accountToErrorCache, adUnitConfigId, timeout, - delegate::getAdUnitConfigById); + return getFromCacheOrDelegate( + adUnitConfigCache, + accountToErrorCache, + adUnitConfigId, + timeout, + delegate::getAdUnitConfigById, + CachingApplicationSettings::noOp); } /** * Retrieves stored data from cache or delegates it to original fetcher. */ @Override - public Future getStoredData(String accountId, Set requestIds, Set impIds, + public Future getStoredData(String accountId, + Set requestIds, + Set impIds, Timeout timeout) { + return getFromCacheOrDelegate(cache, accountId, requestIds, impIds, timeout, delegate::getStoredData); } @@ -81,14 +106,20 @@ public Future getStoredData(String accountId, Set requ * Retrieves amp stored data from cache or delegates it to original fetcher. */ @Override - public Future getAmpStoredData(String accountId, Set requestIds, Set impIds, + public Future getAmpStoredData(String accountId, + Set requestIds, + Set impIds, Timeout timeout) { + return getFromCacheOrDelegate(ampCache, accountId, requestIds, impIds, timeout, delegate::getAmpStoredData); } @Override - public Future getVideoStoredData(String accountId, Set requestIds, Set impIds, + public Future getVideoStoredData(String accountId, + Set requestIds, + Set impIds, Timeout timeout) { + return getFromCacheOrDelegate(videoCache, accountId, requestIds, impIds, timeout, delegate::getVideoStoredData); } @@ -100,15 +131,22 @@ public Future getStoredResponses(Set responseI return delegate.getStoredResponses(responseIds, timeout); } - private static Future getFromCacheOrDelegate(Map cache, Map accountToErrorCache, - String key, Timeout timeout, - BiFunction> retriever) { + private static Future getFromCacheOrDelegate(Map cache, + Map accountToErrorCache, + String key, + Timeout timeout, + BiFunction> retriever, + Consumer metricUpdater) { final T cachedValue = cache.get(key); if (cachedValue != null) { + metricUpdater.accept(MetricName.hit); + return Future.succeededFuture(cachedValue); } + metricUpdater.accept(MetricName.miss); + final String preBidExceptionMessage = accountToErrorCache.get(key); if (preBidExceptionMessage != null) { return Future.failedFuture(new PreBidException(preBidExceptionMessage)); @@ -129,7 +167,11 @@ private static Future getFromCacheOrDelegate(Map cache, Map getFromCacheOrDelegate( - SettingsCache cache, String accountId, Set requestIds, Set impIds, Timeout timeout, + SettingsCache cache, + String accountId, + Set requestIds, + Set impIds, + Timeout timeout, StoredDataFetcher, Set, Timeout, Future> retriever) { // empty string account ID doesn't make sense @@ -170,11 +212,14 @@ private static Future getFromCacheOrDelegate( }); } - private static Future cacheAndReturnFailedFuture(Throwable throwable, String key, + private static Future cacheAndReturnFailedFuture(Throwable throwable, + String key, Map cache) { + if (throwable instanceof PreBidException) { cache.put(key, throwable.getMessage()); } + return Future.failedFuture(throwable); } @@ -182,7 +227,9 @@ private static Map getFromCacheOrAddMissedIds(String accountId, Set ids, Map> cache, Set missedIds) { + final Map idToStoredItem = new HashMap<>(ids.size()); + for (String id : ids) { try { final StoredItem resolvedStoredItem = StoredItemResolver.resolve(null, accountId, id, cache.get(id)); @@ -191,6 +238,7 @@ private static Map getFromCacheOrAddMissedIds(String accountId, missedIds.add(id); } } + return idToStoredItem; } @@ -198,4 +246,7 @@ public void invalidateAccountCache(String accountId) { accountCache.remove(accountId); logger.debug("Account with id {0} was invalidated", accountId); } + + private static void noOp(ANY any) { + } } diff --git a/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java b/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java new file mode 100644 index 00000000000..0a5df0756cb --- /dev/null +++ b/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java @@ -0,0 +1,71 @@ +package org.prebid.server.settings; + +import io.vertx.core.Future; +import org.prebid.server.execution.Timeout; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.StoredDataResult; +import org.prebid.server.settings.model.StoredResponseDataResult; + +import java.util.Objects; +import java.util.Set; + +public class EnrichingApplicationSettings implements ApplicationSettings { + + private final ApplicationSettings delegate; + private final Account defaultAccount; + + public EnrichingApplicationSettings(ApplicationSettings delegate, Account defaultAccount) { + this.delegate = Objects.requireNonNull(delegate); + this.defaultAccount = Objects.equals(Account.builder().build(), defaultAccount) ? null : defaultAccount; + } + + @Override + public Future getAccountById(String accountId, Timeout timeout) { + final Future accountFuture = delegate.getAccountById(accountId, timeout); + + if (defaultAccount == null) { + return accountFuture; + } + + return accountFuture + .map(account -> account.merge(defaultAccount)) + .otherwise(Account.empty(accountId).merge(defaultAccount)); + } + + @Override + public Future getAdUnitConfigById(String adUnitConfigId, Timeout timeout) { + return delegate.getAdUnitConfigById(adUnitConfigId, timeout); + } + + @Override + public Future getStoredData(String accountId, + Set requestIds, + Set impIds, + Timeout timeout) { + + return delegate.getStoredData(accountId, requestIds, impIds, timeout); + } + + @Override + public Future getStoredResponses(Set responseIds, Timeout timeout) { + return delegate.getStoredResponses(responseIds, timeout); + } + + @Override + public Future getAmpStoredData(String accountId, + Set requestIds, + Set impIds, + Timeout timeout) { + + return delegate.getAmpStoredData(accountId, requestIds, impIds, timeout); + } + + @Override + public Future getVideoStoredData(String accountId, + Set requestIds, + Set impIds, + Timeout timeout) { + + return delegate.getVideoStoredData(accountId, requestIds, impIds, timeout); + } +} diff --git a/src/main/java/org/prebid/server/settings/HttpApplicationSettings.java b/src/main/java/org/prebid/server/settings/HttpApplicationSettings.java index b89a181ede1..23d0d870b0b 100644 --- a/src/main/java/org/prebid/server/settings/HttpApplicationSettings.java +++ b/src/main/java/org/prebid/server/settings/HttpApplicationSettings.java @@ -2,10 +2,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.Timeout; import org.prebid.server.json.DecodeException; @@ -14,6 +16,7 @@ import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.StoredDataType; import org.prebid.server.settings.model.StoredResponseDataResult; +import org.prebid.server.settings.proto.response.HttpAccountsResponse; import org.prebid.server.settings.proto.response.HttpFetcherResponse; import org.prebid.server.util.HttpUtil; import org.prebid.server.vertx.http.HttpClient; @@ -59,7 +62,6 @@ */ public class HttpApplicationSettings implements ApplicationSettings { - private static final String NOT_SUPPORTED = "Not supported"; private static final Logger logger = LoggerFactory.getLogger(HttpApplicationSettings.class); private String endpoint; @@ -77,12 +79,62 @@ public HttpApplicationSettings(HttpClient httpClient, JacksonMapper mapper, Stri this.videoEndpoint = HttpUtil.validateUrl(Objects.requireNonNull(videoEndpoint)); } - /** - * Not supported and returns failed result. - */ @Override public Future getAccountById(String accountId, Timeout timeout) { - return Future.failedFuture(new PreBidException("Not supported")); + + return fetchAccountsByIds(Collections.singleton(accountId), timeout) + .map(accounts -> accounts.stream() + .findFirst() + .orElseThrow(() -> + new PreBidException(String.format("Account with id : %s not found", accountId)))); + } + + private Future> fetchAccountsByIds(Set accountIds, Timeout timeout) { + if (CollectionUtils.isEmpty(accountIds)) { + return Future.succeededFuture(Collections.emptySet()); + } + final long remainingTimeout = timeout.remaining(); + if (timeout.remaining() <= 0) { + return Future.failedFuture(new TimeoutException("Timeout has been exceeded")); + } + + return httpClient.get(accountsRequestUrlFrom(endpoint, accountIds), HttpUtil.headers(), remainingTimeout) + .compose(response -> processAccountsResponse(response, accountIds)) + .recover(Future::failedFuture); + } + + private static String accountsRequestUrlFrom(String endpoint, Set accountIds) { + final StringBuilder url = new StringBuilder(endpoint); + url.append(endpoint.contains("?") ? "&" : "?"); + + if (!accountIds.isEmpty()) { + url.append("account-ids=[\"").append(joinIds(accountIds)).append("\"]"); + } + + return url.toString(); + } + + private Future> processAccountsResponse(HttpClientResponse response, Set accountIds) { + return Future.succeededFuture( + toAccountsResult(response.getStatusCode(), response.getBody(), accountIds)); + } + + private Set toAccountsResult(int statusCode, String body, Set accountIds) { + if (statusCode != HttpResponseStatus.OK.code()) { + throw new PreBidException(String.format("Error fetching accounts %s via http: " + + "unexpected response status %d", accountIds, statusCode)); + } + + final HttpAccountsResponse response; + try { + response = mapper.decodeValue(body, HttpAccountsResponse.class); + } catch (DecodeException e) { + throw new PreBidException(String.format("Error fetching accounts %s " + + "via http: failed to parse response: %s", accountIds, e.getMessage())); + } + final Map accounts = response.getAccounts(); + + return MapUtils.isNotEmpty(accounts) ? new HashSet<>(accounts.values()) : Collections.emptySet(); } /** @@ -139,15 +191,15 @@ private Future fetchStoredData(String endpoint, Set re final long remainingTimeout = timeout.remaining(); if (remainingTimeout <= 0) { - return failResponse(new TimeoutException("Timeout has been exceeded"), requestIds, impIds); + return failStoredDataResponse(new TimeoutException("Timeout has been exceeded"), requestIds, impIds); } - return httpClient.get(urlFrom(endpoint, requestIds, impIds), HttpUtil.headers(), remainingTimeout) - .compose(response -> processResponse(response, requestIds, impIds)) - .recover(exception -> failResponse(exception, requestIds, impIds)); + return httpClient.get(storeRequestUrlFrom(endpoint, requestIds, impIds), HttpUtil.headers(), remainingTimeout) + .compose(response -> processStoredDataResponse(response, requestIds, impIds)) + .recover(exception -> failStoredDataResponse(exception, requestIds, impIds)); } - private static String urlFrom(String endpoint, Set requestIds, Set impIds) { + private static String storeRequestUrlFrom(String endpoint, Set requestIds, Set impIds) { final StringBuilder url = new StringBuilder(endpoint); url.append(endpoint.contains("?") ? "&" : "?"); @@ -169,14 +221,14 @@ private static String joinIds(Set ids) { return String.join("\",\"", ids); } - private static Future failResponse(Throwable throwable, Set requestIds, - Set impIds) { + private static Future failStoredDataResponse(Throwable throwable, Set requestIds, + Set impIds) { return Future.succeededFuture( toFailedStoredDataResult(requestIds, impIds, throwable.getMessage())); } - private Future processResponse(HttpClientResponse response, Set requestIds, - Set impIds) { + private Future processStoredDataResponse(HttpClientResponse response, Set requestIds, + Set impIds) { return Future.succeededFuture( toStoredDataResult(requestIds, impIds, response.getStatusCode(), response.getBody())); } @@ -197,7 +249,7 @@ private static StoredDataResult toFailedStoredDataResult(Set requestIds, private StoredDataResult toStoredDataResult(Set requestIds, Set impIds, int statusCode, String body) { - if (statusCode != 200) { + if (statusCode != HttpResponseStatus.OK.code()) { return toFailedStoredDataResult(requestIds, impIds, "HTTP status code %d", statusCode); } diff --git a/src/main/java/org/prebid/server/settings/JdbcApplicationSettings.java b/src/main/java/org/prebid/server/settings/JdbcApplicationSettings.java index 39f1e3c75b3..952d11e24a7 100644 --- a/src/main/java/org/prebid/server/settings/JdbcApplicationSettings.java +++ b/src/main/java/org/prebid/server/settings/JdbcApplicationSettings.java @@ -13,7 +13,9 @@ import org.prebid.server.settings.helper.JdbcStoredResponseResultMapper; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountAnalyticsConfig; +import org.prebid.server.settings.model.AccountBidValidationConfig; import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountStatus; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.StoredResponseDataResult; import org.prebid.server.vertx.jdbc.JdbcClient; @@ -37,13 +39,20 @@ */ public class JdbcApplicationSettings implements ApplicationSettings { + private static final String ACCOUNT_ID_PLACEHOLDER = "%ACCOUNT_ID%"; private static final String REQUEST_ID_PLACEHOLDER = "%REQUEST_ID_LIST%"; private static final String IMP_ID_PLACEHOLDER = "%IMP_ID_LIST%"; private static final String RESPONSE_ID_PLACEHOLDER = "%RESPONSE_ID_LIST%"; + private static final String QUERY_PARAM_PLACEHOLDER = "?"; private final JdbcClient jdbcClient; private final JacksonMapper mapper; + /** + * Query to select account by ids. + */ + private final String selectAccountQuery; + /** * Query to select stored requests and imps by ids, for example: *

@@ -56,7 +65,7 @@ public class JdbcApplicationSettings implements ApplicationSettings {
      *   WHERE impid in (%IMP_ID_LIST%)
      * 
*/ - private final String selectQuery; + private final String selectStoredRequestsQuery; /** * Query to select amp stored requests by ids, for example: @@ -66,7 +75,7 @@ public class JdbcApplicationSettings implements ApplicationSettings { * WHERE reqid in (%REQUEST_ID_LIST%) * */ - private final String selectAmpQuery; + private final String selectAmpStoredRequestsQuery; /** * Query to select stored responses by ids, for example: @@ -76,19 +85,22 @@ public class JdbcApplicationSettings implements ApplicationSettings { * WHERE respid in (%RESPONSE_ID_LIST%) * */ - private final String selectResponseQuery; + private final String selectStoredResponsesQuery; public JdbcApplicationSettings(JdbcClient jdbcClient, JacksonMapper mapper, - String selectQuery, - String selectAmpQuery, - String selectResponseQuery) { + String selectAccountQuery, + String selectStoredRequestsQuery, + String selectAmpStoredRequestsQuery, + String selectStoredResponsesQuery) { this.jdbcClient = Objects.requireNonNull(jdbcClient); this.mapper = Objects.requireNonNull(mapper); - this.selectQuery = Objects.requireNonNull(selectQuery); - this.selectAmpQuery = Objects.requireNonNull(selectAmpQuery); - this.selectResponseQuery = Objects.requireNonNull(selectResponseQuery); + this.selectAccountQuery = Objects.requireNonNull(selectAccountQuery) + .replace(ACCOUNT_ID_PLACEHOLDER, QUERY_PARAM_PLACEHOLDER); + this.selectStoredRequestsQuery = Objects.requireNonNull(selectStoredRequestsQuery); + this.selectAmpStoredRequestsQuery = Objects.requireNonNull(selectAmpStoredRequestsQuery); + this.selectStoredResponsesQuery = Objects.requireNonNull(selectStoredResponsesQuery); } /** @@ -97,9 +109,8 @@ public JdbcApplicationSettings(JdbcClient jdbcClient, */ @Override public Future getAccountById(String accountId, Timeout timeout) { - return jdbcClient.executeQuery("SELECT uuid, price_granularity, banner_cache_ttl, video_cache_ttl," - + " events_enabled, enforce_ccpa, tcf_config, analytics_sampling_factor, truncate_target_attr," - + " default_integration, analytics_config FROM accounts_account where uuid = ? LIMIT 1", + return jdbcClient.executeQuery( + selectAccountQuery, Collections.singletonList(accountId), result -> mapToModelOrError(result, row -> Account.builder() .id(row.getString(0)) @@ -113,6 +124,8 @@ public Future getAccountById(String accountId, Timeout timeout) { .truncateTargetAttr(row.getInteger(8)) .defaultIntegration(row.getString(9)) .analyticsConfig(toModel(row.getString(10), AccountAnalyticsConfig.class)) + .bidValidations(toModel(row.getString(11), AccountBidValidationConfig.class)) + .status(toAccountStatus(row.getString(12))) .build()), timeout) .compose(result -> failedIfNull(result, accountId, "Account")); @@ -161,6 +174,17 @@ private T toModel(String source, Class targetClass) { } } + private static AccountStatus toAccountStatus(String status) { + if (status == null) { + return null; + } + try { + return AccountStatus.valueOf(status); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + /** * Runs a process to get stored requests by a collection of ids from database * and returns {@link Future<{@link StoredDataResult }>}. @@ -168,7 +192,7 @@ private T toModel(String source, Class targetClass) { @Override public Future getStoredData(String accountId, Set requestIds, Set impIds, Timeout timeout) { - return fetchStoredData(selectQuery, accountId, requestIds, impIds, timeout); + return fetchStoredData(selectStoredRequestsQuery, accountId, requestIds, impIds, timeout); } /** @@ -178,7 +202,7 @@ public Future getStoredData(String accountId, Set requ @Override public Future getAmpStoredData(String accountId, Set requestIds, Set impIds, Timeout timeout) { - return fetchStoredData(selectAmpQuery, accountId, requestIds, Collections.emptySet(), timeout); + return fetchStoredData(selectAmpStoredRequestsQuery, accountId, requestIds, Collections.emptySet(), timeout); } /** @@ -188,7 +212,7 @@ public Future getAmpStoredData(String accountId, Set r @Override public Future getVideoStoredData(String accountId, Set requestIds, Set impIds, Timeout timeout) { - return fetchStoredData(selectQuery, accountId, requestIds, impIds, timeout); + return fetchStoredData(selectStoredRequestsQuery, accountId, requestIds, impIds, timeout); } /** @@ -197,11 +221,11 @@ public Future getVideoStoredData(String accountId, Set */ @Override public Future getStoredResponses(Set responseIds, Timeout timeout) { - final String queryResolvedWithParameters = selectResponseQuery.replaceAll(RESPONSE_ID_PLACEHOLDER, + final String queryResolvedWithParameters = selectStoredResponsesQuery.replaceAll(RESPONSE_ID_PLACEHOLDER, parameterHolders(responseIds.size())); final List idsQueryParameters = new ArrayList<>(); - IntStream.rangeClosed(1, StringUtils.countMatches(selectResponseQuery, RESPONSE_ID_PLACEHOLDER)) + IntStream.rangeClosed(1, StringUtils.countMatches(selectStoredResponsesQuery, RESPONSE_ID_PLACEHOLDER)) .forEach(i -> idsQueryParameters.addAll(responseIds)); return jdbcClient.executeQuery(queryResolvedWithParameters, idsQueryParameters, @@ -250,6 +274,8 @@ private static String createParametrizedQuery(String query, int requestIdsSize, private static String parameterHolders(int paramsSize) { return paramsSize == 0 ? "NULL" - : IntStream.range(0, paramsSize).mapToObj(i -> "?").collect(Collectors.joining(",")); + : IntStream.range(0, paramsSize) + .mapToObj(i -> QUERY_PARAM_PLACEHOLDER) + .collect(Collectors.joining(",")); } } diff --git a/src/main/java/org/prebid/server/settings/model/Account.java b/src/main/java/org/prebid/server/settings/model/Account.java index 0d011c9df31..479c2bc680a 100644 --- a/src/main/java/org/prebid/server/settings/model/Account.java +++ b/src/main/java/org/prebid/server/settings/model/Account.java @@ -2,6 +2,7 @@ import lombok.Builder; import lombok.Value; +import org.apache.commons.lang3.ObjectUtils; @Builder @Value @@ -29,6 +30,29 @@ public class Account { AccountAnalyticsConfig analyticsConfig; + AccountBidValidationConfig bidValidations; + + AccountStatus status; + + public Account merge(Account another) { + return Account.builder() + .id(ObjectUtils.defaultIfNull(id, another.id)) + .priceGranularity(ObjectUtils.defaultIfNull(priceGranularity, another.priceGranularity)) + .bannerCacheTtl(ObjectUtils.defaultIfNull(bannerCacheTtl, another.bannerCacheTtl)) + .videoCacheTtl(ObjectUtils.defaultIfNull(videoCacheTtl, another.videoCacheTtl)) + .eventsEnabled(ObjectUtils.defaultIfNull(eventsEnabled, another.eventsEnabled)) + .enforceCcpa(ObjectUtils.defaultIfNull(enforceCcpa, another.enforceCcpa)) + .gdpr(ObjectUtils.defaultIfNull(gdpr, another.gdpr)) + .analyticsSamplingFactor(ObjectUtils.defaultIfNull( + analyticsSamplingFactor, another.analyticsSamplingFactor)) + .truncateTargetAttr(ObjectUtils.defaultIfNull(truncateTargetAttr, another.truncateTargetAttr)) + .defaultIntegration(ObjectUtils.defaultIfNull(defaultIntegration, another.defaultIntegration)) + .analyticsConfig(ObjectUtils.defaultIfNull(analyticsConfig, another.analyticsConfig)) + .bidValidations(ObjectUtils.defaultIfNull(bidValidations, another.bidValidations)) + .status(ObjectUtils.defaultIfNull(status, another.status)) + .build(); + } + public static Account empty(String id) { return Account.builder() .id(id) diff --git a/src/main/java/org/prebid/server/settings/model/AccountBidValidationConfig.java b/src/main/java/org/prebid/server/settings/model/AccountBidValidationConfig.java new file mode 100644 index 00000000000..38c5afb2bfb --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/AccountBidValidationConfig.java @@ -0,0 +1,11 @@ +package org.prebid.server.settings.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +@Value(staticConstructor = "of") +public class AccountBidValidationConfig { + + @JsonProperty("banner-creative-max-size") + BidValidationEnforcement bannerMaxSizeEnforcement; +} diff --git a/src/main/java/org/prebid/server/settings/model/AccountGdprConfig.java b/src/main/java/org/prebid/server/settings/model/AccountGdprConfig.java index 62293e59059..16eb51b5f1f 100644 --- a/src/main/java/org/prebid/server/settings/model/AccountGdprConfig.java +++ b/src/main/java/org/prebid/server/settings/model/AccountGdprConfig.java @@ -1,15 +1,11 @@ package org.prebid.server.settings.model; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.Value; @Builder -@AllArgsConstructor -@NoArgsConstructor -@Data +@Value public class AccountGdprConfig { @JsonProperty("enabled") diff --git a/src/main/java/org/prebid/server/settings/model/AccountStatus.java b/src/main/java/org/prebid/server/settings/model/AccountStatus.java new file mode 100644 index 00000000000..0ac0ebcb66f --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/AccountStatus.java @@ -0,0 +1,6 @@ +package org.prebid.server.settings.model; + +public enum AccountStatus { + + active, inactive +} diff --git a/src/main/java/org/prebid/server/settings/model/BidValidationEnforcement.java b/src/main/java/org/prebid/server/settings/model/BidValidationEnforcement.java new file mode 100644 index 00000000000..f5ed5a6f510 --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/BidValidationEnforcement.java @@ -0,0 +1,6 @@ +package org.prebid.server.settings.model; + +public enum BidValidationEnforcement { + + skip, enforce, warn +} diff --git a/src/main/java/org/prebid/server/settings/proto/response/HttpAccountsResponse.java b/src/main/java/org/prebid/server/settings/proto/response/HttpAccountsResponse.java new file mode 100644 index 00000000000..a893b2dc01a --- /dev/null +++ b/src/main/java/org/prebid/server/settings/proto/response/HttpAccountsResponse.java @@ -0,0 +1,14 @@ +package org.prebid.server.settings.proto.response; + +import lombok.AllArgsConstructor; +import lombok.Value; +import org.prebid.server.settings.model.Account; + +import java.util.Map; + +@AllArgsConstructor(staticName = "of") +@Value +public class HttpAccountsResponse { + + Map accounts; +} diff --git a/src/main/java/org/prebid/server/settings/proto/response/HttpFetcherResponse.java b/src/main/java/org/prebid/server/settings/proto/response/HttpFetcherResponse.java index 9608e9167d7..256e9d3273e 100644 --- a/src/main/java/org/prebid/server/settings/proto/response/HttpFetcherResponse.java +++ b/src/main/java/org/prebid/server/settings/proto/response/HttpFetcherResponse.java @@ -10,7 +10,7 @@ @Value public class HttpFetcherResponse { - private Map requests; + Map requests; - private Map imps; + Map imps; } diff --git a/src/main/java/org/prebid/server/settings/service/JdbcPeriodicRefreshService.java b/src/main/java/org/prebid/server/settings/service/JdbcPeriodicRefreshService.java index 3be91567235..035a03fa028 100644 --- a/src/main/java/org/prebid/server/settings/service/JdbcPeriodicRefreshService.java +++ b/src/main/java/org/prebid/server/settings/service/JdbcPeriodicRefreshService.java @@ -7,12 +7,15 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.execution.Timeout; import org.prebid.server.execution.TimeoutFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.CacheNotificationListener; import org.prebid.server.settings.helper.JdbcStoredDataResultMapper; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.vertx.Initializable; import org.prebid.server.vertx.jdbc.JdbcClient; +import java.time.Clock; import java.time.Instant; import java.util.Collections; import java.util.Date; @@ -42,11 +45,6 @@ public class JdbcPeriodicRefreshService implements Initializable { private static final Logger logger = LoggerFactory.getLogger(JdbcPeriodicRefreshService.class); - private final CacheNotificationListener cacheNotificationListener; - private final Vertx vertx; - private final JdbcClient jdbcClient; - private final long refreshPeriod; - /** * Example of initialize query: *
@@ -56,9 +54,8 @@ public class JdbcPeriodicRefreshService implements Initializable {
      * This query will be run once on startup to fetch _all_ known Stored Request data from the database.
      */
     private final String initQuery;
-
     /**
-     * Example of initialize query:
+     * Example of update query:
      * 
      * SELECT id, requestData, type
      * FROM stored_requests
@@ -68,21 +65,41 @@ public class JdbcPeriodicRefreshService implements Initializable {
      * Wildcard "?" would be used to pass last update date automatically.
      */
     private final String updateQuery;
-    private final TimeoutFactory timeoutFactory;
+    private final long refreshPeriod;
     private final long timeout;
+    private final MetricName cacheType;
+    private final CacheNotificationListener cacheNotificationListener;
+    private final Vertx vertx;
+    private final JdbcClient jdbcClient;
+    private final TimeoutFactory timeoutFactory;
+    private final Metrics metrics;
+    private final Clock clock;
+
     private Instant lastUpdate;
 
-    public JdbcPeriodicRefreshService(CacheNotificationListener cacheNotificationListener,
-                                      Vertx vertx, JdbcClient jdbcClient, long refreshPeriod, String initQuery,
-                                      String updateQuery, TimeoutFactory timeoutFactory, long timeout) {
+    public JdbcPeriodicRefreshService(String initQuery,
+                                      String updateQuery,
+                                      long refreshPeriod,
+                                      long timeout,
+                                      MetricName cacheType,
+                                      CacheNotificationListener cacheNotificationListener,
+                                      Vertx vertx,
+                                      JdbcClient jdbcClient,
+                                      TimeoutFactory timeoutFactory,
+                                      Metrics metrics,
+                                      Clock clock) {
+
+        this.initQuery = Objects.requireNonNull(StringUtils.stripToNull(initQuery));
+        this.updateQuery = Objects.requireNonNull(StringUtils.stripToNull(updateQuery));
+        this.refreshPeriod = refreshPeriod;
+        this.timeout = timeout;
+        this.cacheType = Objects.requireNonNull(cacheType);
         this.cacheNotificationListener = Objects.requireNonNull(cacheNotificationListener);
         this.vertx = Objects.requireNonNull(vertx);
         this.jdbcClient = Objects.requireNonNull(jdbcClient);
-        this.refreshPeriod = refreshPeriod;
-        this.initQuery = Objects.requireNonNull(StringUtils.stripToNull(initQuery));
-        this.updateQuery = Objects.requireNonNull(StringUtils.stripToNull(updateQuery));
         this.timeoutFactory = Objects.requireNonNull(timeoutFactory);
-        this.timeout = timeout;
+        this.metrics = Objects.requireNonNull(metrics);
+        this.clock = Objects.requireNonNull(clock);
     }
 
     @Override
@@ -94,36 +111,52 @@ public void initialize() {
     }
 
     private void getAll() {
-        jdbcClient.executeQuery(initQuery, Collections.emptyList(), JdbcStoredDataResultMapper::map, createTimeout())
-                .map(this::save)
-                .map(ignored -> setLastUpdate(Instant.now()))
-                .recover(JdbcPeriodicRefreshService::failResponse);
+        final long startTime = clock.millis();
+
+        jdbcClient.executeQuery(
+                initQuery,
+                Collections.emptyList(),
+                JdbcStoredDataResultMapper::map,
+                createTimeout())
+                .map(storedDataResult ->
+                        handleResult(storedDataResult, Instant.now(clock), startTime, MetricName.initialize))
+                .recover(exception -> handleFailure(exception, startTime, MetricName.initialize));
     }
 
-    private Void save(StoredDataResult storedDataResult) {
+    private Void handleResult(StoredDataResult storedDataResult,
+                              Instant updateTime,
+                              long startTime,
+                              MetricName refreshType) {
+
         cacheNotificationListener.save(storedDataResult.getStoredIdToRequest(), storedDataResult.getStoredIdToImp());
-        return null;
-    }
+        lastUpdate = updateTime;
+
+        metrics.updateSettingsCacheRefreshTime(cacheType, refreshType, clock.millis() - startTime);
 
-    private Void setLastUpdate(Instant instant) {
-        lastUpdate = instant;
         return null;
     }
 
-    private static Future failResponse(Throwable exception) {
+    private Future handleFailure(Throwable exception, long startTime, MetricName refreshType) {
         logger.warn("Error occurred while request to jdbc refresh service", exception);
+
+        metrics.updateSettingsCacheRefreshTime(cacheType, refreshType, clock.millis() - startTime);
+        metrics.updateSettingsCacheRefreshErrorMetric(cacheType, refreshType);
+
         return Future.failedFuture(exception);
     }
 
     private void refresh() {
-        final Instant updateTime = Instant.now();
-
-        jdbcClient.executeQuery(updateQuery, Collections.singletonList(Date.from(lastUpdate)),
-                JdbcStoredDataResultMapper::map, createTimeout())
-                .map(this::invalidate)
-                .map(this::save)
-                .map(ignored -> setLastUpdate(updateTime))
-                .recover(JdbcPeriodicRefreshService::failResponse);
+        final Instant updateTime = Instant.now(clock);
+        final long startTime = clock.millis();
+
+        jdbcClient.executeQuery(
+                updateQuery,
+                Collections.singletonList(Date.from(lastUpdate)),
+                JdbcStoredDataResultMapper::map,
+                createTimeout())
+                .map(storedDataResult ->
+                        handleResult(invalidate(storedDataResult), updateTime, startTime, MetricName.update))
+                .recover(exception -> handleFailure(exception, startTime, MetricName.update));
     }
 
     private StoredDataResult invalidate(StoredDataResult storedDataResult) {
diff --git a/src/main/java/org/prebid/server/spring/config/JacksonConfiguration.java b/src/main/java/org/prebid/server/spring/config/JsonConfiguration.java
similarity index 68%
rename from src/main/java/org/prebid/server/spring/config/JacksonConfiguration.java
rename to src/main/java/org/prebid/server/spring/config/JsonConfiguration.java
index 52239591ca5..eda4e2fb32d 100644
--- a/src/main/java/org/prebid/server/spring/config/JacksonConfiguration.java
+++ b/src/main/java/org/prebid/server/spring/config/JsonConfiguration.java
@@ -1,15 +1,21 @@
 package org.prebid.server.spring.config;
 
 import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.json.JsonMerger;
 import org.prebid.server.json.ObjectMapperProvider;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-public class JacksonConfiguration {
+public class JsonConfiguration {
 
     @Bean
     JacksonMapper jacksonMapper() {
         return new JacksonMapper(ObjectMapperProvider.mapper());
     }
+
+    @Bean
+    JsonMerger jsonMerger(JacksonMapper mapper) {
+        return new JsonMerger(mapper);
+    }
 }
diff --git a/src/main/java/org/prebid/server/spring/config/MetricsConfiguration.java b/src/main/java/org/prebid/server/spring/config/MetricsConfiguration.java
index b6ad9702fe4..9e9f1b1db8d 100644
--- a/src/main/java/org/prebid/server/spring/config/MetricsConfiguration.java
+++ b/src/main/java/org/prebid/server/spring/config/MetricsConfiguration.java
@@ -6,6 +6,8 @@
 import com.codahale.metrics.SharedMetricRegistries;
 import com.codahale.metrics.graphite.Graphite;
 import com.codahale.metrics.graphite.GraphiteReporter;
+import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
+import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
 import com.izettle.metrics.influxdb.InfluxDbHttpSender;
 import com.izettle.metrics.influxdb.InfluxDbReporter;
 import com.izettle.metrics.influxdb.InfluxDbSender;
@@ -113,7 +115,15 @@ Metrics metrics(@Value("${metrics.metricType}") CounterType counterType, MetricR
 
     @Bean
     MetricRegistry metricRegistry() {
-        return SharedMetricRegistries.getOrCreate(METRIC_REGISTRY_NAME);
+        final boolean alreadyExists = SharedMetricRegistries.names().contains(METRIC_REGISTRY_NAME);
+        final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(METRIC_REGISTRY_NAME);
+
+        if (!alreadyExists) {
+            metricRegistry.register("jvm.gc", new GarbageCollectorMetricSet());
+            metricRegistry.register("jvm.memory", new MemoryUsageGaugeSet());
+        }
+
+        return metricRegistry;
     }
 
     @Bean
diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java
index 4c48db51da1..c68064be078 100644
--- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java
+++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java
@@ -1,9 +1,9 @@
 package org.prebid.server.spring.config;
 
-import com.iab.openrtb.request.BidRequest;
 import de.malkusch.whoisServerList.publicSuffixList.PublicSuffixList;
 import de.malkusch.whoisServerList.publicSuffixList.PublicSuffixListFactory;
 import io.vertx.core.Vertx;
+import io.vertx.core.file.FileSystem;
 import io.vertx.core.http.HttpClientOptions;
 import io.vertx.core.net.JksOptions;
 import org.prebid.server.auction.AmpRequestFactory;
@@ -11,6 +11,7 @@
 import org.prebid.server.auction.AuctionRequestFactory;
 import org.prebid.server.auction.BidResponseCreator;
 import org.prebid.server.auction.BidResponsePostProcessor;
+import org.prebid.server.auction.BidResponseReducer;
 import org.prebid.server.auction.ExchangeService;
 import org.prebid.server.auction.FpdResolver;
 import org.prebid.server.auction.ImplicitParametersExtractor;
@@ -19,6 +20,7 @@
 import org.prebid.server.auction.OrtbTypesResolver;
 import org.prebid.server.auction.PreBidRequestContextFactory;
 import org.prebid.server.auction.PrivacyEnforcementService;
+import org.prebid.server.auction.SchainResolver;
 import org.prebid.server.auction.StoredRequestProcessor;
 import org.prebid.server.auction.StoredResponseProcessor;
 import org.prebid.server.auction.TimeoutResolver;
@@ -38,10 +40,10 @@
 import org.prebid.server.events.EventsService;
 import org.prebid.server.execution.TimeoutFactory;
 import org.prebid.server.identity.IdGenerator;
-import org.prebid.server.identity.IdGeneratorType;
 import org.prebid.server.identity.NoneIdGenerator;
 import org.prebid.server.identity.UUIDIdGenerator;
 import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.json.JsonMerger;
 import org.prebid.server.log.HttpInteractionLogger;
 import org.prebid.server.log.LoggerControlKnob;
 import org.prebid.server.metric.Metrics;
@@ -49,6 +51,7 @@
 import org.prebid.server.privacy.PrivacyExtractor;
 import org.prebid.server.privacy.gdpr.TcfDefinerService;
 import org.prebid.server.settings.ApplicationSettings;
+import org.prebid.server.settings.model.BidValidationEnforcement;
 import org.prebid.server.spring.config.model.CircuitBreakerProperties;
 import org.prebid.server.spring.config.model.ExternalConversionProperties;
 import org.prebid.server.spring.config.model.HttpClientProperties;
@@ -124,13 +127,21 @@ IpAddressHelper ipAddressHelper(@Value("${ipv6.always-mask-right}") int ipv6Alwa
     }
 
     @Bean
-    FpdResolver fpdResolver(JacksonMapper mapper) {
-        return new FpdResolver(mapper);
+    FpdResolver fpdResolver(JacksonMapper mapper, JsonMerger jsonMerger) {
+        return new FpdResolver(mapper, jsonMerger);
     }
 
     @Bean
-    OrtbTypesResolver ortbTypesResolver(JacksonMapper jacksonMapper) {
-        return new OrtbTypesResolver(jacksonMapper);
+    OrtbTypesResolver ortbTypesResolver(JacksonMapper jacksonMapper, JsonMerger jsonMerger) {
+        return new OrtbTypesResolver(jacksonMapper, jsonMerger);
+    }
+
+    @Bean
+    SchainResolver schainResolver(
+            @Value("${auction.host-schain-node}") String globalSchainNode,
+            JacksonMapper mapper) {
+
+        return SchainResolver.create(globalSchainNode, mapper);
     }
 
     @Bean
@@ -199,11 +210,11 @@ AuctionRequestFactory auctionRequestFactory(
             TimeoutFactory timeoutFactory,
             ApplicationSettings applicationSettings,
             PrivacyEnforcementService privacyEnforcementService,
-            IdGenerator idGenerator,
+            IdGenerator sourceIdGenerator,
             JacksonMapper mapper) {
 
-        final List blacklistedApps = splitCommaSeparatedString(blacklistedAppsString);
-        final List blacklistedAccounts = splitCommaSeparatedString(blacklistedAccountsString);
+        final List blacklistedApps = splitToList(blacklistedAppsString);
+        final List blacklistedAccounts = splitToList(blacklistedAccountsString);
 
         return new AuctionRequestFactory(
                 maxRequestSize,
@@ -223,14 +234,21 @@ AuctionRequestFactory auctionRequestFactory(
                 timeoutResolver,
                 timeoutFactory,
                 applicationSettings,
-                idGenerator,
+                sourceIdGenerator,
                 privacyEnforcementService,
                 mapper);
     }
 
     @Bean
-    IdGenerator idGenerator(@Value("${auction.id-generator-type}") IdGeneratorType idGeneratorType) {
-        return idGeneratorType == IdGeneratorType.uuid
+    IdGenerator bidIdGenerator(@Value("${auction.generate-bid-id}") boolean generateBidId) {
+        return generateBidId
+                ? new UUIDIdGenerator()
+                : new NoneIdGenerator();
+    }
+
+    @Bean
+    IdGenerator sourceIdGenerator(@Value("${auction.generate-source-tid}") boolean generateSourceTid) {
+        return generateSourceTid
                 ? new UUIDIdGenerator()
                 : new NoneIdGenerator();
     }
@@ -257,13 +275,19 @@ AmpRequestFactory ampRequestFactory(StoredRequestProcessor storedRequestProcesso
     @Bean
     VideoRequestFactory videoRequestFactory(
             @Value("${auction.max-request-size}") int maxRequestSize,
-            @Value("${auction.video.stored-required:#{false}}") boolean enforceStoredRequest,
+            @Value("${video.stored-request-required}") boolean enforceStoredRequest,
             VideoStoredRequestProcessor storedRequestProcessor,
             AuctionRequestFactory auctionRequestFactory,
-            TimeoutResolver timeoutResolver, JacksonMapper mapper) {
+            TimeoutResolver timeoutResolver,
+            JacksonMapper mapper) {
 
-        return new VideoRequestFactory(maxRequestSize, enforceStoredRequest, storedRequestProcessor,
-                auctionRequestFactory, timeoutResolver, mapper);
+        return new VideoRequestFactory(
+                maxRequestSize,
+                enforceStoredRequest,
+                storedRequestProcessor,
+                auctionRequestFactory,
+                timeoutResolver,
+                mapper);
     }
 
     @Bean
@@ -273,27 +297,39 @@ VideoResponseFactory videoResponseFactory(JacksonMapper mapper) {
 
     @Bean
     VideoStoredRequestProcessor videoStoredRequestProcessor(
-            ApplicationSettings applicationSettings,
-            @Value("${auction.video.stored-required:#{false}}") boolean enforceStoredRequest,
+            @Value("${video.stored-request-required}") boolean enforceStoredRequest,
             @Value("${auction.blacklisted-accounts}") String blacklistedAccountsString,
-            BidRequest defaultVideoBidRequest,
+            @Value("${video.stored-requests-timeout-ms}") long defaultTimeoutMs,
+            @Value("${auction.ad-server-currency:#{null}}") String adServerCurrency,
+            @Value("${default-request.file.path:#{null}}") String defaultBidRequestPath,
+            FileSystem fileSystem,
+            ApplicationSettings applicationSettings,
+            VideoRequestValidator videoRequestValidator,
             Metrics metrics,
             TimeoutFactory timeoutFactory,
             TimeoutResolver timeoutResolver,
-            @Value("${video.stored-requests-timeout-ms}") long defaultTimeoutMs,
-            @Value("${auction.ad-server-currency:#{null}}") String adServerCurrency,
-            JacksonMapper mapper) {
-
-        final List blacklistedAccounts = splitCommaSeparatedString(blacklistedAccountsString);
+            JacksonMapper mapper,
+            JsonMerger jsonMerger) {
 
-        return new VideoStoredRequestProcessor(applicationSettings, new VideoRequestValidator(), enforceStoredRequest,
-                blacklistedAccounts, defaultVideoBidRequest, metrics, timeoutFactory, timeoutResolver, defaultTimeoutMs,
-                adServerCurrency, mapper);
+        return VideoStoredRequestProcessor.create(
+                enforceStoredRequest,
+                splitToList(blacklistedAccountsString),
+                defaultTimeoutMs,
+                adServerCurrency,
+                defaultBidRequestPath,
+                fileSystem,
+                applicationSettings,
+                videoRequestValidator,
+                metrics,
+                timeoutFactory,
+                timeoutResolver,
+                mapper,
+                jsonMerger);
     }
 
     @Bean
-    BidRequest defaultVideoBidRequest() {
-        return BidRequest.builder().build();
+    VideoRequestValidator videoRequestValidator() {
+        return new VideoRequestValidator();
     }
 
     @Bean
@@ -434,7 +470,8 @@ BidResponseCreator bidResponseCreator(
             BidderCatalog bidderCatalog,
             EventsService eventsService,
             StoredRequestProcessor storedRequestProcessor,
-            @Value("${auction.generate-bid-id}") boolean generateBidId,
+            BidResponseReducer bidResponseReducer,
+            IdGenerator bidIdGenerator,
             @Value("${settings.targeting.truncate-attr-chars}") int truncateAttrChars,
             Clock clock,
             JacksonMapper mapper) {
@@ -444,7 +481,8 @@ BidResponseCreator bidResponseCreator(
                 bidderCatalog,
                 eventsService,
                 storedRequestProcessor,
-                generateBidId,
+                bidResponseReducer,
+                bidIdGenerator,
                 truncateAttrChars,
                 clock,
                 mapper);
@@ -457,6 +495,7 @@ ExchangeService exchangeService(
             StoredResponseProcessor storedResponseProcessor,
             PrivacyEnforcementService privacyEnforcementService,
             FpdResolver fpdResolver,
+            SchainResolver schainResolver,
             HttpBidderRequester httpBidderRequester,
             ResponseBidValidator responseBidValidator,
             CurrencyConversionService currencyConversionService,
@@ -472,6 +511,7 @@ ExchangeService exchangeService(
                 storedResponseProcessor,
                 privacyEnforcementService,
                 fpdResolver,
+                schainResolver,
                 httpBidderRequester,
                 responseBidValidator,
                 currencyConversionService,
@@ -485,12 +525,28 @@ ExchangeService exchangeService(
     @Bean
     StoredRequestProcessor storedRequestProcessor(
             @Value("${auction.stored-requests-timeout-ms}") long defaultTimeoutMs,
+            @Value("${default-request.file.path:#{null}}") String defaultBidRequestPath,
+            FileSystem fileSystem,
             ApplicationSettings applicationSettings,
             Metrics metrics,
             TimeoutFactory timeoutFactory,
-            JacksonMapper mapper) {
+            JacksonMapper mapper,
+            JsonMerger jsonMerger) {
+
+        return StoredRequestProcessor.create(
+                defaultTimeoutMs,
+                defaultBidRequestPath,
+                fileSystem,
+                applicationSettings,
+                metrics,
+                timeoutFactory,
+                mapper,
+                jsonMerger);
+    }
 
-        return new StoredRequestProcessor(defaultTimeoutMs, applicationSettings, metrics, timeoutFactory, mapper);
+    @Bean
+    BidResponseReducer bidResponseReducer() {
+        return new BidResponseReducer();
     }
 
     @Bean
@@ -508,10 +564,11 @@ PrivacyEnforcementService privacyEnforcementService(
             TcfDefinerService tcfDefinerService,
             IpAddressHelper ipAddressHelper,
             Metrics metrics,
-            @Value("${ccpa.enforce}") boolean ccpaEnforce) {
+            @Value("${ccpa.enforce}") boolean ccpaEnforce,
+            @Value("${lmt.enforce}") boolean lmtEnforce) {
 
         return new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, ccpaEnforce);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, ccpaEnforce, lmtEnforce);
     }
 
     @Bean
@@ -547,8 +604,12 @@ BidderParamValidator bidderParamValidator(BidderCatalog bidderCatalog, JacksonMa
     }
 
     @Bean
-    ResponseBidValidator responseValidator() {
-        return new ResponseBidValidator();
+    ResponseBidValidator responseValidator(
+            @Value("${auction.validations.banner-creative-max-size}") BidValidationEnforcement bannerMaxSizeEnforcement,
+            @Value("${auction.validations.secure-markup}") BidValidationEnforcement secureMarkupEnforcement,
+            Metrics metrics) {
+
+        return new ResponseBidValidator(bannerMaxSizeEnforcement, secureMarkupEnforcement, metrics);
     }
 
     @Bean
@@ -587,6 +648,7 @@ AmpResponsePostProcessor ampResponsePostProcessor() {
     @Bean
     CurrencyConversionService currencyConversionService(
             @Autowired(required = false) ExternalConversionProperties externalConversionProperties) {
+
         return new CurrencyConversionService(externalConversionProperties);
     }
 
@@ -594,13 +656,26 @@ CurrencyConversionService currencyConversionService(
     @ConditionalOnProperty(prefix = "currency-converter.external-rates", name = "enabled", havingValue = "true")
     ExternalConversionProperties externalConversionProperties(
             @Value("${currency-converter.external-rates.url}") String currencyServerUrl,
-            @Value("${currency-converter.external-rates.default-timeout-ms}") long defaultTimeout,
-            @Value("${currency-converter.external-rates.refresh-period-ms}") long refreshPeriod,
+            @Value("${currency-converter.external-rates.default-timeout-ms}") long defaultTimeoutMs,
+            @Value("${currency-converter.external-rates.refresh-period-ms}") long refreshPeriodMs,
+            @Value("${currency-converter.external-rates.stale-after-ms}") long staleAfterMs,
+            @Value("${currency-converter.external-rates.stale-period-ms:#{null}}") Long stalePeriodMs,
             Vertx vertx,
             HttpClient httpClient,
+            Metrics metrics,
+            Clock clock,
             JacksonMapper mapper) {
 
-        return new ExternalConversionProperties(currencyServerUrl, defaultTimeout, refreshPeriod, vertx, httpClient,
+        return new ExternalConversionProperties(
+                currencyServerUrl,
+                defaultTimeoutMs,
+                refreshPeriodMs,
+                staleAfterMs,
+                stalePeriodMs,
+                vertx,
+                httpClient,
+                metrics,
+                clock,
                 mapper);
     }
 
@@ -614,9 +689,11 @@ LoggerControlKnob loggerControlKnob(Vertx vertx) {
         return new LoggerControlKnob(vertx);
     }
 
-    private static List splitCommaSeparatedString(String listString) {
-        return Stream.of(listString.split(","))
+    private static List splitToList(String listAsString) {
+        return listAsString != null
+                ? Stream.of(listAsString.split(","))
                 .map(String::trim)
-                .collect(Collectors.toList());
+                .collect(Collectors.toList())
+                : null;
     }
 }
diff --git a/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java b/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java
index d4c80580f46..06488c21fb5 100644
--- a/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java
+++ b/src/main/java/org/prebid/server/spring/config/SettingsConfiguration.java
@@ -10,16 +10,19 @@
 import org.apache.commons.lang3.ObjectUtils;
 import org.prebid.server.execution.TimeoutFactory;
 import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.metric.MetricName;
 import org.prebid.server.metric.Metrics;
 import org.prebid.server.settings.ApplicationSettings;
 import org.prebid.server.settings.CachingApplicationSettings;
 import org.prebid.server.settings.CompositeApplicationSettings;
+import org.prebid.server.settings.EnrichingApplicationSettings;
 import org.prebid.server.settings.FileApplicationSettings;
 import org.prebid.server.settings.HttpApplicationSettings;
 import org.prebid.server.settings.JdbcApplicationSettings;
 import org.prebid.server.settings.SettingsCache;
 import org.prebid.server.settings.service.HttpPeriodicRefreshService;
 import org.prebid.server.settings.service.JdbcPeriodicRefreshService;
+import org.prebid.server.spring.config.model.AccountConfigurationProperties;
 import org.prebid.server.spring.config.model.CircuitBreakerProperties;
 import org.prebid.server.vertx.ContextRunner;
 import org.prebid.server.vertx.http.HttpClient;
@@ -72,14 +75,20 @@ static class DatabaseSettingsConfiguration {
 
         @Bean
         JdbcApplicationSettings jdbcApplicationSettings(
+                @Value("${settings.database.account-query}") String accountQuery,
                 @Value("${settings.database.stored-requests-query}") String storedRequestsQuery,
                 @Value("${settings.database.amp-stored-requests-query}") String ampStoredRequestsQuery,
-                @Value("${settings.database.stored-responses-query}") String storedResponseQuery,
+                @Value("${settings.database.stored-responses-query}") String storedResponsesQuery,
                 JdbcClient jdbcClient,
                 JacksonMapper jacksonMapper) {
 
             return new JdbcApplicationSettings(
-                    jdbcClient, jacksonMapper, storedRequestsQuery, ampStoredRequestsQuery, storedResponseQuery);
+                    jdbcClient,
+                    jacksonMapper,
+                    accountQuery,
+                    storedRequestsQuery,
+                    ampStoredRequestsQuery,
+                    storedResponsesQuery);
         }
 
         @Bean
@@ -229,7 +238,8 @@ public HttpPeriodicRefreshService ampHttpPeriodicRefreshService(
     }
 
     @Configuration
-    @ConditionalOnProperty(prefix = "settings.in-memory-cache.jdbc-update",
+    @ConditionalOnProperty(
+            prefix = "settings.in-memory-cache.jdbc-update",
             name = {"refresh-rate", "timeout", "init-query", "update-query", "amp-init-query", "amp-update-query"})
     static class JdbcPeriodicRefreshServiceConfiguration {
 
@@ -248,24 +258,50 @@ static class JdbcPeriodicRefreshServiceConfiguration {
         @Autowired
         TimeoutFactory timeoutFactory;
 
+        @Autowired
+        Metrics metrics;
+
+        @Autowired
+        Clock clock;
+
         @Bean
         public JdbcPeriodicRefreshService jdbcPeriodicRefreshService(
-                SettingsCache settingsCache,
+                @Qualifier("settingsCache") SettingsCache settingsCache,
                 @Value("${settings.in-memory-cache.jdbc-update.init-query}") String initQuery,
                 @Value("${settings.in-memory-cache.jdbc-update.update-query}") String updateQuery) {
 
-            return new JdbcPeriodicRefreshService(settingsCache, vertx, jdbcClient, refreshPeriod,
-                    initQuery, updateQuery, timeoutFactory, timeout);
+            return new JdbcPeriodicRefreshService(
+                    initQuery,
+                    updateQuery,
+                    refreshPeriod,
+                    timeout,
+                    MetricName.stored_request,
+                    settingsCache,
+                    vertx,
+                    jdbcClient,
+                    timeoutFactory,
+                    metrics,
+                    clock);
         }
 
         @Bean
         public JdbcPeriodicRefreshService ampJdbcPeriodicRefreshService(
-                SettingsCache settingsCache,
+                @Qualifier("ampSettingsCache") SettingsCache ampSettingsCache,
                 @Value("${settings.in-memory-cache.jdbc-update.amp-init-query}") String ampInitQuery,
                 @Value("${settings.in-memory-cache.jdbc-update.amp-update-query}") String ampUpdateQuery) {
 
-            return new JdbcPeriodicRefreshService(settingsCache, vertx, jdbcClient, refreshPeriod,
-                    ampInitQuery, ampUpdateQuery, timeoutFactory, timeout);
+            return new JdbcPeriodicRefreshService(
+                    ampInitQuery,
+                    ampUpdateQuery,
+                    refreshPeriod,
+                    timeout,
+                    MetricName.amp_stored_request,
+                    ampSettingsCache,
+                    vertx,
+                    jdbcClient,
+                    timeoutFactory,
+                    metrics,
+                    clock);
         }
     }
 
@@ -292,23 +328,45 @@ CompositeApplicationSettings compositeApplicationSettings(
         }
     }
 
+    @Configuration
+    static class EnrichingSettingsConfiguration {
+
+        @Bean
+        @ConfigurationProperties("settings.default-account-config")
+        AccountConfigurationProperties defaultAccountConfigurationProperties() {
+            return new AccountConfigurationProperties();
+        }
+
+        @Bean
+        EnrichingApplicationSettings enrichingApplicationSettings(
+                CompositeApplicationSettings compositeApplicationSettings,
+                AccountConfigurationProperties defaultAccountConfigurationProperties,
+                JacksonMapper mapper) {
+
+            return new EnrichingApplicationSettings(
+                    compositeApplicationSettings, defaultAccountConfigurationProperties.toAccount(mapper));
+        }
+    }
+
     @Configuration
     static class CachingSettingsConfiguration {
 
         @Bean
         @ConditionalOnProperty(prefix = "settings.in-memory-cache", name = {"ttl-seconds", "cache-size"})
         CachingApplicationSettings cachingApplicationSettings(
-                CompositeApplicationSettings compositeApplicationSettings,
+                EnrichingApplicationSettings enrichingApplicationSettings,
                 ApplicationSettingsCacheProperties cacheProperties,
                 @Qualifier("settingsCache") SettingsCache cache,
                 @Qualifier("ampSettingsCache") SettingsCache ampCache,
-                @Qualifier("videoSettingCache") SettingsCache videoCache) {
+                @Qualifier("videoSettingCache") SettingsCache videoCache,
+                Metrics metrics) {
 
             return new CachingApplicationSettings(
-                    compositeApplicationSettings,
+                    enrichingApplicationSettings,
                     cache,
                     ampCache,
                     videoCache,
+                    metrics,
                     cacheProperties.getTtlSeconds(),
                     cacheProperties.getCacheSize());
         }
@@ -320,8 +378,8 @@ static class ApplicationSettingsConfiguration {
         @Bean
         ApplicationSettings applicationSettings(
                 @Autowired(required = false) CachingApplicationSettings cachingApplicationSettings,
-                @Autowired(required = false) CompositeApplicationSettings compositeApplicationSettings) {
-            return ObjectUtils.defaultIfNull(cachingApplicationSettings, compositeApplicationSettings);
+                EnrichingApplicationSettings enrichingApplicationSettings) {
+            return ObjectUtils.defaultIfNull(cachingApplicationSettings, enrichingApplicationSettings);
         }
     }
 
diff --git a/src/main/java/org/prebid/server/spring/config/bidder/AmxConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/AmxConfiguration.java
new file mode 100644
index 00000000000..6cdf5db7287
--- /dev/null
+++ b/src/main/java/org/prebid/server/spring/config/bidder/AmxConfiguration.java
@@ -0,0 +1,56 @@
+package org.prebid.server.spring.config.bidder;
+
+import org.prebid.server.bidder.BidderDeps;
+import org.prebid.server.bidder.amx.AmxBidder;
+import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
+import org.prebid.server.spring.config.bidder.model.UsersyncConfigurationProperties;
+import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
+import org.prebid.server.spring.config.bidder.util.BidderInfoCreator;
+import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
+import org.prebid.server.spring.env.YamlPropertySourceFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+import javax.validation.constraints.NotBlank;
+
+@Configuration
+@PropertySource(value = "classpath:/bidder-config/amx.yaml", factory = YamlPropertySourceFactory.class)
+public class AmxConfiguration {
+
+    private static final String BIDDER_NAME = "amx";
+
+    @Value("${external-url}")
+    @NotBlank
+    private String externalUrl;
+
+    @Autowired
+    private JacksonMapper mapper;
+
+    @Autowired
+    @Qualifier("amxConfigurationProperties")
+    private BidderConfigurationProperties configProperties;
+
+    @Bean("amxConfigurationProperties")
+    @ConfigurationProperties("adapters.amx")
+    BidderConfigurationProperties configurationProperties() {
+        return new BidderConfigurationProperties();
+    }
+
+    @Bean
+    BidderDeps amxBidderDeps() {
+        final UsersyncConfigurationProperties usersync = configProperties.getUsersync();
+
+        return BidderDepsAssembler.forBidder(BIDDER_NAME)
+                .withConfig(configProperties)
+                .bidderInfo(BidderInfoCreator.create(configProperties))
+                .usersyncerCreator(UsersyncerCreator.create(usersync, externalUrl))
+                .bidderCreator(() -> new AmxBidder(configProperties.getEndpoint(), mapper))
+                .assemble();
+    }
+}
diff --git a/src/main/java/org/prebid/server/spring/config/bidder/ConversantConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/ConversantConfiguration.java
index 91dc61f7823..4f2a10859e1 100644
--- a/src/main/java/org/prebid/server/spring/config/bidder/ConversantConfiguration.java
+++ b/src/main/java/org/prebid/server/spring/config/bidder/ConversantConfiguration.java
@@ -1,5 +1,8 @@
 package org.prebid.server.spring.config.bidder;
 
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
 import org.prebid.server.bidder.BidderDeps;
 import org.prebid.server.bidder.conversant.ConversantAdapter;
 import org.prebid.server.bidder.conversant.ConversantBidder;
@@ -17,8 +20,10 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
+import org.springframework.validation.annotation.Validated;
 
 import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 
 @Configuration
 @PropertySource(value = "classpath:/bidder-config/conversant.yaml", factory = YamlPropertySourceFactory.class)
@@ -35,12 +40,12 @@ public class ConversantConfiguration {
 
     @Autowired
     @Qualifier("conversantConfigurationProperties")
-    private BidderConfigurationProperties configProperties;
+    private ConversantConfigurationProperties configProperties;
 
     @Bean("conversantConfigurationProperties")
     @ConfigurationProperties("adapters.conversant")
-    BidderConfigurationProperties configurationProperties() {
-        return new BidderConfigurationProperties();
+    ConversantConfigurationProperties configurationProperties() {
+        return new ConversantConfigurationProperties();
     }
 
     @Bean
@@ -51,9 +56,20 @@ BidderDeps conversantBidderDeps() {
                 .withConfig(configProperties)
                 .bidderInfo(BidderInfoCreator.create(configProperties))
                 .usersyncerCreator(UsersyncerCreator.create(usersync, externalUrl))
-                .bidderCreator(() -> new ConversantBidder(configProperties.getEndpoint(), mapper))
+                .bidderCreator(() -> new ConversantBidder(configProperties.getEndpoint(),
+                        configProperties.getGenerateBidId(), mapper))
                 .adapterCreator(() -> new ConversantAdapter(usersync.getCookieFamilyName(),
                         configProperties.getEndpoint(), mapper))
                 .assemble();
     }
+
+    @Validated
+    @Data
+    @EqualsAndHashCode(callSuper = true)
+    @NoArgsConstructor
+    private static class ConversantConfigurationProperties extends BidderConfigurationProperties {
+
+        @NotNull
+        private Boolean generateBidId;
+    }
 }
diff --git a/src/main/java/org/prebid/server/spring/config/bidder/NobidConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/NobidConfiguration.java
new file mode 100644
index 00000000000..8f5b0067458
--- /dev/null
+++ b/src/main/java/org/prebid/server/spring/config/bidder/NobidConfiguration.java
@@ -0,0 +1,56 @@
+package org.prebid.server.spring.config.bidder;
+
+import org.prebid.server.bidder.BidderDeps;
+import org.prebid.server.bidder.nobid.NobidBidder;
+import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
+import org.prebid.server.spring.config.bidder.model.UsersyncConfigurationProperties;
+import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
+import org.prebid.server.spring.config.bidder.util.BidderInfoCreator;
+import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
+import org.prebid.server.spring.env.YamlPropertySourceFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+import javax.validation.constraints.NotBlank;
+
+@Configuration
+@PropertySource(value = "classpath:/bidder-config/nobid.yaml", factory = YamlPropertySourceFactory.class)
+public class NobidConfiguration {
+
+    private static final String BIDDER_NAME = "nobid";
+
+    @Value("${external-url}")
+    @NotBlank
+    private String externalUrl;
+
+    @Autowired
+    private JacksonMapper mapper;
+
+    @Autowired
+    @Qualifier("nobidConfigurationProperties")
+    private BidderConfigurationProperties configProperties;
+
+    @Bean("nobidConfigurationProperties")
+    @ConfigurationProperties("adapters.nobid")
+    BidderConfigurationProperties configurationProperties() {
+        return new BidderConfigurationProperties();
+    }
+
+    @Bean
+    BidderDeps nobidBidderDeps() {
+        final UsersyncConfigurationProperties usersync = configProperties.getUsersync();
+
+        return BidderDepsAssembler.forBidder(BIDDER_NAME)
+                .withConfig(configProperties)
+                .bidderInfo(BidderInfoCreator.create(configProperties))
+                .usersyncerCreator(UsersyncerCreator.create(usersync, externalUrl))
+                .bidderCreator(() -> new NobidBidder(configProperties.getEndpoint(), mapper))
+                .assemble();
+    }
+}
diff --git a/src/main/java/org/prebid/server/spring/config/bidder/SilvermobConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/SilvermobConfiguration.java
new file mode 100644
index 00000000000..06d30545f85
--- /dev/null
+++ b/src/main/java/org/prebid/server/spring/config/bidder/SilvermobConfiguration.java
@@ -0,0 +1,56 @@
+package org.prebid.server.spring.config.bidder;
+
+import org.prebid.server.bidder.BidderDeps;
+import org.prebid.server.bidder.silvermob.SilvermobBidder;
+import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
+import org.prebid.server.spring.config.bidder.model.UsersyncConfigurationProperties;
+import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
+import org.prebid.server.spring.config.bidder.util.BidderInfoCreator;
+import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
+import org.prebid.server.spring.env.YamlPropertySourceFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+import javax.validation.constraints.NotBlank;
+
+@Configuration
+@PropertySource(value = "classpath:/bidder-config/silvermob.yaml", factory = YamlPropertySourceFactory.class)
+public class SilvermobConfiguration {
+
+    private static final String BIDDER_NAME = "silvermob";
+
+    @Value("${external-url}")
+    @NotBlank
+    private String externalUrl;
+
+    @Autowired
+    private JacksonMapper mapper;
+
+    @Autowired
+    @Qualifier("silvermobConfigurationProperties")
+    private BidderConfigurationProperties configProperties;
+
+    @Bean("silvermobConfigurationProperties")
+    @ConfigurationProperties("adapters.silvermob")
+    BidderConfigurationProperties configurationProperties() {
+        return new BidderConfigurationProperties();
+    }
+
+    @Bean
+    BidderDeps silvermobBidderDeps() {
+        final UsersyncConfigurationProperties usersync = configProperties.getUsersync();
+
+        return BidderDepsAssembler.forBidder(BIDDER_NAME)
+                .withConfig(configProperties)
+                .bidderInfo(BidderInfoCreator.create(configProperties))
+                .usersyncerCreator(UsersyncerCreator.create(usersync, externalUrl))
+                .bidderCreator(() -> new SilvermobBidder(configProperties.getEndpoint(), mapper))
+                .assemble();
+    }
+}
diff --git a/src/main/java/org/prebid/server/spring/config/model/AccountConfigurationProperties.java b/src/main/java/org/prebid/server/spring/config/model/AccountConfigurationProperties.java
new file mode 100644
index 00000000000..3eac6b136cb
--- /dev/null
+++ b/src/main/java/org/prebid/server/spring/config/model/AccountConfigurationProperties.java
@@ -0,0 +1,56 @@
+package org.prebid.server.spring.config.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.settings.model.Account;
+import org.prebid.server.settings.model.AccountAnalyticsConfig;
+import org.prebid.server.settings.model.AccountGdprConfig;
+import org.prebid.server.settings.model.AccountStatus;
+
+@Data
+@NoArgsConstructor
+public class AccountConfigurationProperties {
+
+    private String priceGranularity;
+
+    private Integer bannerCacheTtl;
+
+    private Integer videoCacheTtl;
+
+    private Boolean eventsEnabled;
+
+    private Boolean enforceCcpa;
+
+    private String gdpr;
+
+    private Integer analyticsSamplingFactor;
+
+    private Integer truncateTargetAttr;
+
+    private String defaultIntegration;
+
+    private String analyticsConfig;
+
+    private AccountStatus status;
+
+    public Account toAccount(JacksonMapper mapper) {
+        return Account.builder()
+                .priceGranularity(getPriceGranularity())
+                .bannerCacheTtl(getBannerCacheTtl())
+                .videoCacheTtl(getVideoCacheTtl())
+                .eventsEnabled(getEventsEnabled())
+                .enforceCcpa(getEnforceCcpa())
+                .gdpr(toModel(mapper, getGdpr(), AccountGdprConfig.class))
+                .analyticsSamplingFactor(getAnalyticsSamplingFactor())
+                .truncateTargetAttr(getTruncateTargetAttr())
+                .defaultIntegration(getDefaultIntegration())
+                .analyticsConfig(toModel(mapper, getAnalyticsConfig(), AccountAnalyticsConfig.class))
+                .status(getStatus())
+                .build();
+    }
+
+    private static  T toModel(JacksonMapper mapper, String source, Class targetClass) {
+        return source != null ? mapper.decodeValue(source, targetClass) : null;
+    }
+}
diff --git a/src/main/java/org/prebid/server/spring/config/model/ExternalConversionProperties.java b/src/main/java/org/prebid/server/spring/config/model/ExternalConversionProperties.java
index ac556347f51..b0206149f0d 100644
--- a/src/main/java/org/prebid/server/spring/config/model/ExternalConversionProperties.java
+++ b/src/main/java/org/prebid/server/spring/config/model/ExternalConversionProperties.java
@@ -4,12 +4,14 @@
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import org.prebid.server.json.JacksonMapper;
+import org.prebid.server.metric.Metrics;
 import org.prebid.server.vertx.http.HttpClient;
 import org.springframework.validation.annotation.Validated;
 
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
+import java.time.Clock;
 
 @Validated
 @Data
@@ -21,11 +23,17 @@ public class ExternalConversionProperties {
 
     @NotNull
     @Min(2)
-    Long defaultTimeout;
+    Long defaultTimeoutMs;
 
     @NotNull
     @Min(2)
-    Long refreshPeriod;
+    Long refreshPeriodMs;
+
+    @NotNull
+    Long staleAfterMs;
+
+    @Min(2)
+    Long stalePeriodMs;
 
     @NotNull
     Vertx vertx;
@@ -33,6 +41,12 @@ public class ExternalConversionProperties {
     @NotNull
     HttpClient httpClient;
 
+    @NotNull
+    Metrics metrics;
+
+    @NotNull
+    Clock clock;
+
     @NotNull
     JacksonMapper mapper;
 }
diff --git a/src/main/java/org/prebid/server/util/HttpUtil.java b/src/main/java/org/prebid/server/util/HttpUtil.java
index 1f12b937722..3344f8d0e04 100644
--- a/src/main/java/org/prebid/server/util/HttpUtil.java
+++ b/src/main/java/org/prebid/server/util/HttpUtil.java
@@ -53,20 +53,6 @@ public final class HttpUtil {
     private HttpUtil() {
     }
 
-    /**
-     * Detects whether browser is safari or not by user agent analysis.
-     */
-    public static boolean isSafari(String userAgent) {
-        // this is a simple heuristic based on this article:
-        // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
-        //
-        // there are libraries available doing different kinds of User-Agent analysis but they impose performance
-        // implications as well, example: https://github.com/nielsbasjes/yauaa
-        return StringUtils.isNotBlank(userAgent)
-                && userAgent.contains("AppleWebKit") && userAgent.contains("Safari")
-                && !userAgent.contains("Chrome") && !userAgent.contains("Chromium");
-    }
-
     /**
      * Checks the input string for using as URL.
      */
diff --git a/src/main/java/org/prebid/server/validation/RequestValidator.java b/src/main/java/org/prebid/server/validation/RequestValidator.java
index ab4a1f3d8d7..795577e2265 100644
--- a/src/main/java/org/prebid/server/validation/RequestValidator.java
+++ b/src/main/java/org/prebid/server/validation/RequestValidator.java
@@ -45,10 +45,10 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchain;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
 import org.prebid.server.proto.openrtb.ext.request.ExtSite;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid;
@@ -67,6 +67,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /**
@@ -76,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";
 
@@ -130,6 +133,7 @@ public ValidationResult validate(BidRequest bidRequest) {
                 validateBidAdjustmentFactors(
                         ObjectUtils.defaultIfNull(extRequestPrebid.getBidadjustmentfactors(), Collections.emptyMap()),
                         aliases);
+                validateSchains(extRequestPrebid.getSchains());
             }
 
             if (CollectionUtils.isEmpty(bidRequest.getImp())) {
@@ -203,6 +207,35 @@ private void validateBidAdjustmentFactors(Map adjustmentFact
         }
     }
 
+    private void validateSchains(List schains) throws ValidationException {
+        if (schains == null) {
+            return;
+        }
+
+        final Set schainBidders = new HashSet<>();
+        for (final ExtRequestPrebidSchain schain : schains) {
+            if (schain == null) {
+                continue;
+            }
+
+            final List bidders = schain.getBidders();
+            if (bidders == null) {
+                continue;
+            }
+
+            for (final String bidder : bidders) {
+                if (schainBidders.contains(bidder)) {
+                    throw new ValidationException(
+                            "request.ext.prebid.schains contains multiple schains for bidder %s; "
+                                    + "it must contain no more than one per bidder.",
+                            bidder);
+                }
+
+                schainBidders.add(bidder);
+            }
+        }
+    }
+
     private boolean isUnknownBidderOrAlias(String bidder, Map aliases) {
         return !bidderCatalog.isValidName(bidder) && !aliases.containsKey(bidder);
     }
@@ -392,18 +425,12 @@ private void validateUser(User user, Map aliases) throws Validat
                 }
             }
 
-            final ExtUserDigiTrust digitrust = extUser.getDigitrust();
-            if (digitrust != null && digitrust.getPref() != null && digitrust.getPref() != 0) {
-                throw new ValidationException("request.user contains a digitrust object that is not valid");
-            }
-
             final List eids = extUser.getEids();
             if (eids != null) {
                 if (eids.isEmpty()) {
                     throw new ValidationException(
                             "request.user.ext.eids must contain at least one element or be undefined");
                 }
-                final Set uniqueSources = new HashSet<>(eids.size());
                 for (int index = 0; index < eids.size(); index++) {
                     final ExtUserEid eid = eids.get(index);
                     if (StringUtils.isBlank(eid.getSource())) {
@@ -431,9 +458,10 @@ private void validateUser(User user, Map aliases) throws Validat
                             }
                         }
                     }
-                    uniqueSources.add(eid.getSource());
                 }
-
+                final Set uniqueSources = eids.stream()
+                        .map(ExtUserEid::getSource)
+                        .collect(Collectors.toSet());
                 if (eids.size() != uniqueSources.size()) {
                     throw new ValidationException("request.user.ext.eids must contain unique sources");
                 }
@@ -492,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);
         }
     }
 
@@ -736,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);
+        }
+
+        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;
         }
 
-        final Iterator> bidderExtensions = ext.fields();
+        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));
         }
     }
 
@@ -755,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);
         }
     }
 
@@ -867,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/main/java/org/prebid/server/validation/ResponseBidValidator.java b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java
index 86e29e29bc1..f0e6ac31d89 100644
--- a/src/main/java/org/prebid/server/validation/ResponseBidValidator.java
+++ b/src/main/java/org/prebid/server/validation/ResponseBidValidator.java
@@ -1,29 +1,74 @@
 package org.prebid.server.validation;
 
+import com.iab.openrtb.request.Banner;
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Format;
+import com.iab.openrtb.request.Imp;
 import com.iab.openrtb.response.Bid;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.prebid.server.auction.BidderAliases;
+import org.prebid.server.auction.model.AuctionContext;
 import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.metric.MetricName;
+import org.prebid.server.metric.Metrics;
+import org.prebid.server.proto.openrtb.ext.response.BidType;
+import org.prebid.server.settings.model.Account;
+import org.prebid.server.settings.model.AccountBidValidationConfig;
+import org.prebid.server.settings.model.BidValidationEnforcement;
 import org.prebid.server.validation.model.ValidationResult;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Currency;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
 
 /**
  * Validator for response {@link Bid} object.
  */
 public class ResponseBidValidator {
 
-    public ValidationResult validate(BidderBid bidderBid) {
+    private static final String[] INSECURE_MARKUP_MARKERS = {"http:", "http%3A"};
+    private static final String[] SECURE_MARKUP_MARKERS = {"https:", "https%3A"};
+
+    private final BidValidationEnforcement bannerMaxSizeEnforcement;
+    private final BidValidationEnforcement secureMarkupEnforcement;
+    private final Metrics metrics;
+
+    public ResponseBidValidator(BidValidationEnforcement bannerMaxSizeEnforcement,
+                                BidValidationEnforcement secureMarkupEnforcement,
+                                Metrics metrics) {
+
+        this.bannerMaxSizeEnforcement = Objects.requireNonNull(bannerMaxSizeEnforcement);
+        this.secureMarkupEnforcement = Objects.requireNonNull(secureMarkupEnforcement);
+        this.metrics = Objects.requireNonNull(metrics);
+    }
+
+    public ValidationResult validate(
+            BidderBid bidderBid, String bidder, AuctionContext auctionContext, BidderAliases aliases) {
+
+        final List warnings = new ArrayList<>();
+
         try {
-            validateFieldsFor(bidderBid.getBid());
+            validateCommonFields(bidderBid.getBid());
             validateCurrency(bidderBid.getBidCurrency());
+
+            if (bidderBid.getType() == BidType.banner) {
+                warnings.addAll(validateBannerFields(bidderBid, bidder, auctionContext, aliases));
+            }
+
+            warnings.addAll(validateSecureMarkup(bidderBid, bidder, auctionContext, aliases));
         } catch (ValidationException e) {
-            return ValidationResult.error(e.getMessage());
+            return ValidationResult.error(warnings, e.getMessage());
         }
-        return ValidationResult.success();
+        return ValidationResult.success(warnings);
     }
 
-    private static void validateFieldsFor(Bid bid) throws ValidationException {
+    private static void validateCommonFields(Bid bid) throws ValidationException {
         if (bid == null) {
             throw new ValidationException("Empty bid object submitted.");
         }
@@ -64,4 +109,122 @@ private static void validateCurrency(String currency) {
             throw new IllegalArgumentException(String.format("BidResponse currency is not valid: %s", currency), e);
         }
     }
+
+    private List validateBannerFields(BidderBid bidderBid,
+                                              String bidder,
+                                              AuctionContext auctionContext,
+                                              BidderAliases aliases) throws ValidationException {
+
+        final Bid bid = bidderBid.getBid();
+        final Account account = auctionContext.getAccount();
+
+        final BidValidationEnforcement bannerMaxSizeEnforcement = effectiveBannerMaxSizeEnforcement(account);
+        if (bannerMaxSizeEnforcement != BidValidationEnforcement.skip
+                && bannerSizeIsNotValid(bid, auctionContext.getBidRequest())) {
+
+            return singleWarningOrValidationException(
+                    bannerMaxSizeEnforcement,
+                    metricName -> metrics.updateSizeValidationMetrics(
+                            aliases.resolveBidder(bidder), account.getId(), metricName),
+                    "Bid \"%s\" has 'w' and 'h' that are not valid. Bid dimensions: '%dx%d'",
+                    bid.getId(), bid.getW(), bid.getH());
+        }
+
+        return Collections.emptyList();
+    }
+
+    private BidValidationEnforcement effectiveBannerMaxSizeEnforcement(Account account) {
+        final AccountBidValidationConfig validationConfig = account.getBidValidations();
+        final BidValidationEnforcement accountBannerMaxSizeEnforcement =
+                validationConfig != null ? validationConfig.getBannerMaxSizeEnforcement() : null;
+
+        return ObjectUtils.defaultIfNull(accountBannerMaxSizeEnforcement, bannerMaxSizeEnforcement);
+    }
+
+    private static boolean bannerSizeIsNotValid(Bid bid, BidRequest bidRequest) throws ValidationException {
+        final Format maxSize = maxSizeForBanner(bid, bidRequest);
+        final Integer bidW = bid.getW();
+        final Integer bidH = bid.getH();
+
+        return bidW == null || bidW > maxSize.getW()
+                || bidH == null || bidH > maxSize.getH();
+    }
+
+    private static Format maxSizeForBanner(Bid bid, BidRequest bidRequest) throws ValidationException {
+        int maxW = 0;
+        int maxH = 0;
+        for (final Format size : bannerFormats(bid, bidRequest)) {
+            maxW = Math.max(0, size.getW());
+            maxH = Math.max(0, size.getH());
+        }
+
+        return Format.builder().w(maxW).h(maxH).build();
+    }
+
+    private static List bannerFormats(Bid bid, BidRequest bidRequest) throws ValidationException {
+        final Imp imp = findCorrespondingImp(bidRequest, bid);
+        final Banner banner = imp.getBanner();
+
+        return ListUtils.emptyIfNull(banner != null ? banner.getFormat() : null);
+    }
+
+    private List validateSecureMarkup(BidderBid bidderBid,
+                                              String bidder,
+                                              AuctionContext auctionContext,
+                                              BidderAliases aliases) throws ValidationException {
+
+        if (secureMarkupEnforcement == BidValidationEnforcement.skip) {
+            return Collections.emptyList();
+        }
+
+        final Bid bid = bidderBid.getBid();
+        final Imp imp = findCorrespondingImp(auctionContext.getBidRequest(), bidderBid.getBid());
+
+        if (isImpSecure(imp) && markupIsNotSecure(bid)) {
+            return singleWarningOrValidationException(
+                    secureMarkupEnforcement,
+                    metricName -> metrics.updateSecureValidationMetrics(
+                            aliases.resolveBidder(bidder), auctionContext.getAccount().getId(), metricName),
+                    "Bid \"%s\" has insecure creative but should be in secure context",
+                    bid.getId());
+        }
+
+        return Collections.emptyList();
+    }
+
+    private static Imp findCorrespondingImp(BidRequest bidRequest, Bid bid) throws ValidationException {
+        return bidRequest.getImp().stream()
+                .filter(curImp -> Objects.equals(curImp.getId(), bid.getImpid()))
+                .findFirst()
+                .orElseThrow(() -> new ValidationException(
+                        "Bid \"%s\" has no corresponding imp in request", bid.getId()));
+    }
+
+    public static boolean isImpSecure(Imp imp) {
+        return Objects.equals(imp.getSecure(), 1);
+    }
+
+    private static boolean markupIsNotSecure(Bid bid) {
+        final String adm = bid.getAdm();
+
+        return StringUtils.containsAny(adm, INSECURE_MARKUP_MARKERS)
+                || !StringUtils.containsAny(adm, SECURE_MARKUP_MARKERS);
+    }
+
+    private static List singleWarningOrValidationException(BidValidationEnforcement enforcement,
+                                                                   Consumer metricsRecorder,
+                                                                   String message,
+                                                                   Object... args) throws ValidationException {
+
+        switch (enforcement) {
+            case enforce:
+                metricsRecorder.accept(MetricName.err);
+                throw new ValidationException(message, args);
+            case warn:
+                metricsRecorder.accept(MetricName.warn);
+                return Collections.singletonList(String.format(message, args));
+            default:
+                throw new IllegalStateException(String.format("Unexpected enforcement: %s", enforcement));
+        }
+    }
 }
diff --git a/src/main/java/org/prebid/server/validation/model/ValidationResult.java b/src/main/java/org/prebid/server/validation/model/ValidationResult.java
index af0f9c13f0d..9b5fc66eee4 100644
--- a/src/main/java/org/prebid/server/validation/model/ValidationResult.java
+++ b/src/main/java/org/prebid/server/validation/model/ValidationResult.java
@@ -1,26 +1,38 @@
 package org.prebid.server.validation.model;
 
-import lombok.AllArgsConstructor;
 import lombok.Value;
 
 import java.util.Collections;
 import java.util.List;
 
-@AllArgsConstructor
 @Value
 public class ValidationResult {
 
+    List warnings;
+
     List errors;
 
+    public boolean hasWarnings() {
+        return !warnings.isEmpty();
+    }
+
     public boolean hasErrors() {
         return !errors.isEmpty();
     }
 
     public static ValidationResult error(String errorMessageFormat, Object... args) {
-        return new ValidationResult(Collections.singletonList(String.format(errorMessageFormat, args)));
+        return error(Collections.emptyList(), errorMessageFormat, args);
+    }
+
+    public static ValidationResult error(List warnings, String errorMessageFormat, Object... args) {
+        return new ValidationResult(warnings, Collections.singletonList(String.format(errorMessageFormat, args)));
     }
 
     public static ValidationResult success() {
-        return new ValidationResult(Collections.emptyList());
+        return success(Collections.emptyList());
+    }
+
+    public static ValidationResult success(List warnings) {
+        return new ValidationResult(warnings, Collections.emptyList());
     }
 }
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 1e948d962dd..3f6b4683ff5 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -82,12 +82,17 @@ auction:
     log-failure-only: false
     log-sampling-rate: 0.0
   max-request-size: 262144
-  id-generator-type: uuid
+  generate-source-tid: true
   generate-bid-id: false
   cache:
     expected-request-time-ms: 10
     only-winning-bids: false
+  validations:
+    banner-creative-max-size: skip
+    secure-markup: skip
+  host-schain-node:
 video:
+  stored-request-required: false
   stored-requests-timeout-ms: 90
 amp:
   default-timeout-ms: 900
@@ -113,6 +118,7 @@ currency-converter:
     url: https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json
     default-timeout-ms: 4000
     refresh-period-ms: 900000
+    stale-after-ms: 259200000
 metrics:
   metricType: flushingCounter
   accounts:
@@ -142,11 +148,11 @@ gdpr:
   vendorlist:
     default-timeout-ms: 2000
     v1:
-      http-endpoint-template: https://vendorlist.consensu.org/v-{VERSION}/vendorlist.json
+      http-endpoint-template: https://vendor-list.consensu.org/v-{VERSION}/vendorlist.json
       refresh-missing-list-period-ms: 3600000
       deprecated: false
     v2:
-      http-endpoint-template: https://vendorlist.consensu.org/v2/archives/vendor-list-v{VERSION}.json
+      http-endpoint-template: https://vendor-list.consensu.org/v2/archives/vendor-list-v{VERSION}.json
       refresh-missing-list-period-ms: 3600000
       deprecated: false
   purposes:
@@ -188,6 +194,8 @@ gdpr:
   purpose-one-treatment-interpretation: ignore
 ccpa:
   enforce: true
+lmt:
+  enforce: true
 geolocation:
   enabled: false
   type: maxmind
diff --git a/src/main/resources/bidder-config/adkerneladn.yaml b/src/main/resources/bidder-config/adkerneladn.yaml
index 72a9b04857d..2c0f93c7c66 100644
--- a/src/main/resources/bidder-config/adkerneladn.yaml
+++ b/src/main/resources/bidder-config/adkerneladn.yaml
@@ -1,7 +1,7 @@
 adapters:
   adkerneladn:
     enabled: false
-    endpoint: http://tag.adkernel.com/rtbpub?account=
+    endpoint: http://{{Host}}/rtbpub?account={{PublisherID}}
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
diff --git a/src/main/resources/bidder-config/admixer.yaml b/src/main/resources/bidder-config/admixer.yaml
index 82cf3b92552..244dbccdcb6 100644
--- a/src/main/resources/bidder-config/admixer.yaml
+++ b/src/main/resources/bidder-config/admixer.yaml
@@ -20,7 +20,7 @@ adapters:
         - native
         - audio
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 511
     usersync:
       url: https://inv-nets.admixer.net/adxcm.aspx?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir=1&rurl=
       redirect-url: /setuid?bidder=admixer&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$$visitor_cookie$$
diff --git a/src/main/resources/bidder-config/adoppler.yaml b/src/main/resources/bidder-config/adoppler.yaml
index ad9492bf19d..203ae18907e 100644
--- a/src/main/resources/bidder-config/adoppler.yaml
+++ b/src/main/resources/bidder-config/adoppler.yaml
@@ -1,7 +1,7 @@
 adapters:
   adoppler:
     enabled: false
-    endpoint: http://app.trustedmarketplace.io/ads
+    endpoint: http://{{AccountID}}.trustedmarketplace.io/ads/processHeaderBid/{{AdUnit}}
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
@@ -22,4 +22,4 @@ adapters:
       redirect-url:
       cookie-family-name: adoppler
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/adpone.yaml b/src/main/resources/bidder-config/adpone.yaml
index ab182145225..c34a33e49a4 100644
--- a/src/main/resources/bidder-config/adpone.yaml
+++ b/src/main/resources/bidder-config/adpone.yaml
@@ -14,7 +14,7 @@ adapters:
       site-media-types:
         - banner
       supported-vendors:
-      vendor-id: 16
+      vendor-id: 799
     usersync:
       url: https://usersync.adpone.com/csync?redir=
       redirect-url: /setuid?bidder=adpone&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid={uid}
diff --git a/src/main/resources/bidder-config/adprime.yaml b/src/main/resources/bidder-config/adprime.yaml
index 94963141bd6..5ac2c4c0eca 100644
--- a/src/main/resources/bidder-config/adprime.yaml
+++ b/src/main/resources/bidder-config/adprime.yaml
@@ -8,7 +8,7 @@ adapters:
     deprecated-names:
     aliases:
     meta-info:
-      maintainer-email: rafal@adprime.com
+      maintainer-email: prebid@adprime.com
       app-media-types:
         - banner
         - video
@@ -16,7 +16,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 104
+      vendor-id: 0
     usersync:
       url:
       redirect-url:
diff --git a/src/main/resources/bidder-config/adtarget.yaml b/src/main/resources/bidder-config/adtarget.yaml
index 03cf31f8010..82df7f78e53 100644
--- a/src/main/resources/bidder-config/adtarget.yaml
+++ b/src/main/resources/bidder-config/adtarget.yaml
@@ -16,10 +16,10 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 779
     usersync:
       url: https://sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir=
       redirect-url: /setuid?bidder=adtarget&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid={uid}
       cookie-family-name: adtarget
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/adtelligent.yaml b/src/main/resources/bidder-config/adtelligent.yaml
index 5eff25f26ac..c43f508cee2 100644
--- a/src/main/resources/bidder-config/adtelligent.yaml
+++ b/src/main/resources/bidder-config/adtelligent.yaml
@@ -16,7 +16,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 410
     usersync:
       url: https://sync.adtelligent.com/csync?t=p&ep=0&gdpr={{gdpr}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir=
       redirect-url: /setuid?bidder=adtelligent&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid={uid}
diff --git a/src/main/resources/bidder-config/amx.yaml b/src/main/resources/bidder-config/amx.yaml
new file mode 100644
index 00000000000..2b59825225b
--- /dev/null
+++ b/src/main/resources/bidder-config/amx.yaml
@@ -0,0 +1,25 @@
+adapters:
+  amx:
+    enabled: false
+    endpoint: http://pbs.amxrtb.com/auction/openrtb
+    pbs-enforces-gdpr: true
+    pbs-enforces-ccpa: true
+    modifying-vast-xml-allowed: true
+    deprecated-names:
+    aliases:
+    meta-info:
+      maintainer-email: prebid@amxrtb.com
+      app-media-types:
+        - banner
+        - video
+      site-media-types:
+        - banner
+        - video
+      supported-vendors:
+      vendor-id: 0
+    usersync:
+      url: https://prebid.a-mo.net/cchain/0?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&cb=
+      redirect-url: /setuid?bidder=amx&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=
+      cookie-family-name: amx
+      type: redirect
+      support-cors: false
diff --git a/src/main/resources/bidder-config/conversant.yaml b/src/main/resources/bidder-config/conversant.yaml
index dd6ed57d493..ac2caabe8ce 100644
--- a/src/main/resources/bidder-config/conversant.yaml
+++ b/src/main/resources/bidder-config/conversant.yaml
@@ -5,6 +5,7 @@ adapters:
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
+    generate-bid-id: true
     deprecated-names:
     aliases:
     meta-info:
diff --git a/src/main/resources/bidder-config/datablocks.yaml b/src/main/resources/bidder-config/datablocks.yaml
index 944b61dd63b..e97fecb40b5 100644
--- a/src/main/resources/bidder-config/datablocks.yaml
+++ b/src/main/resources/bidder-config/datablocks.yaml
@@ -18,10 +18,10 @@ adapters:
         - video
         - native
       supported-vendors:
-      vendor-id: 14
+      vendor-id: 0
     usersync:
       url: https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&r=
       redirect-url: /setuid?bidder=datablocks&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=${uid}
       cookie-family-name: datablocks
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/deepintent.yaml b/src/main/resources/bidder-config/deepintent.yaml
index 4f5896f1bb7..b140708beb3 100644
--- a/src/main/resources/bidder-config/deepintent.yaml
+++ b/src/main/resources/bidder-config/deepintent.yaml
@@ -11,16 +11,13 @@ adapters:
       maintainer-email: sourabh@deepintent.com
       app-media-types:
         - banner
-        - video
       site-media-types:
         - banner
-        - video
       supported-vendors:
       vendor-id: 541
     usersync:
-      url: https://cdn.deepintent.com/syncpixel.html?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir=
-      sd:
-      redirect-url: /setuid?bidder=deepintent&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=[uid]
+      url: https://match.deepintent.com/usersync/136?id=unk&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir=
+      redirect-url: /setuid?bidder=deepintent&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=[UID]
       cookie-family-name: deepintent
-      type: redirect
+      type: iframe
       support-cors: false
diff --git a/src/main/resources/bidder-config/emxdigital.yaml b/src/main/resources/bidder-config/emxdigital.yaml
index 3a2b11f7291..47434023da3 100644
--- a/src/main/resources/bidder-config/emxdigital.yaml
+++ b/src/main/resources/bidder-config/emxdigital.yaml
@@ -10,8 +10,11 @@ adapters:
     meta-info:
       maintainer-email: adops@emxdigital.com
       app-media-types:
+        - banner
+        - video
       site-media-types:
         - banner
+        - video
       supported-vendors:
       vendor-id: 183
     usersync:
diff --git a/src/main/resources/bidder-config/ix.yaml b/src/main/resources/bidder-config/ix.yaml
index 6bf899e5284..2c8d2a458e8 100644
--- a/src/main/resources/bidder-config/ix.yaml
+++ b/src/main/resources/bidder-config/ix.yaml
@@ -10,8 +10,15 @@ adapters:
     meta-info:
       maintainer-email: pdu-supply-prebid@indexexchange.com
       app-media-types:
+        - banner
+        - video
+        - native
+        - audio
       site-media-types:
         - banner
+        - video
+        - native
+        - audio
       supported-vendors:
       vendor-id: 10
     usersync:
diff --git a/src/main/resources/bidder-config/kubient.yaml b/src/main/resources/bidder-config/kubient.yaml
index dd602011e69..b8856f0ff40 100644
--- a/src/main/resources/bidder-config/kubient.yaml
+++ b/src/main/resources/bidder-config/kubient.yaml
@@ -8,7 +8,7 @@ adapters:
     deprecated-names:
     aliases:
     meta-info:
-      maintainer-email: support@kubient.com
+      maintainer-email: prebid@kubient.com
       app-media-types:
         - banner
         - video
@@ -16,7 +16,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 794
     usersync:
       url:
       redirect-url:
diff --git a/src/main/resources/bidder-config/logicad.yaml b/src/main/resources/bidder-config/logicad.yaml
index 9761a40a805..659e8a9be15 100644
--- a/src/main/resources/bidder-config/logicad.yaml
+++ b/src/main/resources/bidder-config/logicad.yaml
@@ -14,7 +14,7 @@ adapters:
       site-media-types:
         - banner
       supported-vendors:
-      vendor-id: 14
+      vendor-id: 0
     usersync:
       url: https://cr-p31.ladsp.jp/cookiesender/31?r=true&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ru=
       redirect-url: /setuid?bidder=logicad&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$UID
diff --git a/src/main/resources/bidder-config/marsmedia.yaml b/src/main/resources/bidder-config/marsmedia.yaml
index d6098c1cff8..6bcff853d1b 100644
--- a/src/main/resources/bidder-config/marsmedia.yaml
+++ b/src/main/resources/bidder-config/marsmedia.yaml
@@ -16,10 +16,10 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 776
     usersync:
       url: https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect=
       redirect-url: /setuid?bidder=marsmedia&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=${UUID}
       cookie-family-name: marsmedia
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/mobilefuse.yaml b/src/main/resources/bidder-config/mobilefuse.yaml
index f612bda31e6..2f47ae1796c 100644
--- a/src/main/resources/bidder-config/mobilefuse.yaml
+++ b/src/main/resources/bidder-config/mobilefuse.yaml
@@ -1,7 +1,7 @@
 adapters:
   mobilefuse:
     enabled: false
-    endpoint: http://mfx-us-east.mobilefuse.com/openrtb?pub_id=
+    endpoint: http://mfx.mobilefuse.com/openrtb?pub_id=
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
@@ -14,10 +14,10 @@ adapters:
         - video
       site-media-types:
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 909
     usersync:
       url:
       redirect-url:
       cookie-family-name: mobilefuse
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/nanointeractive.yaml b/src/main/resources/bidder-config/nanointeractive.yaml
index 04543d81ac9..88099ec6440 100644
--- a/src/main/resources/bidder-config/nanointeractive.yaml
+++ b/src/main/resources/bidder-config/nanointeractive.yaml
@@ -14,10 +14,10 @@ adapters:
       site-media-types:
         - banner
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 72
     usersync:
       url: https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{gdpr}}&consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirectUri=
       redirect-url: /setuid?bidder=nanointeractive&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$UID
       cookie-family-name: nanointeractive
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/nobid.yaml b/src/main/resources/bidder-config/nobid.yaml
new file mode 100644
index 00000000000..2312d7f3469
--- /dev/null
+++ b/src/main/resources/bidder-config/nobid.yaml
@@ -0,0 +1,25 @@
+adapters:
+  nobid:
+    enabled: false
+    endpoint: https://ads.servenobid.com/ortb_adreq?tek=pbs&ver=1
+    pbs-enforces-gdpr: true
+    pbs-enforces-ccpa: true
+    modifying-vast-xml-allowed: true
+    deprecated-names:
+    aliases:
+    meta-info:
+      maintainer-email: developers@nobid.io
+      app-media-types:
+        - banner
+        - video
+      site-media-types:
+        - banner
+        - video
+      supported-vendors:
+      vendor-id: 816
+    usersync:
+      url: https://ads.servenobid.com/getsync?tek=pbs&ver=1&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect=
+      redirect-url: /setuid?bidder=nobid&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$UID
+      cookie-family-name: nobid
+      type: redirect
+      support-cors: false
diff --git a/src/main/resources/bidder-config/pubnative.yaml b/src/main/resources/bidder-config/pubnative.yaml
index 3d5fab4bad2..b7c8c0cc7bc 100644
--- a/src/main/resources/bidder-config/pubnative.yaml
+++ b/src/main/resources/bidder-config/pubnative.yaml
@@ -18,10 +18,10 @@ adapters:
         - video
         - native
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 512
     usersync:
       url:
       redirect-url:
       cookie-family-name: pubnative
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/silvermob.yaml b/src/main/resources/bidder-config/silvermob.yaml
new file mode 100644
index 00000000000..a5a3738e2e5
--- /dev/null
+++ b/src/main/resources/bidder-config/silvermob.yaml
@@ -0,0 +1,24 @@
+adapters:
+  silvermob:
+    enabled: false
+    endpoint: http://{{Host}}.silvermob.com/marketplace/api/dsp/bid/{{ZoneID}}
+    pbs-enforces-gdpr: true
+    pbs-enforces-ccpa: true
+    modifying-vast-xml-allowed: true
+    deprecated-names:
+    aliases:
+    meta-info:
+      maintainer-email: support@silvermob.com
+      app-media-types:
+        - banner
+        - video
+        - native
+      site-media-types:
+      supported-vendors:
+      vendor-id: 0
+    usersync:
+      url:
+      redirect-url:
+      cookie-family-name: silvermob
+      type: redirect
+      support-cors: false
diff --git a/src/main/resources/bidder-config/smaato.yaml b/src/main/resources/bidder-config/smaato.yaml
index bfacf24c294..1600a328546 100644
--- a/src/main/resources/bidder-config/smaato.yaml
+++ b/src/main/resources/bidder-config/smaato.yaml
@@ -16,10 +16,10 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 82
     usersync:
-      url:
-      redirect-url:
+      url: https://s.ad.smaato.net/c/?adExInit=n&redir=
+      redirect-url: /setuid?bidder=smaato&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$UID
       cookie-family-name: smaato
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/smartadserver.yaml b/src/main/resources/bidder-config/smartadserver.yaml
index a16630a94fa..00674a88e22 100644
--- a/src/main/resources/bidder-config/smartadserver.yaml
+++ b/src/main/resources/bidder-config/smartadserver.yaml
@@ -16,7 +16,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 328
+      vendor-id: 45
     usersync:
       url: https://ssbsync-global.smartadserver.com/api/sync?callerId=5&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirectUri=
       redirect-url: /setuid?bidder=smartadserver&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=[ssb_sync_pid]
diff --git a/src/main/resources/bidder-config/tappx.yaml b/src/main/resources/bidder-config/tappx.yaml
index f0e3541dd0a..ba6019409ce 100644
--- a/src/main/resources/bidder-config/tappx.yaml
+++ b/src/main/resources/bidder-config/tappx.yaml
@@ -14,7 +14,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 628
     usersync:
       url:
       redirect-url:
diff --git a/src/main/resources/bidder-config/ttx.yaml b/src/main/resources/bidder-config/ttx.yaml
index 53b5dc71c91..c57ac2f027d 100644
--- a/src/main/resources/bidder-config/ttx.yaml
+++ b/src/main/resources/bidder-config/ttx.yaml
@@ -1,6 +1,7 @@
 adapters:
   ttx:
     enabled: false
+    # Old http://ssc.33across.com/api/v1/hb
     endpoint: https://ssc.33across.com/api/v1/s2s
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
@@ -10,9 +11,9 @@ adapters:
     meta-info:
       maintainer-email: headerbidding@33across.com
       app-media-types:
-        - banner
       site-media-types:
         - banner
+        - video
       supported-vendors:
       vendor-id: 58
     usersync:
diff --git a/src/main/resources/bidder-config/ucfunnel.yaml b/src/main/resources/bidder-config/ucfunnel.yaml
index 98f2309600e..acec14ae741 100644
--- a/src/main/resources/bidder-config/ucfunnel.yaml
+++ b/src/main/resources/bidder-config/ucfunnel.yaml
@@ -1,7 +1,7 @@
 adapters:
   ucfunnel:
     enabled: false
-    endpoint: http://pbs.aralego.com/prebid
+    endpoint: https://pbs.aralego.com/prebid
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
@@ -16,7 +16,7 @@ adapters:
         - banner
         - video
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 607
     usersync:
       url: https://sync.aralego.com/idsync?gdpr={{gdpr}}}&gdpr_consent={{gdpr_consent}}&usprivacy={{us_privacy}}&redirect=
       redirect-url: /setuid?bidder=ucfunnel&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=SspCookieUserId
diff --git a/src/main/resources/bidder-config/verizonmedia.yaml b/src/main/resources/bidder-config/verizonmedia.yaml
index f8cdbef1ef0..784c33c963f 100644
--- a/src/main/resources/bidder-config/verizonmedia.yaml
+++ b/src/main/resources/bidder-config/verizonmedia.yaml
@@ -1,7 +1,7 @@
 adapters:
   verizonmedia:
     enabled: false
-    endpoint: https://
+    endpoint: https://s2shb.ssp.yahoo.com/admax/bid/partners/MAG
     pbs-enforces-gdpr: true
     pbs-enforces-ccpa: true
     modifying-vast-xml-allowed: true
@@ -10,13 +10,14 @@ adapters:
     meta-info:
       maintainer-email: dsp-supply-prebid@verizonmedia.com
       app-media-types:
+        - banner
       site-media-types:
         - banner
       supported-vendors:
       vendor-id: 25
     usersync:
-      url:
+      url: https://ups.analytics.yahoo.com/ups/58401/sync?redir=true&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}
       redirect-url:
       cookie-family-name: verizonmedia
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/vrtcal.yaml b/src/main/resources/bidder-config/vrtcal.yaml
index 1ad90ba09e6..2a632b7b440 100644
--- a/src/main/resources/bidder-config/vrtcal.yaml
+++ b/src/main/resources/bidder-config/vrtcal.yaml
@@ -13,10 +13,10 @@ adapters:
         - banner
       site-media-types:
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 706
     usersync:
       url:
       redirect-url:
       cookie-family-name: vrtcal
       type: redirect
-      support-cors: false
\ No newline at end of file
+      support-cors: false
diff --git a/src/main/resources/bidder-config/yeahmobi.yaml b/src/main/resources/bidder-config/yeahmobi.yaml
index 4e1a9eb6023..4c982fd50b4 100644
--- a/src/main/resources/bidder-config/yeahmobi.yaml
+++ b/src/main/resources/bidder-config/yeahmobi.yaml
@@ -15,7 +15,7 @@ adapters:
         - native
       site-media-types:
       supported-vendors:
-      vendor-id: 0
+      vendor-id: 803
     usersync:
       url:
       redirect-url:
diff --git a/src/main/resources/static/bidder-params/adoppler.json b/src/main/resources/static/bidder-params/adoppler.json
index 7bf55ec1250..eaa4e6df80e 100644
--- a/src/main/resources/static/bidder-params/adoppler.json
+++ b/src/main/resources/static/bidder-params/adoppler.json
@@ -7,6 +7,10 @@
     "adunit": {
       "type": "string",
       "description": "AdUnit to bid against to."
+    },
+    "client": {
+      "type": "string",
+      "description": "Client name."
     }
   },
   "required": ["adunit"]
diff --git a/src/main/resources/static/bidder-params/amx.json b/src/main/resources/static/bidder-params/amx.json
new file mode 100644
index 00000000000..f9b1b26b3db
--- /dev/null
+++ b/src/main/resources/static/bidder-params/amx.json
@@ -0,0 +1,16 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "AMX RTB Adapter Params",
+  "description": "A schema to validate params accepted by the AMX adapter",
+  "type": "object",
+  "properties": {
+    "tagId" : {
+      "type": "string",
+      "description": "Set a tagId (overrides site.publisher.id, or app.publisher.id)"
+    },
+    "adUnitId": {
+      "type": "string",
+      "description": "Override imp.tagid value to provide a custom value in AMX ad unit ID reporting"
+    }
+  }
+}
diff --git a/src/main/resources/static/bidder-params/conversant.json b/src/main/resources/static/bidder-params/conversant.json
index 1154a4a998f..947a38b5be6 100644
--- a/src/main/resources/static/bidder-params/conversant.json
+++ b/src/main/resources/static/bidder-params/conversant.json
@@ -24,10 +24,6 @@
       "type": "integer",
       "description": "Ad position on screen."
     },
-    "mobile": {
-      "type": "integer",
-      "description": "Indicate if the site is mobile optimized."
-    },
     "mimes": {
       "type": "array",
       "description": "Array of content MIME types.  For videos only.",
@@ -56,4 +52,4 @@
   },
   "required": [
   ]
-}
\ No newline at end of file
+}
diff --git a/src/main/resources/static/bidder-params/deepintent.json b/src/main/resources/static/bidder-params/deepintent.json
index fbb607a346b..772938805f1 100644
--- a/src/main/resources/static/bidder-params/deepintent.json
+++ b/src/main/resources/static/bidder-params/deepintent.json
@@ -5,10 +5,10 @@
 
   "type": "object",
   "properties": {
-    "TagID": {
+    "tagId": {
       "type": "string",
       "description": "An ID which identifies the deepintent ad tag"
     }
   },
-  "required" : [ "TagID" ]
+  "required" : [ "tagId" ]
 }
diff --git a/src/main/resources/static/bidder-params/ix.json b/src/main/resources/static/bidder-params/ix.json
index 7ed56deef46..da22db69803 100644
--- a/src/main/resources/static/bidder-params/ix.json
+++ b/src/main/resources/static/bidder-params/ix.json
@@ -7,6 +7,15 @@
     "siteId": {
       "type": "string",
       "description": "An ID which identifies the site selling the impression"
+    },
+    "size": {
+      "type": "array",
+      "items": {
+        "type": "integer"
+      },
+      "minItems": 2,
+      "maxItems": 2,
+      "description": "An array of two integer containing the dimension"
     }
   },
   "required": [
diff --git a/src/main/resources/static/bidder-params/nobid.json b/src/main/resources/static/bidder-params/nobid.json
new file mode 100644
index 00000000000..10c2f248d38
--- /dev/null
+++ b/src/main/resources/static/bidder-params/nobid.json
@@ -0,0 +1,17 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "NoBid Adapter Params",
+  "description": "A schema which validates params accepted by the NoBid adapter",
+
+  "type": "object",
+  "properties": {
+    "siteId": {
+      "type": "integer",
+      "description": "A Required ID which identifies the NoBid site. The siteId paramerter is provided by your NoBid account manager."
+    }, "placementId": {
+      "type": "integer",
+      "description": "An oprional ID which identifies an adunit in a site. The placementId paramerter is provided by your NoBid account manager."
+    }
+  },
+  "required": ["siteId"]
+}
diff --git a/src/main/resources/static/bidder-params/silvermob.json b/src/main/resources/static/bidder-params/silvermob.json
new file mode 100644
index 00000000000..0ded8714cb5
--- /dev/null
+++ b/src/main/resources/static/bidder-params/silvermob.json
@@ -0,0 +1,17 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "SilverMob Adapter Params",
+  "description": "A schema which validates params accepted by the SilverMob adapter",
+  "type": "object",
+  "properties": {
+    "zoneid": {
+      "type": "string",
+      "description": "Zone ID"
+    },
+    "host": {
+      "type": "string",
+      "description": "Host"
+    }
+  },
+  "required": ["zoneid", "host"]
+}
diff --git a/src/test/java/org/prebid/server/auction/AmpRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/AmpRequestFactoryTest.java
index 8f3e7bb64b6..6b5889f6d28 100644
--- a/src/test/java/org/prebid/server/auction/AmpRequestFactoryTest.java
+++ b/src/test/java/org/prebid/server/auction/AmpRequestFactoryTest.java
@@ -26,6 +26,7 @@
 import org.prebid.server.auction.model.AuctionContext;
 import org.prebid.server.exception.InvalidRequestException;
 import org.prebid.server.metric.MetricName;
+import org.prebid.server.proto.openrtb.ext.ExtIncludeBrandCategory;
 import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange;
 import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity;
 import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
@@ -278,10 +279,8 @@ public void shouldReturnBidRequestWithDefaultIncludeWinnersIfStoredBidRequestExt
                 .extracting(BidRequest::getExt).isNotNull()
                 .extracting(ExtRequest::getPrebid)
                 .extracting(ExtRequestPrebid::getTargeting)
-                .extracting(ExtRequestTargeting::getIncludewinners, ExtRequestTargeting::getPricegranularity)
-                // assert that includeWinners was set with default value and priceGranularity remained unchanged
-                .containsExactly(
-                        tuple(true, mapper.createObjectNode().put("foo", "bar")));
+                .extracting(ExtRequestTargeting::getIncludewinners)
+                .containsExactly(true);
     }
 
     @Test
@@ -308,6 +307,30 @@ public void shouldReturnBidRequestWithIncludeWinnersFromStoredBidRequest() {
                 .containsExactly(false);
     }
 
+    @Test
+    public void shouldReturnBidRequestWithIncludeFormatFromStoredBidRequest() {
+        // given
+        givenBidRequest(
+                builder -> builder
+                        .ext(givenRequestExt(
+                                ExtRequestTargeting.builder()
+                                        .pricegranularity(mapper.createObjectNode().put("foo", "bar"))
+                                        .includeformat(true)
+                                        .build())),
+                Imp.builder().build());
+
+        // when
+        final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest();
+
+        // then
+        assertThat(singletonList(request))
+                .extracting(BidRequest::getExt).isNotNull()
+                .extracting(ExtRequest::getPrebid)
+                .extracting(ExtRequestPrebid::getTargeting)
+                .extracting(ExtRequestTargeting::getIncludeformat)
+                .containsExactly(true);
+    }
+
     @Test
     public void shouldReturnBidRequestWithDefaultIncludeBidderKeysIfStoredRequestExtTargetingHasNoIncludeBidderKeys() {
         // given
@@ -376,6 +399,29 @@ public void shouldReturnBidRequestWithDefaultPriceGranularityIfStoredBidRequestE
                                 ExtGranularityRange.of(BigDecimal.valueOf(20), BigDecimal.valueOf(0.1)))))));
     }
 
+    @Test
+    public void shouldReturnBidRequestWithNotChangedExtRequestPrebidTargetingFields() {
+        // given
+        givenBidRequest(
+                builder -> builder
+                        .ext(givenRequestExt(ExtRequestTargeting.builder()
+                                .includebrandcategory(ExtIncludeBrandCategory.of(1, "publisher", true))
+                                .truncateattrchars(10)
+                                .build())),
+                Imp.builder().build());
+
+        // when
+        final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest();
+
+        // then
+        assertThat(singletonList(request))
+                .extracting(BidRequest::getExt).isNotNull()
+                .extracting(ExtRequest::getPrebid)
+                .extracting(ExtRequestPrebid::getTargeting)
+                .extracting(ExtRequestTargeting::getIncludebrandcategory, ExtRequestTargeting::getTruncateattrchars)
+                .containsOnly(tuple(ExtIncludeBrandCategory.of(1, "publisher", true), 10));
+    }
+
     private Answer answerWithFirstArgument() {
         return invocationOnMock -> invocationOnMock.getArguments()[0];
     }
@@ -537,14 +583,14 @@ public void shouldSetBidRequestSiteExt() {
     }
 
     @Test
-    public void shouldReturnBidRequestWithOverriddenSitePageByCurlParamValue() {
+    public void shouldReturnBidRequestWithOverriddenSitePageAndDomainByCurlParamValue() {
         // given
-        given(httpRequest.getParam("curl")).willReturn("overridden-site-page");
+        given(httpRequest.getParam("curl")).willReturn("http://overridden.site.page:8080/path");
 
         givenBidRequest(
                 builder -> builder
                         .ext(ExtRequest.empty())
-                        .site(Site.builder().page("will-be-overridden").build()),
+                        .site(Site.builder().page("http://will.be.overridden/path").build()),
                 Imp.builder().build());
 
         // when
@@ -553,14 +599,15 @@ public void shouldReturnBidRequestWithOverriddenSitePageByCurlParamValue() {
         // then
         assertThat(singletonList(request))
                 .extracting(BidRequest::getSite)
-                .extracting(Site::getPage, Site::getExt)
-                .containsOnly(tuple("overridden-site-page", ExtSite.of(1, null)));
+                .extracting(Site::getPage, Site::getDomain, Site::getExt)
+                .containsOnly(tuple(
+                        "http://overridden.site.page:8080/path", "overridden.site.page", ExtSite.of(1, null)));
     }
 
     @Test
-    public void shouldReturnBidRequestWithSitePageContainingCurlParamValueWhenSitePreviouslyNotExistInRequest() {
+    public void shouldReturnBidRequestWithSitePageAndDomainContainingCurlParamValueWhenSiteNotInRequest() {
         // given
-        given(httpRequest.getParam("curl")).willReturn("overridden-site-page");
+        given(httpRequest.getParam("curl")).willReturn("http://overridden.site.page:8080/path");
 
         givenBidRequest(
                 builder -> builder
@@ -574,8 +621,9 @@ public void shouldReturnBidRequestWithSitePageContainingCurlParamValueWhenSitePr
         // then
         assertThat(singletonList(request))
                 .extracting(BidRequest::getSite)
-                .extracting(Site::getPage, Site::getExt)
-                .containsOnly(tuple("overridden-site-page", ExtSite.of(1, null)));
+                .extracting(Site::getPage, Site::getDomain, Site::getExt)
+                .containsOnly(tuple(
+                        "http://overridden.site.page:8080/path", "overridden.site.page", ExtSite.of(1, null)));
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java
index 2a78f3a8cc7..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;
@@ -26,7 +27,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.prebid.server.VertxTest;
-import org.prebid.server.assertion.FutureAssertion;
 import org.prebid.server.auction.model.AuctionContext;
 import org.prebid.server.auction.model.IpAddress;
 import org.prebid.server.bidder.BidderCatalog;
@@ -48,6 +48,7 @@
 import org.prebid.server.privacy.gdpr.model.TcfContext;
 import org.prebid.server.privacy.model.Privacy;
 import org.prebid.server.privacy.model.PrivacyContext;
+import org.prebid.server.proto.openrtb.ext.ExtIncludeBrandCategory;
 import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange;
 import org.prebid.server.proto.openrtb.ext.request.ExtMediaTypePriceGranularity;
 import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity;
@@ -61,10 +62,9 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
 import org.prebid.server.proto.openrtb.ext.request.ExtSite;
-import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.settings.ApplicationSettings;
 import org.prebid.server.settings.model.Account;
+import org.prebid.server.settings.model.AccountStatus;
 import org.prebid.server.validation.RequestValidator;
 import org.prebid.server.validation.model.ValidationResult;
 
@@ -77,6 +77,7 @@
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
+import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
 import static org.apache.commons.lang3.StringUtils.EMPTY;
@@ -91,6 +92,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.prebid.server.assertion.FutureAssertion.assertThat;
 
 public class AuctionRequestFactoryTest extends VertxTest {
 
@@ -144,7 +146,7 @@ public void setUp() {
         given(timeoutResolver.resolve(any())).willReturn(2000L);
         given(timeoutResolver.adjustTimeout(anyLong())).willReturn(1900L);
 
-        given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any()))
+        given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any(), any()))
                 .willReturn(Future.succeededFuture(PrivacyContext.of(
                         Privacy.of("0", EMPTY, Ccpa.EMPTY, 0),
                         TcfContext.empty())));
@@ -274,6 +276,32 @@ public void shouldReturnFailedFutureIfAccountIsEnforcedAndFailedGetAccountById()
                 .hasMessage("Unauthorized account id: absentId");
     }
 
+    @Test
+    public void shouldReturnFailedFutureIfAccountIsInactive() {
+        // given
+        given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(Account.builder()
+                .id("accountId")
+                .status(AccountStatus.inactive)
+                .build()));
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .app(App.builder()
+                        .publisher(Publisher.builder().id("accountId").build())
+                        .build())
+                .build();
+
+        givenBidRequest(bidRequest);
+
+        // when
+        final Future future = factory.fromRequest(routingContext, 0L);
+
+        // then
+        assertThat(future).isFailed();
+        assertThat(future.cause())
+                .isInstanceOf(UnauthorizedAccountException.class)
+                .hasMessage("Account accountId is inactive");
+    }
+
     @Test
     public void shouldReturnFailedFutureIfRequestBodyExceedsMaxRequestSize() {
         // given
@@ -529,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);
 
@@ -556,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
@@ -626,9 +749,7 @@ public void shouldNotSetSitePageIfNoReferer() {
     @Test
     public void shouldNotSetSitePageIfDomainCouldNotBeDerived() {
         // given
-        givenBidRequest(BidRequest.builder()
-                .site(Site.builder().domain("home.com").build())
-                .build());
+        givenValidBidRequest();
 
         given(paramsExtractor.refererFrom(any())).willReturn("http://not-valid-site");
         given(paramsExtractor.domainFrom(anyString())).willThrow(new PreBidException("Couldn't derive domain"));
@@ -638,7 +759,28 @@ public void shouldNotSetSitePageIfDomainCouldNotBeDerived() {
 
         // then
         assertThat(request.getSite()).isEqualTo(
-                Site.builder().domain("home.com").ext(ExtSite.of(0, null)).build());
+                Site.builder().ext(ExtSite.of(0, null)).build());
+    }
+
+    @Test
+    public void shouldSetDomainFromPageInsteadOfReferer() {
+        // given
+        givenBidRequest(BidRequest.builder()
+                .site(Site.builder().page("http://page.site.com/page1.html").build())
+                .build());
+
+        given(paramsExtractor.refererFrom(any())).willReturn("http://any-site/referer.html");
+        given(paramsExtractor.domainFrom(anyString())).willReturn("site.com");
+
+        // when
+        final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest();
+
+        // then
+        verify(paramsExtractor).domainFrom(eq("http://page.site.com/page1.html"));
+
+        assertThat(singleton(request.getSite()))
+                .extracting(Site::getPage, Site::getDomain)
+                .containsOnly(tuple("http://page.site.com/page1.html", "site.com"));
     }
 
     @Test
@@ -694,27 +836,6 @@ public void shouldSetSiteExtAmpIfNoReferer() {
                         .ext(ExtSite.of(0, null)).build());
     }
 
-    @Test
-    public void shouldSetUserExtDigitrustPerfIfNotDefined() {
-        // given
-        givenBidRequest(BidRequest.builder()
-                .user(User.builder()
-                        .ext(ExtUser.builder()
-                                .digitrust(ExtUserDigiTrust.of("id", 123, null))
-                                .build())
-                        .build())
-                .build());
-
-        // when
-        final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest();
-
-        // then
-        assertThat(request.getUser().getExt())
-                .isEqualTo(ExtUser.builder()
-                        .digitrust(ExtUserDigiTrust.of("id", 123, 0))
-                        .build());
-    }
-
     @Test
     public void shouldSetSourceTidIfNotDefined() {
         // given
@@ -1136,6 +1257,31 @@ public void shouldNotChangeAnyOtherExtRequestPrebidCacheFields() {
                 .containsOnly(tuple(cacheBids, cacheVastxml));
     }
 
+    @Test
+    public void shouldNotChangeAnyOtherExtRequestPrebidTargetingFields() {
+        // given
+        givenBidRequest(BidRequest.builder()
+                .imp(singletonList(Imp.builder().ext(mapper.createObjectNode()).build()))
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .targeting(ExtRequestTargeting.builder()
+                                .includebrandcategory(ExtIncludeBrandCategory.of(1, "publisher", true))
+                                .truncateattrchars(10)
+                                .build())
+                        .build()))
+                .build());
+
+        // when
+        final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest();
+
+        // then
+        assertThat(singletonList(request))
+                .extracting(BidRequest::getExt)
+                .extracting(ExtRequest::getPrebid)
+                .extracting(ExtRequestPrebid::getTargeting)
+                .extracting(ExtRequestTargeting::getIncludebrandcategory, ExtRequestTargeting::getTruncateattrchars)
+                .containsOnly(tuple(ExtIncludeBrandCategory.of(1, "publisher", true), 10));
+    }
+
     @Test
     public void shouldSetCacheWinningonlyFromRequestWhenCacheWinningonlyIsPresent() {
         // given
@@ -1386,7 +1532,8 @@ public void shouldReturnFailedFutureIfRequestValidationFailed() {
         given(storedRequestProcessor.processStoredRequests(any(), any()))
                 .willReturn(Future.succeededFuture(BidRequest.builder().build()));
 
-        given(requestValidator.validate(any())).willReturn(new ValidationResult(asList("error1", "error2")));
+        given(requestValidator.validate(any()))
+                .willReturn(new ValidationResult(emptyList(), asList("error1", "error2")));
 
         // when
         final Future future = factory.fromRequest(routingContext, 0L);
@@ -1665,7 +1812,7 @@ public void shouldReturnAuctionContextWithWebRequestTypeMetric() {
         final Future auctionContextFuture = factory.fromRequest(routingContext, 0L);
 
         // then
-        FutureAssertion.assertThat(auctionContextFuture).isSucceeded();
+        assertThat(auctionContextFuture).isSucceeded();
         assertThat(auctionContextFuture.result().getRequestTypeMetric()).isEqualTo(MetricName.openrtb2web);
     }
 
@@ -1678,7 +1825,7 @@ public void shouldReturnAuctionContextWithAppRequestTypeMetric() {
         final Future auctionContextFuture = factory.fromRequest(routingContext, 0L);
 
         // then
-        FutureAssertion.assertThat(auctionContextFuture).isSucceeded();
+        assertThat(auctionContextFuture).isSucceeded();
         assertThat(auctionContextFuture.result().getRequestTypeMetric()).isEqualTo(MetricName.openrtb2app);
     }
 
@@ -1693,14 +1840,14 @@ public void shouldEnrichRequestWithIpAddressAndCountryAndSaveAuctionContext() {
                         .geoInfo(GeoInfo.builder().vendor("v").country("ua").build())
                         .build(),
                 "ip");
-        given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any()))
+        given(privacyEnforcementService.contextFromBidRequest(any(), any(), any(), any(), any()))
                 .willReturn(Future.succeededFuture(privacyContext));
 
         // when
         final Future auctionContextFuture = factory.fromRequest(routingContext, 0L);
 
         // then
-        FutureAssertion.assertThat(auctionContextFuture).isSucceeded();
+        assertThat(auctionContextFuture).isSucceeded();
 
         final AuctionContext auctionContext = auctionContextFuture.result();
         assertThat(auctionContext.getBidRequest().getDevice()).isEqualTo(
diff --git a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java
index 2bb19adfca4..b3ccd2076a7 100644
--- a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java
+++ b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java
@@ -41,6 +41,8 @@
 import org.prebid.server.events.EventsService;
 import org.prebid.server.execution.Timeout;
 import org.prebid.server.execution.TimeoutFactory;
+import org.prebid.server.identity.IdGenerator;
+import org.prebid.server.identity.IdGeneratorType;
 import org.prebid.server.proto.openrtb.ext.ExtPrebid;
 import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange;
 import org.prebid.server.proto.openrtb.ext.request.ExtImp;
@@ -73,14 +75,12 @@
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
@@ -121,6 +121,11 @@ public class BidResponseCreatorTest extends VertxTest {
     private EventsService eventsService;
     @Mock
     private StoredRequestProcessor storedRequestProcessor;
+    @Mock
+    private BidResponseReducer bidResponseReducer;
+    @Mock
+    private IdGenerator idGenerator;
+
     private Clock clock;
 
     private Timeout timeout;
@@ -132,9 +137,12 @@ public void setUp() {
         given(cacheService.getEndpointHost()).willReturn("testHost");
         given(cacheService.getEndpointPath()).willReturn("testPath");
         given(cacheService.getCachedAssetURLTemplate()).willReturn("uuid=");
+        given(bidResponseReducer.removeRedundantBids(any()))
+                .willAnswer(invocationOnMock -> invocationOnMock.getArgument(0));
 
         given(storedRequestProcessor.videoStoredDataResult(any(), anyList(), anyList(), any()))
                 .willReturn(Future.succeededFuture(VideoStoredDataResult.empty()));
+        given(idGenerator.getType()).willReturn(IdGeneratorType.none);
 
         clock = Clock.fixed(Instant.ofEpochMilli(1000L), ZoneOffset.UTC);
 
@@ -143,7 +151,8 @@ public void setUp() {
                 bidderCatalog,
                 eventsService,
                 storedRequestProcessor,
-                false,
+                bidResponseReducer,
+                idGenerator,
                 0,
                 clock,
                 jacksonMapper);
@@ -212,25 +221,31 @@ public void shouldRequestCacheServiceWithExpectedArguments() {
         bidResponseCreator.create(bidderResponses, auctionContext, cacheInfo, false);
 
         // then
-        Map> biddersToCacheBidIds = new HashMap<>();
-        biddersToCacheBidIds.put("bidder1", Arrays.asList("bidId1", "bidId2"));
-        biddersToCacheBidIds.put("bidder2", Arrays.asList("bidId3", "bidId4"));
+        ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class);
         verify(cacheService).cacheBidsOpenrtb(
                 argThat(t -> t.containsAll(asList(bid1, bid4, bid3, bid2))),
                 same(auctionContext),
-                eq(CacheContext.builder()
-                        .shouldCacheBids(true)
-                        .shouldCacheVideoBids(true)
-                        .cacheBidsTtl(99)
-                        .cacheVideoBidsTtl(101)
-                        .bidderToVideoBidIdsToModify(emptyMap())
-                        .bidderToBidIds(biddersToCacheBidIds)
-                        .build()),
+                contextArgumentCaptor.capture(),
                 eq(EventsContext.builder()
                         .enabledForAccount(true)
                         .enabledForRequest(true)
                         .auctionTimestamp(1000L)
                         .build()));
+
+        Map> biddersToCacheBidIds = doubleMap(
+                "bidder1", doubleMap("bidId1-impId1", "bidId1", "bidId2-impId2", "bidId2"),
+                "bidder2", doubleMap("bidId3-impId1", "bidId3", "bidId4-impId2", "bidId4"));
+
+        assertThat(contextArgumentCaptor.getValue())
+                .satisfies(context -> {
+                    assertThat(context.isShouldCacheBids()).isTrue();
+                    assertThat(context.isShouldCacheVideoBids()).isTrue();
+                    assertThat(context.getCacheBidsTtl()).isEqualTo(99);
+                    assertThat(context.getCacheVideoBidsTtl()).isEqualTo(101);
+                    assertThat(contextArgumentCaptor.getValue().getBidderToBidsToGeneratedIds().getBidderToBidIds())
+                            .containsAllEntriesOf(biddersToCacheBidIds);
+                    assertThat(context.getBidderToVideoGeneratedBidIdsToModify().getBidderToBidIds()).isEmpty();
+                });
     }
 
     @Test
@@ -260,17 +275,23 @@ public void shouldRequestCacheServiceWithWinningBidsOnlyWhenWinningonlyIsTrue()
         bidResponseCreator.create(bidderResponses, auctionContext, cacheInfo, false);
 
         // then
-        Map> biddersToCacheBidIds = new HashMap<>();
-        biddersToCacheBidIds.put("bidder1", Arrays.asList("bidId1", "bidId2"));
-        biddersToCacheBidIds.put("bidder2", Arrays.asList("bidId3", "bidId4"));
+        ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class);
         verify(cacheService).cacheBidsOpenrtb(
-                argThat(t -> t.containsAll(asList(bid1, bid2)) && t.size() == 2),
+                eq(asList(bid1, bid2)),
                 same(auctionContext),
-                eq(CacheContext.builder()
-                        .bidderToVideoBidIdsToModify(emptyMap())
-                        .bidderToBidIds(biddersToCacheBidIds)
-                        .build()),
+                contextArgumentCaptor.capture(),
                 eq(EventsContext.builder().auctionTimestamp(1000L).build()));
+
+        final Map> biddersToCacheBidIds = doubleMap(
+                "bidder1", doubleMap("bidId1-impId1", "bidId1", "bidId2-impId2", "bidId2"),
+                "bidder2", doubleMap("bidId3-impId1", "bidId3", "bidId4-impId2", "bidId4"));
+
+        assertThat(contextArgumentCaptor.getValue())
+                .satisfies(context -> {
+                    assertThat(contextArgumentCaptor.getValue().getBidderToBidsToGeneratedIds().getBidderToBidIds())
+                            .containsAllEntriesOf(biddersToCacheBidIds);
+                    assertThat(context.getBidderToVideoGeneratedBidIdsToModify().getBidderToBidIds()).isEmpty();
+                });
     }
 
     @Test
@@ -308,22 +329,30 @@ public void shouldRequestCacheServiceWithVideoBidsToModifyWhenEventsEnabledAndFo
         bidResponseCreator.create(bidderResponses, auctionContext, cacheInfo, false);
 
         // then
-        Map> biddersToCacheBidIds = new HashMap<>();
-        biddersToCacheBidIds.put("bidder1", Collections.singletonList("bidId1"));
-        biddersToCacheBidIds.put("bidder2", Collections.singletonList("bidId2"));
+        final Map> biddersToCacheBidIds = doubleMap(
+                "bidder1", singletonMap("bidId1-impId1", "bidId1"),
+                "bidder2", singletonMap("bidId2-impId2", "bidId2"));
+
+        final ArgumentCaptor cacheContextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class);
+
         verify(cacheService).cacheBidsOpenrtb(
                 argThat(t -> t.containsAll(asList(bid1, bid2))),
                 same(auctionContext),
-                eq(CacheContext.builder()
-                        .shouldCacheVideoBids(true)
-                        .bidderToVideoBidIdsToModify(singletonMap("bidder1", singletonList("bidId1")))
-                        .bidderToBidIds(biddersToCacheBidIds)
-                        .build()),
+                cacheContextArgumentCaptor.capture(),
                 eq(EventsContext.builder()
                         .enabledForAccount(true)
                         .enabledForRequest(true)
                         .auctionTimestamp(1000L)
                         .build()));
+
+        assertThat(cacheContextArgumentCaptor.getValue())
+                .satisfies(context -> {
+                    assertThat(context.getBidderToVideoGeneratedBidIdsToModify().getBidderToBidIds())
+                            .containsAllEntriesOf(singletonMap("bidder1", singletonMap("bidId1-impId1", "bidId1")));
+                    assertThat(context.getBidderToBidsToGeneratedIds().getBidderToBidIds())
+                            .containsAllEntriesOf(biddersToCacheBidIds);
+                    assertThat(context.isShouldCacheVideoBids()).isTrue();
+                });
     }
 
     @Test
@@ -343,14 +372,19 @@ public void shouldCallCacheServiceEvenRoundedCpmIsZero() {
         bidResponseCreator.create(bidderResponses, auctionContext, cacheInfo, false);
 
         // then
+        final ArgumentCaptor cacheContextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class);
         verify(cacheService).cacheBidsOpenrtb(
                 argThat(bids -> bids.contains(bid1)),
                 same(auctionContext),
-                eq(CacheContext.builder()
-                        .bidderToVideoBidIdsToModify(emptyMap())
-                        .bidderToBidIds(singletonMap("bidder1", Collections.singletonList("bidId1")))
-                        .build()),
+                cacheContextArgumentCaptor.capture(),
                 eq(EventsContext.builder().auctionTimestamp(1000L).build()));
+
+        assertThat(cacheContextArgumentCaptor.getValue())
+                .satisfies(context -> {
+                    assertThat(context.getBidderToVideoGeneratedBidIdsToModify().getBidderToBidIds()).isEmpty();
+                    assertThat(context.getBidderToBidsToGeneratedIds().getBidderToBidIds())
+                            .containsAllEntriesOf(singletonMap("bidder1", singletonMap("bidId1-impId1", "bidId1")));
+                });
     }
 
     @Test
@@ -418,7 +452,7 @@ public void shouldSetNbrNullAndPopulateSeatbidWhenAtLeastOneBidIsPresent() {
         // given
         final AuctionContext auctionContext = givenAuctionContext(givenBidRequest());
 
-        final Bid bid = Bid.builder().impid("imp1").build();
+        final Bid bid = Bid.builder().impid("imp1").id("bidId").build();
         final List bidderResponses = singletonList(BidderResponse.of("bidder1",
                 givenSeatBid(BidderBid.of(bid, null, null)), 100));
 
@@ -438,7 +472,7 @@ public void shouldSkipBidderResponsesWhereSeatBidContainEmptyBids() {
         // given
         final AuctionContext auctionContext = givenAuctionContext(givenBidRequest());
 
-        final Bid bid = Bid.builder().impid("imp1").build();
+        final Bid bid = Bid.builder().impid("imp1").id("bidId").build();
         final List bidderResponses = asList(
                 BidderResponse.of("bidder1", givenSeatBid(), 0),
                 BidderResponse.of("bidder2", givenSeatBid(BidderBid.of(bid, banner, "USD")), 0));
@@ -465,13 +499,16 @@ public void shouldOverrideBidIdWhenGenerateBidIdIsTurnedOn() {
                 .build();
         final List bidderResponses = singletonList(
                 BidderResponse.of("bidder2", givenSeatBid(BidderBid.of(bid, banner, "USD")), 0));
+        given(idGenerator.getType()).willReturn(IdGeneratorType.uuid);
+        given(idGenerator.generateId()).willReturn("de7fc739-0a6e-41ad-8961-701c30c82166");
 
         final BidResponseCreator bidResponseCreator = new BidResponseCreator(
                 cacheService,
                 bidderCatalog,
                 eventsService,
                 storedRequestProcessor,
-                true,
+                bidResponseReducer,
+                idGenerator,
                 0,
                 clock,
                 jacksonMapper);
@@ -489,11 +526,76 @@ public void shouldOverrideBidIdWhenGenerateBidIdIsTurnedOn() {
                 .extracting(ExtBidPrebid::getBidid)
                 .hasSize(1)
                 .first()
-                .satisfies(bidId -> assertThat(UUID.fromString(bidId)).isInstanceOf(UUID.class));
+                .isEqualTo("de7fc739-0a6e-41ad-8961-701c30c82166");
 
         verify(cacheService, never()).cacheBidsOpenrtb(anyList(), any(), any(), any());
     }
 
+    @Test
+    public void shouldUseGeneratedBidIdForEventAndCacheWhenGeneratedIdIsTurnedOn() {
+        // given
+        final Account account = Account.builder().id("accountId").eventsEnabled(true).build();
+        final BidRequest bidRequest = givenBidRequest(
+                identity(),
+                extBuilder -> extBuilder
+                        .events(mapper.createObjectNode())
+                        .integration("pbjs"));
+
+        final AuctionContext auctionContext = givenAuctionContext(
+                bidRequest,
+                contextBuilder -> contextBuilder.account(account));
+
+        final ExtPrebid prebid = ExtPrebid.of(ExtBidPrebid.builder().type(banner).build(), null);
+        final Bid bid = Bid.builder()
+                .id("bidId1")
+                .impid("impId1")
+                .price(BigDecimal.ONE)
+                .ext(mapper.valueToTree(prebid))
+                .build();
+
+        final List bidderResponses = singletonList(
+                BidderResponse.of("bidder1", givenSeatBid(BidderBid.of(bid, banner, "USD")), 0));
+
+        final String generatedBid = "de7fc739-0a6e-41ad-8961-701c30c82166";
+        given(idGenerator.getType()).willReturn(IdGeneratorType.uuid);
+        given(idGenerator.generateId()).willReturn(generatedBid);
+
+        final BidRequestCacheInfo cacheInfo = BidRequestCacheInfo.builder().doCaching(true).build();
+        givenCacheServiceResult(singletonMap(bid, CacheInfo.of("id", null, null, null)));
+
+        final Events events = Events.of("http://event-type-win", "http://event-type-view");
+        given(eventsService.createEvent(anyString(), anyString(), anyString(), anyLong(), anyString()))
+                .willReturn(events);
+
+        final BidResponseCreator bidResponseCreator = new BidResponseCreator(
+                cacheService,
+                bidderCatalog,
+                eventsService,
+                storedRequestProcessor,
+                bidResponseReducer,
+                idGenerator,
+                0,
+                clock,
+                jacksonMapper);
+
+        // when
+        bidResponseCreator.create(bidderResponses, auctionContext, cacheInfo, false).result();
+
+        // then
+        final ArgumentCaptor cacheContextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class);
+        verify(cacheService).cacheBidsOpenrtb(any(), any(), cacheContextArgumentCaptor.capture(), any());
+
+        assertThat(cacheContextArgumentCaptor.getValue())
+                .satisfies(context -> {
+                    assertThat(context.isShouldCacheVideoBids()).isFalse();
+                    assertThat(context.getBidderToVideoGeneratedBidIdsToModify().getBidderToBidIds()).isEmpty();
+                    assertThat(context.getBidderToBidsToGeneratedIds().getBidderToBidIds())
+                            .containsAllEntriesOf(singletonMap("bidder1", singletonMap("bidId1-impId1", generatedBid)));
+                });
+
+        verify(eventsService).createEvent(eq(generatedBid), anyString(), anyString(), anyLong(), anyString());
+    }
+
     @Test
     public void shouldSetExpectedResponseSeatBidAndBidFields() {
         // given
@@ -526,33 +628,22 @@ public void shouldSetExpectedResponseSeatBidAndBidFields() {
     }
 
     @Test
-    public void shouldFilterByDealsAndPriceBidsWhenImpIdsAreEqual() {
+    public void shouldFilterBidByBidReducerResponse() {
         // given
         final AuctionContext auctionContext = givenAuctionContext(givenBidRequest());
 
-        final Bid simpleBidImp1 = Bid.builder().id("bidId1i1").price(BigDecimal.valueOf(5.67)).impid("i1").build();
-        final Bid simpleBid1Imp2 = Bid.builder().id("bidId1i2").price(BigDecimal.valueOf(15.67)).impid("i2").build();
-        final Bid simpleBid2Imp2 = Bid.builder().id("bidId2i2").price(BigDecimal.valueOf(17.67)).impid("i2").build();
         final Bid dealBid1Imp1 = Bid.builder().id("bidId1i1d").dealid("d1").price(BigDecimal.valueOf(4.98)).impid("i1")
                 .build();
         final Bid dealBid2Imp1 = Bid.builder().id("bidId2i1d").dealid("d2").price(BigDecimal.valueOf(5.00)).impid("i1")
                 .build();
         final BidderSeatBid seatBidWithDeals = givenSeatBid(
-                BidderBid.of(simpleBidImp1, banner, null),
-                BidderBid.of(simpleBid1Imp2, banner, null),
-                BidderBid.of(simpleBid2Imp2, banner, null), // will stay (top price)
-                BidderBid.of(dealBid2Imp1, banner, null),   // will stay (deal + topPrice)
+                BidderBid.of(dealBid2Imp1, banner, null),
                 BidderBid.of(dealBid1Imp1, banner, null));
 
-        final Bid simpleBid3Imp2 = Bid.builder().id("bidId3i2").price(BigDecimal.valueOf(7.25)).impid("i2").build();
-        final Bid simpleBid4Imp2 = Bid.builder().id("bidId4i2").price(BigDecimal.valueOf(7.26)).impid("i2").build();
-        final BidderSeatBid seatBidWithSimpleBids = givenSeatBid(
-                BidderBid.of(simpleBid3Imp2, banner, null),
-                BidderBid.of(simpleBid4Imp2, banner, null)); // will stay (top price)
+        final List bidderResponses = singletonList(BidderResponse.of("bidder1", seatBidWithDeals, 100));
 
-        final List bidderResponses = asList(
-                BidderResponse.of("bidder1", seatBidWithDeals, 100),
-                BidderResponse.of("bidder2", seatBidWithSimpleBids, 111));
+        given(bidResponseReducer.removeRedundantBids(any()))
+                .willReturn(BidderResponse.of("bidder1", givenSeatBid(BidderBid.of(dealBid2Imp1, banner, null)), 100));
 
         // when
         final BidResponse bidResponse =
@@ -560,9 +651,9 @@ public void shouldFilterByDealsAndPriceBidsWhenImpIdsAreEqual() {
 
         // then
         assertThat(bidResponse.getSeatbid())
-                .flatExtracting(SeatBid::getBid).hasSize(3)
+                .flatExtracting(SeatBid::getBid).hasSize(1)
                 .extracting(Bid::getId)
-                .containsOnly("bidId2i2", "bidId2i1d", "bidId4i2");
+                .containsOnly("bidId2i1d");
     }
 
     @Test
@@ -727,7 +818,7 @@ public void shouldSetBidAdmToNullIfCacheIdIsPresentAndReturnCreativeBidsIsFalse(
                 identity(),
                 extBuilder -> extBuilder.targeting(givenTargeting())));
 
-        final Bid bid = Bid.builder().price(BigDecimal.ONE).adm("adm").impid("i1").build();
+        final Bid bid = Bid.builder().price(BigDecimal.ONE).adm("adm").id("bidId").impid("i1").build();
         final List bidderResponses = singletonList(BidderResponse.of("bidder1",
                 givenSeatBid(BidderBid.of(bid, banner, "USD")), 100));
 
@@ -755,7 +846,7 @@ public void shouldSetBidAdmToNullIfVideoCacheIdIsPresentAndReturnCreativeVideoBi
                 identity(),
                 extBuilder -> extBuilder.targeting(givenTargeting())));
 
-        final Bid bid = Bid.builder().price(BigDecimal.ONE).adm("adm").impid("i1").build();
+        final Bid bid = Bid.builder().price(BigDecimal.ONE).adm("adm").id("bidId").impid("i1").build();
         final List bidderResponses = singletonList(BidderResponse.of("bidder1",
                 givenSeatBid(BidderBid.of(bid, banner, "USD")), 100));
 
@@ -783,7 +874,7 @@ public void shouldSetBidExpWhenCacheIdIsMatched() {
                 identity(),
                 extBuilder -> extBuilder.targeting(givenTargeting())));
 
-        final Bid bid = Bid.builder().price(BigDecimal.ONE).impid("i1").build();
+        final Bid bid = Bid.builder().price(BigDecimal.ONE).impid("i1").id("bidId").build();
         final List bidderResponses = singletonList(BidderResponse.of("bidder1",
                 givenSeatBid(BidderBid.of(bid, banner, "USD")), 100));
 
@@ -811,7 +902,7 @@ public void shouldSetBidExpMaxTtlWhenCacheIdIsMatchedAndBothTtlIsSet() {
                 identity(),
                 extBuilder -> extBuilder.targeting(givenTargeting())));
 
-        final Bid bid = Bid.builder().price(BigDecimal.ONE).impid("i1").build();
+        final Bid bid = Bid.builder().price(BigDecimal.ONE).impid("i1").id("bidId").build();
         final List bidderResponses = singletonList(BidderResponse.of("bidder1",
                 givenSeatBid(BidderBid.of(bid, banner, "USD")), 100));
 
@@ -904,7 +995,8 @@ public void shouldTruncateTargetingKeywordsByGlobalConfig() {
                 bidderCatalog,
                 eventsService,
                 storedRequestProcessor,
-                false,
+                bidResponseReducer,
+                idGenerator,
                 20,
                 clock,
                 jacksonMapper);
@@ -971,6 +1063,7 @@ public void shouldTruncateTargetingKeywordsByRequestPassedValue() {
                                         BigDecimal.valueOf(0.5))))))
                         .includewinners(true)
                         .includebidderkeys(true)
+                        .includeformat(false)
                         .truncateattrchars(20)
                         .build()));
         final AuctionContext auctionContext = givenAuctionContext(
@@ -1052,6 +1145,7 @@ public void shouldPopulateTargetingKeywordsFromMediaTypePriceGranularities() {
                                 null))
                         .includewinners(true)
                         .includebidderkeys(true)
+                        .includeformat(false)
                         .build())));
 
         final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(5.67)).impid("i1").build();
@@ -1446,6 +1540,7 @@ public void shouldNotPopulateWinningBidTargetingIfIncludeWinnersFlagIsFalse() {
                                         BigDecimal.valueOf(0.5))))))
                         .includewinners(false)
                         .includebidderkeys(true)
+                        .includeformat(false)
                         .build())));
 
         final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(5.67)).impid("i1").build();
@@ -1487,6 +1582,7 @@ public void shouldNotPopulateBidderKeysTargetingIfIncludeBidderKeysFlagIsFalse()
                                         BigDecimal.valueOf(0.5))))))
                         .includewinners(true)
                         .includebidderkeys(false)
+                        .includeformat(false)
                         .build())));
 
         final Bid bid = Bid.builder().id("bidId").price(BigDecimal.valueOf(5.67)).impid("i1").build();
@@ -1688,7 +1784,7 @@ public void impToStoredVideoJsonShouldInjectStoredVideoWhenExtOptionsIsTrueAndVi
         final Bid bid1 = Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.valueOf(5.67)).build();
         final Bid bid2 = Bid.builder().id("bidId2").impid("impId2").price(BigDecimal.valueOf(2)).build();
         final Bid bid3 = Bid.builder().id("bidId3").impid("impId3").price(BigDecimal.valueOf(3)).build();
-        final List bidderBids = Arrays.asList(
+        final List bidderBids = mutableList(
                 BidderBid.of(bid1, banner, "USD"),
                 BidderBid.of(bid2, banner, "USD"),
                 BidderBid.of(bid3, banner, "USD"));
@@ -1705,7 +1801,7 @@ public void impToStoredVideoJsonShouldInjectStoredVideoWhenExtOptionsIsTrueAndVi
                 bidResponseCreator.create(bidderResponses, auctionContext, CACHE_INFO, false);
 
         // then
-        verify(storedRequestProcessor).videoStoredDataResult(any(), eq(Arrays.asList(imp1, imp3)), anyList(),
+        verify(storedRequestProcessor).videoStoredDataResult(any(), eq(asList(imp1, imp3)), anyList(),
                 eq(timeout));
 
         assertThat(result.result().getSeatbid())
@@ -1729,8 +1825,7 @@ public void impToStoredVideoJsonShouldAddErrorsWithPrebidBidderWhenStoredVideoRe
         final AuctionContext auctionContext = givenAuctionContext(bidRequest);
 
         final Bid bid1 = Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.valueOf(5.67)).build();
-        final List bidderBids = singletonList(
-                BidderBid.of(bid1, banner, "USD"));
+        final List bidderBids = singletonList(BidderBid.of(bid1, banner, "USD"));
         final List bidderResponses = singletonList(
                 BidderResponse.of("bidder1", BidderSeatBid.of(bidderBids, emptyList(), emptyList()), 100));
 
@@ -1789,8 +1884,7 @@ public void shouldProcessRequestAndAddErrorFromAuctionContext() {
                 contextBuilder -> contextBuilder.prebidErrors(singletonList("privacy error")));
 
         final Bid bid1 = Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.valueOf(5.67)).build();
-        final List bidderBids = singletonList(
-                BidderBid.of(bid1, banner, "USD"));
+        final List bidderBids = singletonList(BidderBid.of(bid1, banner, "USD"));
         final List bidderResponses = singletonList(
                 BidderResponse.of("bidder1", BidderSeatBid.of(bidderBids, emptyList(), emptyList()), 100));
 
@@ -1939,7 +2033,7 @@ private static BidRequest givenBidRequest(Imp... imps) {
     }
 
     private static BidderSeatBid givenSeatBid(BidderBid... bids) {
-        return BidderSeatBid.of(new ArrayList<>(asList(bids)), emptyList(), emptyList());
+        return BidderSeatBid.of(mutableList(bids), emptyList(), emptyList());
     }
 
     private static ExtRequestTargeting givenTargeting() {
@@ -1949,6 +2043,7 @@ private static ExtRequestTargeting givenTargeting() {
                                 BigDecimal.valueOf(0.5))))))
                 .includewinners(true)
                 .includebidderkeys(true)
+                .includeformat(false)
                 .build();
     }
 
@@ -1965,4 +2060,25 @@ private static String toTargetingByKey(Bid bid, String targetingKey) {
         final Map targeting = toExtPrebid(bid.getExt()).getPrebid().getTargeting();
         return targeting != null ? targeting.get(targetingKey) : null;
     }
+
+    @SuppressWarnings("unchecked")
+    private static  void assertMapWithUnorderedList(Map> map, Map> expectedMap) {
+        assertThat(map).hasSize(expectedMap.size());
+        for (Map.Entry> keyToValues : expectedMap.entrySet()) {
+            final V[] values = (V[]) keyToValues.getValue().toArray();
+            assertThat(expectedMap.get(keyToValues.getKey())).containsOnly(values);
+        }
+    }
+
+    private  Map doubleMap(K key1, V value1, K key2, V value2) {
+        final Map result = new HashMap<>();
+        result.put(key1, value1);
+        result.put(key2, value2);
+        return result;
+    }
+
+    @SafeVarargs
+    private static  List mutableList(T... values) {
+        return Arrays.stream(values).collect(Collectors.toList());
+    }
 }
diff --git a/src/test/java/org/prebid/server/auction/BidResponseReducerTest.java b/src/test/java/org/prebid/server/auction/BidResponseReducerTest.java
new file mode 100644
index 00000000000..d20f0dbb716
--- /dev/null
+++ b/src/test/java/org/prebid/server/auction/BidResponseReducerTest.java
@@ -0,0 +1,116 @@
+package org.prebid.server.auction;
+
+import com.iab.openrtb.response.Bid;
+import org.junit.Test;
+import org.prebid.server.auction.model.BidderResponse;
+import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.bidder.model.BidderSeatBid;
+import org.prebid.server.proto.openrtb.ext.response.BidType;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+public class BidResponseReducerTest {
+
+    private final BidResponseReducer bidResponseReducer = new BidResponseReducer();
+
+    @Test
+    public void removeRedundantBidsShouldReduceNonDealBidsByPriceDroppingNonDealsBids() {
+        // given
+        final BidderResponse bidderResponse = BidderResponse.of(
+                "bidder1",
+                givenSeatBid(
+                        givenBidderBid("bidId1", "impId1", "dealId1", 5.0f), // deal
+                        givenBidderBid("bidId2", "impId1", "dealId2", 6.0f), // deal
+                        givenBidderBid("bidId3", "impId1", null, 7.0f) // non deal
+                ),
+                0);
+
+        // when
+        final BidderResponse resultBidderResponse = bidResponseReducer.removeRedundantBids(bidderResponse);
+
+        // then
+        assertThat(resultBidderResponse.getSeatBid().getBids())
+                .extracting(BidderBid::getBid)
+                .extracting(Bid::getId)
+                .containsOnly("bidId2");
+    }
+
+    @Test
+    public void removeRedundantBidsShouldReduceNonDealBidsByPrice() {
+        // given
+        final BidderResponse bidderResponse = BidderResponse.of(
+                "bidder1",
+                givenSeatBid(
+                        givenBidderBid("bidId1", "impId1", null, 5.0f), // non deal
+                        givenBidderBid("bidId2", "impId1", null, 6.0f), // non deal
+                        givenBidderBid("bidId3", "impId1", null, 7.0f) // non deal
+                ),
+                0);
+
+        // when
+        final BidderResponse resultBidderResponse = bidResponseReducer.removeRedundantBids(bidderResponse);
+
+        // then
+        assertThat(resultBidderResponse.getSeatBid().getBids())
+                .extracting(BidderBid::getBid)
+                .extracting(Bid::getId)
+                .containsOnly("bidId3");
+    }
+
+    @Test
+    public void removeRedundantBidsShouldNotReduceBids() {
+        // given
+        final BidderResponse bidderResponse = BidderResponse.of(
+                "bidder1",
+                givenSeatBid(givenBidderBid("bidId1", "impId1", null, 5.0f)),
+                0);
+
+        // when
+        final BidderResponse resultBidderResponse = bidResponseReducer.removeRedundantBids(bidderResponse);
+
+        // then
+        assertThat(resultBidderResponse.getSeatBid().getBids())
+                .extracting(BidderBid::getBid)
+                .extracting(Bid::getId)
+                .containsOnly("bidId1");
+    }
+
+    @Test
+    public void removeRedundantBidsShouldReduceAllTypesOfBidsForMultipleImps() {
+        // given
+        final BidderResponse bidderResponse = BidderResponse.of(
+                "bidder1",
+                givenSeatBid(
+                        givenBidderBid("bidId3-1", "impId1", "dealId3", 5.0f), // deal
+                        givenBidderBid("bidId4-1", "impId1", null, 5.0f), // non deal
+                        givenBidderBid("bidId1-2", "impId2", "dealId4", 5.0f), // deal
+                        givenBidderBid("bidId2-2", "impId2", "dealId5", 6.0f), // deal
+                        givenBidderBid("bidId3-2", "impId2", null, 5.0f), // non deal
+                        givenBidderBid("bidId1-3", "impId3", null, 5.0f), // non deal
+                        givenBidderBid("bidId2-3", "impId3", null, 6.0f)  // non deal
+                ),
+                0);
+
+        // when
+        final BidderResponse resultBidderResponse = bidResponseReducer.removeRedundantBids(bidderResponse);
+
+        // then
+        assertThat(resultBidderResponse.getSeatBid().getBids())
+                .extracting(BidderBid::getBid)
+                .extracting(Bid::getId)
+                .containsOnly("bidId3-1", "bidId2-2", "bidId2-3");
+    }
+
+    private static BidderBid givenBidderBid(String bidId, String impId, String dealId, float price) {
+        return BidderBid.of(
+                Bid.builder().id(bidId).impid(impId).dealid(dealId).price(BigDecimal.valueOf(price)).build(),
+                BidType.banner, "USD");
+    }
+
+    private static BidderSeatBid givenSeatBid(BidderBid... bidderBids) {
+        return BidderSeatBid.of(Arrays.asList(bidderBids), null, null);
+    }
+}
diff --git a/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java b/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java
index 283786c38ea..f24f4d6149e 100644
--- a/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java
+++ b/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java
@@ -1,6 +1,7 @@
 package org.prebid.server.auction;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.iab.openrtb.request.BidRequest;
 import io.vertx.core.Future;
 import io.vertx.core.Handler;
 import io.vertx.core.Vertx;
@@ -15,13 +16,21 @@
 import org.prebid.server.currency.CurrencyConversionService;
 import org.prebid.server.currency.proto.CurrencyConversionRates;
 import org.prebid.server.exception.PreBidException;
+import org.prebid.server.metric.Metrics;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestCurrency;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
 import org.prebid.server.spring.config.model.ExternalConversionProperties;
 import org.prebid.server.vertx.http.HttpClient;
 import org.prebid.server.vertx.http.model.HttpClientResponse;
 
 import java.math.BigDecimal;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneOffset;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.BooleanSupplier;
 
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonMap;
@@ -53,6 +62,9 @@ public class CurrencyConversionServiceTest extends VertxTest {
     private HttpClient httpClient;
     @Mock
     private Vertx vertx;
+    @Mock
+    private Metrics metrics;
+    private final Clock clock = Clock.fixed(Instant.now(), ZoneOffset.UTC);
 
     private CurrencyConversionService currencyService;
 
@@ -64,13 +76,13 @@ public void setUp() throws JsonProcessingException {
         givenHttpClientReturnsResponse(httpClient, 200,
                 mapper.writeValueAsString(CurrencyConversionRates.of(null, currencyRates)));
 
-        currencyService = setExternalResource(URL, 1L, vertx, httpClient);
+        currencyService = createInitializedService(URL, 1L, -3600L, httpClient);
     }
 
     @Test
     public void creationShouldFailOnInvalidCurrencyServerUrl() {
         assertThatIllegalArgumentException()
-                .isThrownBy(() -> setExternalResource("invalid-url", 1L, vertx, httpClient))
+                .isThrownBy(() -> createInitializedService("invalid-url", 1L, -1L, httpClient))
                 .withMessage("URL supplied is not valid: invalid-url");
     }
 
@@ -79,13 +91,38 @@ public void initializeShouldSetLastUpdatedDate() {
         assertThat(currencyService.getLastUpdated()).isNotNull();
     }
 
+    @Test
+    public void currencyRatesGaugeShouldReportStale() {
+        // then
+        final ArgumentCaptor gaugeValueProviderCaptor = ArgumentCaptor.forClass(BooleanSupplier.class);
+        verify(metrics).createCurrencyRatesGauge(gaugeValueProviderCaptor.capture());
+        final BooleanSupplier gaugeValueProvider = gaugeValueProviderCaptor.getValue();
+
+        assertThat(gaugeValueProvider.getAsBoolean()).isTrue();
+    }
+
+    @Test
+    public void currencyRatesGaugeShouldReportNotStale() {
+        // when
+        metrics = mock(Metrics.class); // original mock is already spoiled by service initialization in setUp
+        currencyService = createInitializedService(URL, 1L, 3600L, httpClient);
+
+        // then
+        final ArgumentCaptor gaugeValueProviderCaptor = ArgumentCaptor.forClass(BooleanSupplier.class);
+        verify(metrics).createCurrencyRatesGauge(gaugeValueProviderCaptor.capture());
+        final BooleanSupplier gaugeValueProvider = gaugeValueProviderCaptor.getValue();
+
+        assertThat(gaugeValueProvider.getAsBoolean()).isFalse();
+    }
+
     @Test
     public void convertCurrencyShouldReturnSamePriceIfBidAndServerCurrenciesEquals() {
         // given
         final BigDecimal price = BigDecimal.valueOf(100);
 
         // when
-        final BigDecimal convertedPrice = currencyService.convertCurrency(price, null, USD, USD, false);
+        final BigDecimal convertedPrice = currencyService.convertCurrency(price,
+                givenBidRequestWithCurrencies(null, false), USD, USD);
 
         // then
         assertThat(convertedPrice).isSameAs(price);
@@ -98,8 +135,8 @@ public void convertCurrencyShouldUseUSDByDefaultIfBidCurrencyIsNull() {
                 singletonMap(GBP, singletonMap(USD, BigDecimal.valueOf(1.4306)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, GBP, null,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), GBP, null);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(0.699))).isEqualTo(0);
@@ -112,8 +149,8 @@ public void convertCurrencyShouldReturnConvertedByStraightMultiplierPrice() {
                 singletonMap(EUR, BigDecimal.valueOf(1.1565)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, GBP, EUR,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), GBP, EUR);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(0.865))).isEqualTo(0);
@@ -126,8 +163,8 @@ public void convertCurrencyShouldReturnConvertedByInvertedMultiplierPrice() {
                 BigDecimal.valueOf(1.1565)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, GBP,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), EUR, GBP);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.156))).isEqualTo(0);
@@ -141,8 +178,8 @@ public void convertCurrencyShouldReturnConvertedByIntermediateMultiplierPrice()
         requestConversionRates.put(EUR, singletonMap(USD, BigDecimal.valueOf(1.2304)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, GBP,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), EUR, GBP);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.163))).isEqualTo(0);
@@ -155,8 +192,8 @@ public void convertCurrencyShouldReturnConvertedBySingleDigitMultiplierPrice() {
         requestConversionRates.put(EUR, singletonMap(USD, BigDecimal.valueOf(0.5)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(new BigDecimal("1.23"), requestConversionRates, EUR,
-                USD, false);
+        final BigDecimal price = currencyService.convertCurrency(new BigDecimal("1.23"),
+                givenBidRequestWithCurrencies(requestConversionRates, false), EUR, USD);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(2.460))).isEqualTo(0);
@@ -165,7 +202,8 @@ public void convertCurrencyShouldReturnConvertedBySingleDigitMultiplierPrice() {
     @Test
     public void convertCurrencyShouldUseLatestRatesIfRequestRatesIsNull() {
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, null, EUR, GBP, false);
+        final BigDecimal price = currencyService.convertCurrency(
+                BigDecimal.ONE, givenBidRequestWithCurrencies(null, false), EUR, GBP);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.149))).isEqualTo(0);
@@ -178,8 +216,8 @@ public void convertCurrencyShouldUseConversionRateFromServerIfusepbsratesIsTrue(
         requestConversionRates.put(EUR, singletonMap(USD, BigDecimal.valueOf(0.6)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, GBP,
-                true);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, true), EUR, GBP);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.149))).isEqualTo(0);
@@ -192,8 +230,8 @@ public void convertCurrencyShouldUseConversionRateFromRequestIfusepbsratesIsFals
                 BigDecimal.valueOf(0.6)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, USD,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), EUR, USD);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.667))).isEqualTo(0);
@@ -206,8 +244,8 @@ public void convertCurrencyShouldUseLatestRatesIfMultiplierWasNotFoundInRequestR
                 singletonMap(EUR, BigDecimal.valueOf(0.8434)));
 
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, UAH,
-                false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(requestConversionRates, false), EUR, UAH);
 
         // then
         assertThat(price.compareTo(BigDecimal.valueOf(1.156))).isEqualTo(0);
@@ -216,7 +254,8 @@ public void convertCurrencyShouldUseLatestRatesIfMultiplierWasNotFoundInRequestR
     @Test
     public void convertCurrencyShouldReturnSamePriceIfBidCurrencyIsNullAndServerCurrencyUSD() {
         // when
-        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE, emptyMap(), USD, null, false);
+        final BigDecimal price = currencyService.convertCurrency(BigDecimal.ONE,
+                givenBidRequestWithCurrencies(emptyMap(), false), USD, null);
 
         // then
         assertThat(price.compareTo(BigDecimal.ONE)).isEqualTo(0);
@@ -229,8 +268,8 @@ public void convertCurrencyShouldFailWhenRequestRatesIsNullAndNoExternalRatesPro
 
         // when and then
         assertThatExceptionOfType(PreBidException.class)
-                .isThrownBy(() -> currencyConversionService.convertCurrency(BigDecimal.ONE, null, EUR, GBP,
-                        false))
+                .isThrownBy(() -> currencyConversionService.convertCurrency(BigDecimal.ONE,
+                        givenBidRequestWithCurrencies(null, false), EUR, GBP))
                 .withMessage("Unable to convert bid currency GBP to desired ad server currency EUR");
     }
 
@@ -238,7 +277,8 @@ public void convertCurrencyShouldFailWhenRequestRatesIsNullAndNoExternalRatesPro
     public void convertCurrencyShouldThrowPrebidExceptionIfServerAndRequestRatesAreNull() {
         // when and then
         assertThatExceptionOfType(PreBidException.class)
-                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE, null, USD, EUR, false))
+                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE,
+                        givenBidRequestWithCurrencies(null, false), USD, EUR))
                 .withMessage("Unable to convert bid currency EUR to desired ad server currency USD");
     }
 
@@ -251,12 +291,12 @@ public void convertCurrencyShouldThrowPrebidExceptionIfMultiplierWasNotFoundFrom
         givenHttpClientReturnsResponse(httpClient, 503, "server unavailable");
 
         // when
-        currencyService = setExternalResource(URL, 1L, vertx, httpClient);
+        currencyService = createInitializedService(URL, 1L, -1L, httpClient);
 
         // then
         assertThatExceptionOfType(PreBidException.class)
-                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE, requestConversionRates, EUR, AUD,
-                        false))
+                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE,
+                        givenBidRequestWithCurrencies(requestConversionRates, false), EUR, AUD))
                 .withMessage("Unable to convert bid currency AUD to desired ad server currency EUR");
     }
 
@@ -266,11 +306,12 @@ public void convertCurrencyShouldThrowExceptionWhenCurrencyServerResponseStatusN
         givenHttpClientReturnsResponse(httpClient, 503, "server unavailable");
 
         // when
-        currencyService = setExternalResource(URL, 1L, vertx, httpClient);
+        currencyService = createInitializedService(URL, 1L, -1L, httpClient);
 
         // then
         assertThatExceptionOfType(PreBidException.class)
-                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE, null, UAH, AUD, false))
+                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE,
+                        givenBidRequestWithCurrencies(null, false), UAH, AUD))
                 .withMessage("Unable to convert bid currency AUD to desired ad server currency UAH");
     }
 
@@ -280,11 +321,12 @@ public void convertCurrencyShouldThrowExceptionWhenCurrencyServerResponseContain
         givenHttpClientReturnsResponse(httpClient, 200, "{\"foo\": \"bar\"}");
 
         // when
-        currencyService = setExternalResource(URL, 1L, vertx, httpClient);
+        currencyService = createInitializedService(URL, 1L, -1L, httpClient);
 
         // then
         assertThatExceptionOfType(PreBidException.class)
-                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE, null, UAH, AUD, false))
+                .isThrownBy(() -> currencyService.convertCurrency(BigDecimal.ONE,
+                        givenBidRequestWithCurrencies(null, false), UAH, AUD))
                 .withMessage("Unable to convert bid currency AUD to desired ad server currency UAH");
     }
 
@@ -297,7 +339,7 @@ public void initializeShouldMakeOneInitialRequestAndTwoScheduled() {
         givenHttpClientReturnsResponse(httpClient, 200, "{\"foo\": \"bar\"}");
 
         // when and then
-        currencyService = setExternalResource(URL, 1000, vertx, httpClient);
+        currencyService = createInitializedService(URL, 1000, -1L, httpClient);
 
         final ArgumentCaptor> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         verify(vertx).setPeriodic(eq(1000L), handlerCaptor.capture());
@@ -309,11 +351,26 @@ public void initializeShouldMakeOneInitialRequestAndTwoScheduled() {
         verify(httpClient, times(3)).get(anyString(), anyLong());
     }
 
-    private static CurrencyConversionService setExternalResource(String url, long refreshPeriod, Vertx vertx,
-                                                                 HttpClient httpClient) {
+    private CurrencyConversionService createInitializedService(String url,
+                                                               long refreshPeriod,
+                                                               long staleAfter,
+                                                               HttpClient httpClient) {
+
         final CurrencyConversionService currencyService = new CurrencyConversionService(
-                new ExternalConversionProperties(url, 1000L, refreshPeriod, vertx, httpClient, jacksonMapper));
+                new ExternalConversionProperties(
+                        url,
+                        1000L,
+                        refreshPeriod,
+                        staleAfter,
+                        null,
+                        vertx,
+                        httpClient,
+                        metrics,
+                        clock,
+                        jacksonMapper));
+
         currencyService.initialize();
+
         return currencyService;
     }
 
@@ -322,4 +379,12 @@ private static void givenHttpClientReturnsResponse(HttpClient httpClient, int st
         given(httpClient.get(anyString(), anyLong()))
                 .willReturn(Future.succeededFuture(httpClientResponse));
     }
+
+    private BidRequest givenBidRequestWithCurrencies(Map> requestCurrencies,
+                                                     Boolean usepbsrates) {
+        return BidRequest.builder()
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .currency(ExtRequestCurrency.of(requestCurrencies, usepbsrates)).build()))
+                .build();
+    }
 }
diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
index 47aa5684387..a0a9c836758 100644
--- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
+++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
@@ -71,9 +71,7 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchainSchainNode;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
 import org.prebid.server.proto.openrtb.ext.request.ExtSite;
-import org.prebid.server.proto.openrtb.ext.request.ExtSource;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid;
 import org.prebid.server.proto.openrtb.ext.response.BidType;
@@ -143,6 +141,8 @@ public class ExchangeServiceTest extends VertxTest {
     @Mock
     private FpdResolver fpdResolver;
     @Mock
+    private SchainResolver schainResolver;
+    @Mock
     private HttpBidderRequester httpBidderRequester;
     @Mock
     private ResponseBidValidator responseBidValidator;
@@ -189,10 +189,12 @@ public void setUp() {
         given(fpdResolver.resolveSite(any(), any())).willAnswer(invocation -> invocation.getArgument(0));
         given(fpdResolver.resolveApp(any(), any())).willAnswer(invocation -> invocation.getArgument(0));
 
-        given(responseBidValidator.validate(any())).willReturn(ValidationResult.success());
+        given(schainResolver.resolveForBidder(anyString(), any())).willReturn(null);
+
+        given(responseBidValidator.validate(any(), any(), any(), any())).willReturn(ValidationResult.success());
         given(usersyncer.getCookieFamilyName()).willReturn("cookieFamily");
 
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any()))
+        given(currencyService.convertCurrency(any(), any(), any(), any()))
                 .willAnswer(invocationOnMock -> invocationOnMock.getArgument(0));
 
         given(storedResponseProcessor.getStoredResponseResult(any(), any(), any()))
@@ -209,6 +211,7 @@ public void setUp() {
                 storedResponseProcessor,
                 privacyEnforcementService,
                 fpdResolver,
+                schainResolver,
                 httpBidderRequester,
                 responseBidValidator,
                 currencyService,
@@ -228,6 +231,7 @@ public void creationShouldFailOnNegativeExpectedCacheTime() {
                         storedResponseProcessor,
                         privacyEnforcementService,
                         fpdResolver,
+                        schainResolver,
                         httpBidderRequester,
                         responseBidValidator,
                         currencyService,
@@ -291,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()))
@@ -311,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());
@@ -327,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()))
@@ -353,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()))
@@ -391,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
@@ -491,33 +458,35 @@ public void shouldPassRequestWithInjectedSchainInSourceExt() {
         givenBidder(bidder2Name, bidder2, givenEmptySeatBid());
         givenBidder(bidder3Name, bidder3, givenEmptySeatBid());
 
-        final ObjectNode schainExtObjectNode = mapper.createObjectNode().put("any", "any");
-        final ExtRequestPrebidSchainSchainNode specificNodes = ExtRequestPrebidSchainSchainNode.of("asi", "sid", 1,
-                "rid", "name", "domain", schainExtObjectNode);
-        final ExtRequestPrebidSchainSchain specificSchain = ExtRequestPrebidSchainSchain.of("ver", 1,
-                singletonList(specificNodes), schainExtObjectNode);
+        final ExtRequestPrebidSchainSchainNode specificNodes = ExtRequestPrebidSchainSchainNode.of(
+                "asi", "sid", 1, "rid", "name", "domain", null);
+        final ExtRequestPrebidSchainSchain specificSchain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, singletonList(specificNodes), null);
         final ExtRequestPrebidSchain schainForBidders = ExtRequestPrebidSchain.of(
-                asList(bidder1Name, bidder2Name),
-                specificSchain);
-        final ExtRequestPrebidSchainSchain allSchainObject = ExtRequestPrebidSchainSchain.of("ver", 1,
-                singletonList(specificNodes), schainExtObjectNode);
-        final ExtRequestPrebidSchainSchainNode generalNodes = ExtRequestPrebidSchainSchainNode.of("t", null, 0, "a",
-                null, "ads", null);
-        final ExtRequestPrebidSchainSchain generalSchain = ExtRequestPrebidSchainSchain.of("t", 123,
-                singletonList(generalNodes), null);
+                asList(bidder1Name, bidder2Name), specificSchain);
+
+        final ExtRequestPrebidSchainSchainNode generalNodes = ExtRequestPrebidSchainSchainNode.of(
+                "t", null, 0, "a", null, "ads", null);
+        final ExtRequestPrebidSchainSchain generalSchain = ExtRequestPrebidSchainSchain.of(
+                "t", 123, singletonList(generalNodes), null);
         final ExtRequestPrebidSchain allSchain = ExtRequestPrebidSchain.of(singletonList("*"), generalSchain);
+
         final ExtRequest extRequest = ExtRequest.of(
                 ExtRequestPrebid.builder()
                         .schains(asList(schainForBidders, allSchain))
                         .auctiontimestamp(1000L)
                         .build());
-
-        final BidRequest bidRequest = givenBidRequest(asList(
-                givenImp(singletonMap(bidder1Name, 1), identity()),
-                givenImp(singletonMap(bidder2Name, 2), identity()),
-                givenImp(singletonMap(bidder3Name, 3), identity())),
+        final BidRequest bidRequest = givenBidRequest(
+                asList(
+                        givenImp(singletonMap(bidder1Name, 1), identity()),
+                        givenImp(singletonMap(bidder2Name, 2), identity()),
+                        givenImp(singletonMap(bidder3Name, 3), identity())),
                 builder -> builder.ext(extRequest));
 
+        given(schainResolver.resolveForBidder(eq("bidder1"), same(bidRequest))).willReturn(specificSchain);
+        given(schainResolver.resolveForBidder(eq("bidder2"), same(bidRequest))).willReturn(specificSchain);
+        given(schainResolver.resolveForBidder(eq("bidder3"), same(bidRequest))).willReturn(generalSchain);
+
         // when
         exchangeService.holdAuction(givenRequestContext(bidRequest));
 
@@ -525,8 +494,7 @@ public void shouldPassRequestWithInjectedSchainInSourceExt() {
         final ArgumentCaptor bidRequest1Captor = ArgumentCaptor.forClass(BidRequest.class);
         verify(httpBidderRequester).requestBids(same(bidder1), bidRequest1Captor.capture(), any(), anyBoolean());
         final BidRequest capturedBidRequest1 = bidRequest1Captor.getValue();
-        ExtSource extSource = capturedBidRequest1.getSource().getExt();
-        ExtRequestPrebidSchainSchain requestSchain1 = extSource.getSchain();
+        final ExtRequestPrebidSchainSchain requestSchain1 = capturedBidRequest1.getSource().getExt().getSchain();
         assertThat(requestSchain1).isNotNull();
         assertThat(requestSchain1).isEqualTo(specificSchain);
         assertThat(capturedBidRequest1.getExt().getPrebid().getSchains()).isNull();
@@ -534,7 +502,7 @@ public void shouldPassRequestWithInjectedSchainInSourceExt() {
         final ArgumentCaptor bidRequest2Captor = ArgumentCaptor.forClass(BidRequest.class);
         verify(httpBidderRequester).requestBids(same(bidder2), bidRequest2Captor.capture(), any(), anyBoolean());
         final BidRequest capturedBidRequest2 = bidRequest2Captor.getValue();
-        ExtRequestPrebidSchainSchain requestSchain2 = extSource.getSchain();
+        final ExtRequestPrebidSchainSchain requestSchain2 = capturedBidRequest2.getSource().getExt().getSchain();
         assertThat(requestSchain2).isNotNull();
         assertThat(requestSchain2).isEqualTo(specificSchain);
         assertThat(capturedBidRequest2.getExt().getPrebid().getSchains()).isNull();
@@ -542,42 +510,12 @@ public void shouldPassRequestWithInjectedSchainInSourceExt() {
         final ArgumentCaptor bidRequest3Captor = ArgumentCaptor.forClass(BidRequest.class);
         verify(httpBidderRequester).requestBids(same(bidder3), bidRequest3Captor.capture(), any(), anyBoolean());
         final BidRequest capturedBidRequest3 = bidRequest3Captor.getValue();
-        ExtRequestPrebidSchainSchain requestSchain3 = extSource.getSchain();
+        final ExtRequestPrebidSchainSchain requestSchain3 = capturedBidRequest3.getSource().getExt().getSchain();
         assertThat(requestSchain3).isNotNull();
-        assertThat(requestSchain3).isEqualTo(allSchainObject);
+        assertThat(requestSchain3).isEqualTo(generalSchain);
         assertThat(capturedBidRequest3.getExt().getPrebid().getSchains()).isNull();
     }
 
-    @Test
-    public void shouldRejectDuplicatedSchainBidders() {
-        // given
-        final String bidder1 = "bidder";
-        final String bidder2 = "bidder"; // same name
-
-        final ExtRequestPrebidSchain schainForBidder1 = ExtRequestPrebidSchain.of(
-                singletonList(bidder1), ExtRequestPrebidSchainSchain.of("ver1", null, null, null));
-        final ExtRequestPrebidSchain schainForBidder2 = ExtRequestPrebidSchain.of(
-                singletonList(bidder2), ExtRequestPrebidSchainSchain.of("ver2", null, null, null));
-
-        final ExtRequest extRequest = ExtRequest.of(
-                ExtRequestPrebid.builder()
-                        .schains(asList(schainForBidder1, schainForBidder2))
-                        .build());
-
-        final BidRequest bidRequest = givenBidRequest(asList(
-                givenImp(singletonMap(bidder1, 1), identity()),
-                givenImp(singletonMap(bidder2, 2), identity())),
-                builder -> builder.ext(extRequest));
-
-        // when
-        exchangeService.holdAuction(givenRequestContext(bidRequest));
-
-        // then
-        final ArgumentCaptor bidRequestCaptor = ArgumentCaptor.forClass(BidRequest.class);
-        verify(httpBidderRequester).requestBids(any(), bidRequestCaptor.capture(), any(), anyBoolean());
-        assertThat(bidRequestCaptor.getValue().getSource()).isNull();
-    }
-
     @Test
     public void shouldReturnFailedFutureWithUnchangedMessageWhenPrivacyEnforcementServiceFails() {
         // given
@@ -693,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()))
@@ -704,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()))
@@ -943,8 +889,9 @@ public void shouldTolerateNullRequestExtPrebidTargeting() {
                 .allSatisfy(map -> assertThat(map).isNull());
     }
 
+    @SuppressWarnings("unchecked")
     @Test
-    public void shouldTolerateResponseBidValidationErrors() throws JsonProcessingException {
+    public void shouldTolerateResponseBidValidationErrors() {
         // given
         givenBidder("bidder1", mock(Bidder.class), givenSeatBid(singletonList(
                 givenBid(Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.valueOf(1.23)).build()))));
@@ -956,20 +903,67 @@ public void shouldTolerateResponseBidValidationErrors() throws JsonProcessingExc
                         .auctiontimestamp(1000L)
                         .build())));
 
-        given(responseBidValidator.validate(any()))
-                .willReturn(ValidationResult.error("bid validation error"));
-
-        final List bidderErrors = singletonList(ExtBidderError.of(BidderError.Type.generic.getCode(),
+        given(responseBidValidator.validate(any(), any(), any(), any())).willReturn(ValidationResult.error(
+                singletonList("bid validation warning"),
                 "bid validation error"));
-        givenBidResponseCreator(singletonMap("bidder1", bidderErrors));
+
+        givenBidResponseCreator(singletonList(Bid.builder().build()));
 
         // when
-        final BidResponse bidResponse = exchangeService.holdAuction(givenRequestContext(bidRequest)).result();
+        exchangeService.holdAuction(givenRequestContext(bidRequest)).result();
 
         // then
-        final ExtBidResponse ext = mapper.treeToValue(bidResponse.getExt(), ExtBidResponse.class);
-        assertThat(ext.getErrors()).hasSize(1)
-                .containsOnly(entry("bidder1", bidderErrors));
+        final ArgumentCaptor> bidderResponsesCaptor = ArgumentCaptor.forClass(List.class);
+        verify(bidResponseCreator).create(bidderResponsesCaptor.capture(), any(), any(), anyBoolean());
+        final List bidderResponses = bidderResponsesCaptor.getValue();
+
+        assertThat(bidderResponses)
+                .extracting(BidderResponse::getSeatBid)
+                .flatExtracting(BidderSeatBid::getBids)
+                .isEmpty();
+        assertThat(bidderResponses)
+                .extracting(BidderResponse::getSeatBid)
+                .flatExtracting(BidderSeatBid::getErrors)
+                .containsOnly(
+                        BidderError.generic("bid validation warning"),
+                        BidderError.generic("bid validation error"));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void shouldTolerateResponseBidValidationWarnings() {
+        // given
+        givenBidder("bidder1", mock(Bidder.class), givenSeatBid(singletonList(
+                givenBid(Bid.builder().id("bidId1").impid("impId1").price(BigDecimal.valueOf(1.23)).build()))));
+
+        final BidRequest bidRequest = givenBidRequest(singletonList(
+                // imp ids are not really used for matching, included them here for clarity
+                givenImp(singletonMap("bidder1", 1), builder -> builder.id("impId1"))),
+                builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .auctiontimestamp(1000L)
+                        .build())));
+
+        given(responseBidValidator.validate(any(), any(), any(), any())).willReturn(ValidationResult.success(
+                singletonList("bid validation warning")));
+
+        givenBidResponseCreator(singletonList(Bid.builder().build()));
+
+        // when
+        exchangeService.holdAuction(givenRequestContext(bidRequest)).result();
+
+        // then
+        final ArgumentCaptor> bidderResponsesCaptor = ArgumentCaptor.forClass(List.class);
+        verify(bidResponseCreator).create(bidderResponsesCaptor.capture(), any(), any(), anyBoolean());
+        final List bidderResponses = bidderResponsesCaptor.getValue();
+
+        assertThat(bidderResponses)
+                .extracting(BidderResponse::getSeatBid)
+                .flatExtracting(BidderSeatBid::getBids)
+                .hasSize(1);
+        assertThat(bidderResponses)
+                .extracting(BidderResponse::getSeatBid)
+                .flatExtracting(BidderSeatBid::getErrors)
+                .containsOnly(BidderError.generic("bid validation warning"));
     }
 
     @Test
@@ -986,7 +980,7 @@ public void shouldRejectBidIfCurrencyIsNotValid() throws JsonProcessingException
                         .auctiontimestamp(1000L)
                         .build())));
 
-        given(responseBidValidator.validate(any()))
+        given(responseBidValidator.validate(any(), any(), any(), any()))
                 .willReturn(ValidationResult.error("BidResponse currency is not valid: USDD"));
 
         final List bidderErrors = singletonList(ExtBidderError.of(BidderError.Type.generic.getCode(),
@@ -1011,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()))
@@ -1025,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()))
@@ -1044,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());
@@ -1203,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())));
@@ -1228,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())));
@@ -1317,9 +1326,8 @@ public void shouldPassUserExtDataOnlyForAllowedBidder() {
 
         final ObjectNode dataNode = mapper.createObjectNode().put("data", "value");
         final Map bidderToGdpr = doubleMap("someBidder", 1, "missingBidder", 0);
-        final ExtUserDigiTrust extUserDigiTrust = ExtUserDigiTrust.of("dId", 23, 222);
         final List eids = singletonList(ExtUserEid.of("eId", "id", emptyList(), null));
-        final ExtUser extUser = ExtUser.builder().data(dataNode).digitrust(extUserDigiTrust).eids(eids).build();
+        final ExtUser extUser = ExtUser.builder().data(dataNode).eids(eids).build();
 
         final BidRequest bidRequest = givenBidRequest(givenSingleImp(bidderToGdpr),
                 builder -> builder
@@ -1343,7 +1351,7 @@ public void shouldPassUserExtDataOnlyForAllowedBidder() {
         verify(httpBidderRequester, times(2)).requestBids(any(), bidRequestCaptor.capture(), any(), anyBoolean());
         final List capturedBidRequests = bidRequestCaptor.getAllValues();
 
-        final ExtUser maskedExtUser = ExtUser.builder().digitrust(extUserDigiTrust).eids(eids).build();
+        final ExtUser maskedExtUser = ExtUser.builder().eids(eids).build();
         assertThat(capturedBidRequests)
                 .extracting(BidRequest::getUser)
                 .extracting(User::getKeywords, User::getGender, User::getYob, User::getGeo, User::getExt)
@@ -1394,9 +1402,8 @@ public void shouldMaskUserExtIfDataBiddersListIsEmpty() {
 
         final ObjectNode dataNode = mapper.createObjectNode().put("data", "value");
         final Map bidderToGdpr = doubleMap("someBidder", 1, "missingBidder", 0);
-        final ExtUserDigiTrust extUserDigiTrust = ExtUserDigiTrust.of("dId", 23, 222);
         final List eids = singletonList(ExtUserEid.of("eId", "id", emptyList(), null));
-        final ExtUser extUser = ExtUser.builder().data(dataNode).digitrust(extUserDigiTrust).eids(eids).build();
+        final ExtUser extUser = ExtUser.builder().data(dataNode).eids(eids).build();
 
         final BidRequest bidRequest = givenBidRequest(givenSingleImp(bidderToGdpr),
                 builder -> builder
@@ -1418,7 +1425,7 @@ public void shouldMaskUserExtIfDataBiddersListIsEmpty() {
         verify(httpBidderRequester, times(2)).requestBids(any(), bidRequestCaptor.capture(), any(), anyBoolean());
         final List capturedBidRequests = bidRequestCaptor.getAllValues();
 
-        final ExtUser expectedExtUser = ExtUser.builder().digitrust(extUserDigiTrust).eids(eids).build();
+        final ExtUser expectedExtUser = ExtUser.builder().eids(eids).build();
         assertThat(capturedBidRequests)
                 .extracting(BidRequest::getUser)
                 .extracting(User::getKeywords, User::getGender, User::getYob, User::getGeo, User::getExt)
@@ -1789,6 +1796,7 @@ public void shouldPassReducedGlobalTimeoutToConnectorAndOriginalToBidResponseCre
                 storedResponseProcessor,
                 privacyEnforcementService,
                 fpdResolver,
+                schainResolver,
                 httpBidderRequester,
                 responseBidValidator,
                 currencyService,
@@ -1831,7 +1839,7 @@ public void shouldReturnBidsWithUpdatedPriceCurrencyConversion() {
                 identity());
 
         final BigDecimal updatedPrice = BigDecimal.valueOf(5.0);
-        given(currencyService.convertCurrency(any(), any(), any(), any(), anyBoolean())).willReturn(updatedPrice);
+        given(currencyService.convertCurrency(any(), any(), any(), any())).willReturn(updatedPrice);
 
         givenBidResponseCreator(singletonList(Bid.builder().price(updatedPrice).build()));
 
@@ -1855,7 +1863,7 @@ public void shouldReturnSameBidPriceIfNoChangesAppliedToBidPrice() {
                 identity());
 
         // returns the same price as in argument
-        given(currencyService.convertCurrency(any(), any(), any(), any(), anyBoolean()))
+        given(currencyService.convertCurrency(any(), any(), any(), any()))
                 .willAnswer(invocationOnMock -> invocationOnMock.getArgument(0));
 
         // when
@@ -1878,7 +1886,7 @@ public void shouldDropBidIfPrebidExceptionWasThrownDuringCurrencyConversion() {
         final BidRequest bidRequest = givenBidRequest(singletonList(givenImp(singletonMap("bidder", 2), identity())),
                 identity());
 
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any()))
+        given(currencyService.convertCurrency(any(), any(), any(), any()))
                 .willThrow(new PreBidException("Unable to convert bid currency CUR to desired ad server currency USD"));
 
         // when
@@ -1912,7 +1920,7 @@ public void shouldUpdateBidPriceWithCurrencyConversionAndPriceAdjustmentFactor()
                         .auctiontimestamp(1000L)
                         .build())));
 
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any()))
+        given(currencyService.convertCurrency(any(), any(), any(), any()))
                 .willReturn(BigDecimal.valueOf(10));
 
         // when
@@ -1947,7 +1955,7 @@ public void shouldUpdatePriceForOneBidAndDropAnotherIfPrebidExceptionHappensForS
                 identity());
 
         final BigDecimal updatedPrice = BigDecimal.valueOf(10.0);
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any())).willReturn(updatedPrice)
+        given(currencyService.convertCurrency(any(), any(), any(), any())).willReturn(updatedPrice)
                 .willThrow(
                         new PreBidException("Unable to convert bid currency CUR2 to desired ad server currency USD"));
 
@@ -1957,8 +1965,8 @@ public void shouldUpdatePriceForOneBidAndDropAnotherIfPrebidExceptionHappensForS
         // then
         final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class);
         verify(bidResponseCreator).create(argumentCaptor.capture(), any(), any(), anyBoolean());
-        verify(currencyService).convertCurrency(eq(firstBidderPrice), eq(null), any(), eq("CUR1"), eq(null));
-        verify(currencyService).convertCurrency(eq(secondBidderPrice), eq(null), any(), eq("CUR2"), eq(null));
+        verify(currencyService).convertCurrency(eq(firstBidderPrice), eq(bidRequest), any(), eq("CUR1"));
+        verify(currencyService).convertCurrency(eq(secondBidderPrice), eq(bidRequest), any(), eq("CUR2"));
 
         assertThat(argumentCaptor.getValue()).hasSize(1);
 
@@ -1988,8 +1996,8 @@ public void shouldRespondWithOneBidAndErrorWhenBidResponseContainsOneUnsupported
                         identity()))).build();
 
         final BigDecimal updatedPrice = BigDecimal.valueOf(20);
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any())).willReturn(updatedPrice);
-        given(currencyService.convertCurrency(any(), any(), eq("BAD"), eq("CUR"), any()))
+        given(currencyService.convertCurrency(any(), any(), any(), any())).willReturn(updatedPrice);
+        given(currencyService.convertCurrency(any(), any(), eq("BAD"), eq("CUR")))
                 .willThrow(new PreBidException("Unable to convert bid currency CUR to desired ad server currency BAD"));
 
         // when
@@ -1998,8 +2006,8 @@ public void shouldRespondWithOneBidAndErrorWhenBidResponseContainsOneUnsupported
         // then
         final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class);
         verify(bidResponseCreator).create(argumentCaptor.capture(), any(), any(), anyBoolean());
-        verify(currencyService).convertCurrency(eq(firstBidderPrice), eq(null), eq("BAD"), eq("USD"), eq(null));
-        verify(currencyService).convertCurrency(eq(secondBidderPrice), eq(null), eq("BAD"), eq("CUR"), eq(null));
+        verify(currencyService).convertCurrency(eq(firstBidderPrice), eq(bidRequest), eq("BAD"), eq("USD"));
+        verify(currencyService).convertCurrency(eq(secondBidderPrice), eq(bidRequest), eq("BAD"), eq("CUR"));
 
         assertThat(argumentCaptor.getValue()).hasSize(2);
 
@@ -2031,7 +2039,7 @@ public void shouldUpdateBidPriceWithCurrencyConversionAndAddErrorAboutMultipleCu
                 builder -> builder.cur(asList("CUR1", "CUR2", "CUR2")));
 
         final BigDecimal updatedPrice = BigDecimal.valueOf(10.0);
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any())).willReturn(updatedPrice);
+        given(currencyService.convertCurrency(any(), any(), any(), any())).willReturn(updatedPrice);
 
         // when
         exchangeService.holdAuction(givenRequestContext(bidRequest)).result();
@@ -2039,7 +2047,7 @@ public void shouldUpdateBidPriceWithCurrencyConversionAndAddErrorAboutMultipleCu
         // then
         final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class);
         verify(bidResponseCreator).create(argumentCaptor.capture(), any(), any(), anyBoolean());
-        verify(currencyService).convertCurrency(eq(bidderPrice), eq(null), eq("CUR1"), eq("USD"), eq(null));
+        verify(currencyService).convertCurrency(eq(bidderPrice), eq(bidRequest), eq("CUR1"), eq("USD"));
 
         assertThat(argumentCaptor.getValue()).hasSize(1);
 
@@ -2075,8 +2083,8 @@ public void shouldUpdateBidPriceWithCurrencyConversionForMultipleBid() {
                 singletonList(givenImp(impBidders, identity())), builder -> builder.cur(singletonList("USD")));
 
         final BigDecimal updatedPrice = BigDecimal.valueOf(10.0);
-        given(currencyService.convertCurrency(any(), any(), any(), any(), any())).willReturn(updatedPrice);
-        given(currencyService.convertCurrency(any(), any(), any(), eq("USD"), any())).willReturn(bidder3Price);
+        given(currencyService.convertCurrency(any(), any(), any(), any())).willReturn(updatedPrice);
+        given(currencyService.convertCurrency(any(), any(), any(), eq("USD"))).willReturn(bidder3Price);
 
         // when
         exchangeService.holdAuction(givenRequestContext(bidRequest)).result();
@@ -2084,9 +2092,9 @@ public void shouldUpdateBidPriceWithCurrencyConversionForMultipleBid() {
         // then
         final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(List.class);
         verify(bidResponseCreator).create(argumentCaptor.capture(), any(), any(), anyBoolean());
-        verify(currencyService).convertCurrency(eq(bidder1Price), eq(null), eq("USD"), eq("EUR"), eq(null));
-        verify(currencyService).convertCurrency(eq(bidder2Price), eq(null), eq("USD"), eq("GBP"), eq(null));
-        verify(currencyService).convertCurrency(eq(bidder3Price), eq(null), eq("USD"), eq("USD"), eq(null));
+        verify(currencyService).convertCurrency(eq(bidder1Price), eq(bidRequest), eq("USD"), eq("EUR"));
+        verify(currencyService).convertCurrency(eq(bidder2Price), eq(bidRequest), eq("USD"), eq("GBP"));
+        verify(currencyService).convertCurrency(eq(bidder3Price), eq(bidRequest), eq("USD"), eq("USD"));
         verifyNoMoreInteractions(currencyService);
 
         assertThat(argumentCaptor.getValue())
@@ -2323,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/FpdResolverTest.java b/src/test/java/org/prebid/server/auction/FpdResolverTest.java
index bb0c6def595..46e9bec4fda 100644
--- a/src/test/java/org/prebid/server/auction/FpdResolverTest.java
+++ b/src/test/java/org/prebid/server/auction/FpdResolverTest.java
@@ -14,6 +14,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.prebid.server.VertxTest;
+import org.prebid.server.json.JsonMerger;
 import org.prebid.server.proto.openrtb.ext.request.ExtApp;
 import org.prebid.server.proto.openrtb.ext.request.ExtAppPrebid;
 import org.prebid.server.proto.openrtb.ext.request.ExtBidderConfig;
@@ -40,7 +41,7 @@ public class FpdResolverTest extends VertxTest {
 
     @Before
     public void setUp() {
-        fpdResolver = new FpdResolver(jacksonMapper);
+        fpdResolver = new FpdResolver(jacksonMapper, new JsonMerger(jacksonMapper));
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/auction/ImplicitParametersExtractorTest.java b/src/test/java/org/prebid/server/auction/ImplicitParametersExtractorTest.java
index 39bf00d299a..12b57310bbc 100644
--- a/src/test/java/org/prebid/server/auction/ImplicitParametersExtractorTest.java
+++ b/src/test/java/org/prebid/server/auction/ImplicitParametersExtractorTest.java
@@ -85,6 +85,14 @@ public void refererFromShouldReturnRefererWithHttpSchemeIfRefererHeaderDoesNotCo
         assertThat(extractor.refererFrom(httpRequest)).isEqualTo("http://example.com");
     }
 
+    @Test
+    public void domainFromShouldFailIfUrlIsMissing() {
+        assertThatCode(() -> extractor.domainFrom(null))
+                .isInstanceOf(PreBidException.class)
+                .hasMessage("Invalid URL 'null': null")
+                .hasCauseInstanceOf(MalformedURLException.class);
+    }
+
     @Test
     public void domainFromShouldFailIfUrlCouldNotBeParsed() {
         assertThatCode(() -> extractor.domainFrom("httpP://non_an_url"))
diff --git a/src/test/java/org/prebid/server/auction/OrtbTypesResolverTest.java b/src/test/java/org/prebid/server/auction/OrtbTypesResolverTest.java
index 3071553c82b..89ef6ec9751 100644
--- a/src/test/java/org/prebid/server/auction/OrtbTypesResolverTest.java
+++ b/src/test/java/org/prebid/server/auction/OrtbTypesResolverTest.java
@@ -5,6 +5,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.junit.Test;
 import org.prebid.server.VertxTest;
+import org.prebid.server.json.JsonMerger;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -13,7 +14,8 @@
 
 public class OrtbTypesResolverTest extends VertxTest {
 
-    private final OrtbTypesResolver ortbTypesResolver = new OrtbTypesResolver(jacksonMapper);
+    private final OrtbTypesResolver ortbTypesResolver =
+            new OrtbTypesResolver(jacksonMapper, new JsonMerger(jacksonMapper));
 
     @Test
     public void normalizeTargetingShouldNotChangeNodeIfItsTypeIsNotObject() {
diff --git a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java b/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java
index f720c3cfa1e..10bd0b29045 100644
--- a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java
+++ b/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java
@@ -42,7 +42,6 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid;
 import org.prebid.server.proto.request.CookieSyncRequest;
@@ -53,6 +52,7 @@
 import java.time.Clock;
 import java.time.Instant;
 import java.time.ZoneId;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -66,9 +66,11 @@
 import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
+import static java.util.function.UnaryOperator.identity;
 import static org.apache.commons.lang3.StringUtils.EMPTY;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -119,7 +121,7 @@ public void setUp() {
         privacyExtractor = new PrivacyExtractor();
 
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, false);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, false, false);
     }
 
     @Test
@@ -131,7 +133,7 @@ public void contextFromBidRequestShouldReturnCoppaContext() {
 
         // when
         final Future privacyContext = privacyEnforcementService.contextFromBidRequest(
-                bidRequest, Account.empty("account"), null, null);
+                bidRequest, Account.empty("account"), null, null, new ArrayList<>());
 
         // then
         FutureAssertion.assertThat(privacyContext).succeededWith(
@@ -165,7 +167,7 @@ public void contextFromBidRequestShouldReturnTcfContext() {
 
         // when
         final Future privacyContext = privacyEnforcementService.contextFromBidRequest(
-                bidRequest, Account.empty(accountId), requestType, null);
+                bidRequest, Account.empty(accountId), requestType, null, new ArrayList<>());
 
         // then
         final Privacy privacy = Privacy.of("1", "consent", Ccpa.of("1YYY"), 0);
@@ -207,7 +209,7 @@ public void contextFromBidRequestShouldReturnTcfContextWithIpMasked() {
 
         // when
         final Future privacyContext = privacyEnforcementService.contextFromBidRequest(
-                bidRequest, Account.empty("account"), MetricName.openrtb2web, null);
+                bidRequest, Account.empty("account"), MetricName.openrtb2web, null, new ArrayList<>());
 
         // then
         final Privacy privacy = Privacy.of("1", "consent", Ccpa.of("1YYY"), 0);
@@ -323,7 +325,7 @@ public void contextFromCookieSyncRequestShouldReturnContext() {
     }
 
     @Test
-    public void shouldMaskForCoppaWhenDeviceLmtIsOneAndRegsCoppaIsOneAndDoesNotCallTcfServices() {
+    public void shouldMaskForCoppaWhenDeviceLmtIsEnforceAndOneAndRegsCoppaIsOneAndDoesNotCallTcfServices() {
         // given
         final User user = notMaskedUser(notMaskedExtUser());
         final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1));
@@ -358,7 +360,7 @@ public void shouldMaskForCoppaWhenDeviceLmtIsOneAndRegsCoppaIsOneAndDoesNotCallT
     public void shouldMaskForCcpaWhenUsPolicyIsValidAndCoppaIsZero() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any()))
                 .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null)));
@@ -492,8 +494,11 @@ public void shouldNotMaskWhenDeviceLmtIsZeroAndCoppaIsZeroAndGdprIsZeroAndTcfDef
     }
 
     @Test
-    public void shouldMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOne() {
+    public void shouldMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOneAndLmtIsEnforced() {
         // given
+        privacyEnforcementService = new PrivacyEnforcementService(
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, false, true);
+
         given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
                 .willReturn(Future.succeededFuture(
                         TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null)));
@@ -528,6 +533,46 @@ public void shouldMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOne() {
         verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any());
     }
 
+    @Test
+    public void shouldNotMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOneAndLmtIsNotEnforced() {
+        // given
+        given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
+                .willReturn(Future.succeededFuture(
+                        TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null)));
+
+        final User user = notMaskedUser();
+        final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1));
+        final Regs regs = Regs.of(0, null);
+        final Map bidderToUser = singletonMap(BIDDER_NAME, user);
+
+        final BidRequest bidRequest = givenBidRequest(givenSingleImp(
+                singletonMap(BIDDER_NAME, 1)),
+                bidRequestBuilder -> bidRequestBuilder
+                        .user(user)
+                        .device(device)
+                        .regs(regs));
+
+        final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0);
+
+        final AuctionContext context = auctionContext(bidRequest, privacyContext);
+
+        // when
+        final List result = privacyEnforcementService
+                .mask(context, bidderToUser, singletonList(BIDDER_NAME), BidderAliases.of(null, null, bidderCatalog))
+                .result();
+
+        // then
+        final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder()
+                .user(user)
+                .device(device)
+                .requestBidder(BIDDER_NAME)
+                .build();
+        assertThat(result).containsOnly(expectedBidderPrivacy);
+
+        verify(tcfDefinerService)
+                .resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any());
+    }
+
     @Test
     public void shouldMaskForTcfWhenTcfDefinerServiceRestrictDeviceAndUser() {
         // given
@@ -602,6 +647,9 @@ public void shouldMaskUserIdsWhenTcfDefinerServiceRestrictUserIds() {
         assertThat(result).containsOnly(expectedBidderPrivacy);
 
         verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any());
+
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(true), anyBoolean(), anyBoolean(),
+                anyBoolean());
     }
 
     @Test
@@ -616,7 +664,6 @@ public void shouldMaskUserIdsWhenTcfDefinerServiceRestrictUserIdsAndReturnNullWh
 
         final ExtUser extUser = ExtUser.builder()
                 .eids(singletonList(ExtUserEid.of("Test", "id", emptyList(), null)))
-                .digitrust(ExtUserDigiTrust.of("idDigit", 12, 23))
                 .build();
         final User user = User.builder()
                 .buyeruid(BUYER_UID)
@@ -686,6 +733,9 @@ public void shouldMaskGeoWhenTcfDefinerServiceRestrictGeo() {
         assertThat(result).containsOnly(expectedBidderPrivacy);
 
         verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any());
+
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), anyBoolean(), eq(true), anyBoolean(),
+                anyBoolean());
     }
 
     @Test
@@ -775,6 +825,118 @@ public void shouldMaskDeviceInfoWhenTcfDefinerServiceRestrictDeviceInfo() {
         verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any());
     }
 
+    @Test
+    public void shouldSendAnalyticsBlockedMetricIfRestrictedByPrivacyEnforcement() {
+        // given
+        final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll();
+        privacyEnforcementAction.setBlockAnalyticsReport(true);
+
+        given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
+                .willReturn(Future.succeededFuture(
+                        TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null)));
+
+        final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap(BIDDER_NAME, 1)), identity());
+        final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0);
+        final AuctionContext context = auctionContext(bidRequest, privacyContext);
+
+        // when
+        privacyEnforcementService.mask(context, emptyMap(), singletonList(BIDDER_NAME), aliases);
+
+        // then
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), anyBoolean(), anyBoolean(), eq(true),
+                anyBoolean());
+    }
+
+    @Test
+    public void shouldNotSendRelatedMetricsIfBlockBidderRequestEnforcementIsPresent() {
+        // given
+        final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll();
+        privacyEnforcementAction.setBlockBidderRequest(true); // has highest priority
+        privacyEnforcementAction.setRemoveUserIds(true);
+        privacyEnforcementAction.setMaskGeo(true);
+        privacyEnforcementAction.setBlockAnalyticsReport(true);
+
+        given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
+                .willReturn(Future.succeededFuture(
+                        TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null)));
+
+        final ExtUser extUser = notMaskedExtUser();
+        final User user = notMaskedUser(extUser);
+        final Map bidderToUser = singletonMap(BIDDER_NAME, user);
+
+        final BidRequest bidRequest = givenBidRequest(givenSingleImp(
+                singletonMap(BIDDER_NAME, 1)),
+                bidRequestBuilder -> bidRequestBuilder
+                        .user(user)
+                        .device(notMaskedDevice()));
+        final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0);
+
+        final AuctionContext context = auctionContext(bidRequest, privacyContext);
+
+        // when
+        privacyEnforcementService.mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases);
+
+        // then
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), eq(false), eq(false),
+                eq(true));
+    }
+
+    @Test
+    public void shouldNotSendUserIdRemovedMetricIfNoPrivateUserInformation() {
+        // given
+        final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll();
+        privacyEnforcementAction.setRemoveUserIds(true);
+
+        given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
+                .willReturn(Future.succeededFuture(
+                        TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null)));
+
+        final ExtUser extUser = ExtUser.builder().consent("consent").build();
+        final User user = User.builder().gender("gender").ext(extUser).build();
+        final Map bidderToUser = singletonMap(BIDDER_NAME, user);
+
+        final BidRequest bidRequest = givenBidRequest(givenSingleImp(
+                singletonMap(BIDDER_NAME, 1)),
+                bidRequestBuilder -> bidRequestBuilder
+                        .user(user));
+        final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0);
+
+        final AuctionContext context = auctionContext(bidRequest, privacyContext);
+
+        // when
+        privacyEnforcementService.mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases);
+
+        // then
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), anyBoolean(), anyBoolean(),
+                anyBoolean());
+    }
+
+    @Test
+    public void shouldNotSendGeoMaskedMetricIfNoPrivateGeoInformation() {
+        // given
+        final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll();
+        privacyEnforcementAction.setMaskGeo(true);
+
+        given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any()))
+                .willReturn(Future.succeededFuture(
+                        TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null)));
+
+        final BidRequest bidRequest = givenBidRequest(givenSingleImp(
+                singletonMap(BIDDER_NAME, 1)),
+                bidRequestBuilder -> bidRequestBuilder
+                        .device(Device.builder().model("blackberry").build()));
+        final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0);
+
+        final AuctionContext context = auctionContext(bidRequest, privacyContext);
+
+        // when
+        privacyEnforcementService.mask(context, emptyMap(), singletonList(BIDDER_NAME), aliases);
+
+        // then
+        verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), anyBoolean(), anyBoolean(),
+                anyBoolean());
+    }
+
     @Test
     public void shouldRerunEmptyResultWhenTcfDefinerServiceRestrictRequest() {
         // given
@@ -802,7 +964,6 @@ public void shouldRerunEmptyResultWhenTcfDefinerServiceRestrictRequest() {
                 .result();
 
         // then
-
         final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder()
                 .requestBidder(BIDDER_NAME)
                 .blockedRequestByTcf(true)
@@ -900,7 +1061,6 @@ public void shouldNotReturnUserIfMaskingAppliedAndUserBecameEmptyObject() {
         // given
         final ExtUser extUser = ExtUser.builder()
                 .eids(singletonList(ExtUserEid.of("Test", "id", emptyList(), null)))
-                .digitrust(ExtUserDigiTrust.of("idDigit", 12, 23))
                 .build();
         final User user = User.builder()
                 .buyeruid("buyeruid")
@@ -967,7 +1127,7 @@ public void shouldReturnFailedFutureWhenTcfServiceIsReturnFailedFuture() {
     public void shouldMaskForCcpaAndTcfWhenUsPolicyIsValidAndGdprIsEnforcedAndCOPPAIsZero() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         final String bidder1Name = "bidder1Name";
         final String bidder2Name = "bidder2Name";
@@ -1037,7 +1197,7 @@ public void shouldMaskForCcpaAndTcfWhenUsPolicyIsValidAndGdprIsEnforcedAndCOPPAI
     public void shouldNotMaskForCcpaWhenCatchAllWildcardIsPresentInNosaleList() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any()))
                 .willReturn(Future.succeededFuture(
@@ -1091,7 +1251,7 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsFalseInConfigur
     public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrueInConfigurationAndFalseInAccount() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         final Ccpa ccpa = Ccpa.of("1YYY");
         final Account account = Account.builder().enforceCcpa(false).build();
@@ -1104,7 +1264,7 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrueInConfigura
     public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrue() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         final Ccpa ccpa = Ccpa.of("1YNY");
         final Account account = Account.builder().build();
@@ -1117,7 +1277,7 @@ public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrue() {
     public void isCcpaEnforcedShouldReturnTrueWhenEnforcedPropertyIsTrueAndCcpaReturnsTrue() {
         // given
         privacyEnforcementService = new PrivacyEnforcementService(
-                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true);
+                bidderCatalog, privacyExtractor, tcfDefinerService, ipAddressHelper, metrics, true, false);
 
         final Ccpa ccpa = Ccpa.of("1YYY");
         final Account account = Account.builder().build();
@@ -1183,7 +1343,8 @@ public void shouldReturnCorrectMaskedForMultipleBidders() {
                 .user(notMaskedUser())
                 .device(notMaskedDevice())
                 .build();
-        assertThat(result).hasSize(3).containsOnly(expectedBidder1Masked, expectedBidder2Masked, expectedBidder3Masked);
+        assertThat(result).hasSize(3)
+                .containsOnly(expectedBidder1Masked, expectedBidder2Masked, expectedBidder3Masked);
 
         final HashSet bidderNames = new HashSet<>(asList(bidder1Name, bidder2Name, bidder3Name));
         verify(tcfDefinerService).resultForBidderNames(eq(bidderNames), any(), any(), any());
@@ -1267,7 +1428,6 @@ private static User notMaskedUser(ExtUser extUser) {
     private static ExtUser notMaskedExtUser() {
         return ExtUser.builder()
                 .eids(singletonList(ExtUserEid.of("Test", "id", emptyList(), null)))
-                .digitrust(ExtUserDigiTrust.of("idDigit", 12, 23))
                 .prebid(ExtUserPrebid.of(singletonMap("key", "value")))
                 .build();
     }
@@ -1339,7 +1499,7 @@ private static Device givenCoppaMaskedDevice(UnaryOperator
     }
 
     private static  List givenSingleImp(T ext) {
-        return singletonList(givenImp(ext, UnaryOperator.identity()));
+        return singletonList(givenImp(ext, identity()));
     }
 
     private static  Imp givenImp(T ext, UnaryOperator impBuilderCustomizer) {
diff --git a/src/test/java/org/prebid/server/auction/SchainResolverTest.java b/src/test/java/org/prebid/server/auction/SchainResolverTest.java
new file mode 100644
index 00000000000..15109706c6e
--- /dev/null
+++ b/src/test/java/org/prebid/server/auction/SchainResolverTest.java
@@ -0,0 +1,164 @@
+package org.prebid.server.auction;
+
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Source;
+import org.junit.Before;
+import org.junit.Test;
+import org.prebid.server.VertxTest;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchain;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchainSchain;
+import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchainSchainNode;
+import org.prebid.server.proto.openrtb.ext.request.ExtSource;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SchainResolverTest extends VertxTest {
+
+    private SchainResolver schainResolver;
+
+    @Before
+    public void setUp() {
+        schainResolver = SchainResolver.create(null, jacksonMapper);
+    }
+
+    @Test
+    public void shouldResolveSchainsWhenCatchAllPresent() {
+        // given
+        final ExtRequestPrebidSchainSchainNode specificNodes = ExtRequestPrebidSchainSchainNode.of(
+                "asi", "sid", 1, "rid", "name", "domain", null);
+        final ExtRequestPrebidSchainSchain specificSchain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, singletonList(specificNodes), null);
+        final ExtRequestPrebidSchain schainForBidders = ExtRequestPrebidSchain.of(
+                asList("bidder1", "bidder2"), specificSchain);
+
+        final ExtRequestPrebidSchainSchainNode generalNodes = ExtRequestPrebidSchainSchainNode.of(
+                "t", null, 0, "a", null, "ads", null);
+        final ExtRequestPrebidSchainSchain generalSchain = ExtRequestPrebidSchainSchain.of(
+                "t", 123, singletonList(generalNodes), null);
+        final ExtRequestPrebidSchain allSchain = ExtRequestPrebidSchain.of(singletonList("*"), generalSchain);
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .schains(asList(schainForBidders, allSchain))
+                        .build()))
+                .build();
+
+        // when and then
+        assertThat(schainResolver.resolveForBidder("bidder1", bidRequest)).isSameAs(specificSchain);
+        assertThat(schainResolver.resolveForBidder("bidder2", bidRequest)).isSameAs(specificSchain);
+        assertThat(schainResolver.resolveForBidder("bidder3", bidRequest)).isSameAs(generalSchain);
+    }
+
+    @Test
+    public void shouldReturnNullWhenAbsentForBidderAndNoCatchAll() {
+        // given
+        final ExtRequestPrebidSchainSchainNode specificNodes = ExtRequestPrebidSchainSchainNode.of(
+                "asi", "sid", 1, "rid", "name", "domain", null);
+        final ExtRequestPrebidSchainSchain specificSchain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, singletonList(specificNodes), null);
+        final ExtRequestPrebidSchain schainForBidders = ExtRequestPrebidSchain.of(
+                singletonList("bidder1"), specificSchain);
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .schains(singletonList(schainForBidders))
+                        .build()))
+                .build();
+
+        // when and then
+        assertThat(schainResolver.resolveForBidder("bidder2", bidRequest)).isNull();
+    }
+
+    @Test
+    public void shouldIgnoreDuplicatedBidderSchains() {
+        // given
+        final ExtRequestPrebidSchain schain1 = ExtRequestPrebidSchain.of(
+                singletonList("bidder"), ExtRequestPrebidSchainSchain.of("ver1", null, null, null));
+        final ExtRequestPrebidSchain schain2 = ExtRequestPrebidSchain.of(
+                singletonList("bidder"), ExtRequestPrebidSchainSchain.of("ver2", null, null, null));
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .schains(asList(schain1, schain2))
+                        .build()))
+                .build();
+
+        // when and then
+        assertThat(schainResolver.resolveForBidder("bidder", bidRequest)).isNull();
+    }
+
+    @Test
+    public void shouldInjectGlobalNodeIntoResolvedSchain() {
+        // given
+        schainResolver = SchainResolver.create(
+                "{\"asi\": \"pbshostcompany.com\", \"sid\":\"00001\"}",
+                jacksonMapper);
+
+        final ExtRequestPrebidSchainSchainNode node = ExtRequestPrebidSchainSchainNode.of(
+                "asi", "sid", 1, "rid", "name", "domain", null);
+        final ExtRequestPrebidSchainSchain schain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, singletonList(node), null);
+        final ExtRequestPrebidSchain schainEntry = ExtRequestPrebidSchain.of(
+                singletonList("bidder"), schain);
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .schains(singletonList(schainEntry))
+                        .build()))
+                .build();
+
+        // when and then
+        final ExtRequestPrebidSchainSchainNode globalNode = ExtRequestPrebidSchainSchainNode.of(
+                "pbshostcompany.com", "00001", null, null, null, null, null);
+        final ExtRequestPrebidSchainSchain expectedSchain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, asList(node, globalNode), null);
+        assertThat(schainResolver.resolveForBidder("bidder", bidRequest)).isEqualTo(expectedSchain);
+    }
+
+    @Test
+    public void shouldReturnSchainWithGlobalNodeOnly() {
+        // given
+        schainResolver = SchainResolver.create(
+                "{\"asi\": \"pbshostcompany.com\", \"sid\":\"00001\"}",
+                jacksonMapper);
+
+        final BidRequest bidRequest = BidRequest.builder().build();
+
+        // when and then
+        final ExtRequestPrebidSchainSchainNode globalNode = ExtRequestPrebidSchainSchainNode.of(
+                "pbshostcompany.com", "00001", null, null, null, null, null);
+        final ExtRequestPrebidSchainSchain expectedSchain = ExtRequestPrebidSchainSchain.of(
+                null, null, singletonList(globalNode), null);
+        assertThat(schainResolver.resolveForBidder("bidder", bidRequest)).isEqualTo(expectedSchain);
+    }
+
+    @Test
+    public void shouldInjectGlobalNodeIntoExistingSchain() {
+        // given
+        schainResolver = SchainResolver.create(
+                "{\"asi\": \"pbshostcompany.com\", \"sid\":\"00001\"}",
+                jacksonMapper);
+
+        final ExtRequestPrebidSchainSchainNode node = ExtRequestPrebidSchainSchainNode.of(
+                "asi", "sid", 1, "rid", "name", "domain", null);
+        final ExtRequestPrebidSchainSchain schain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, singletonList(node), null);
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .source(Source.builder()
+                        .ext(ExtSource.of(schain))
+                        .build())
+                .build();
+
+        // when and then
+        final ExtRequestPrebidSchainSchainNode globalNode = ExtRequestPrebidSchainSchainNode.of(
+                "pbshostcompany.com", "00001", null, null, null, null, null);
+        final ExtRequestPrebidSchainSchain expectedSchain = ExtRequestPrebidSchainSchain.of(
+                "ver", 1, asList(node, globalNode), null);
+        assertThat(schainResolver.resolveForBidder("bidder", bidRequest)).isEqualTo(expectedSchain);
+    }
+}
diff --git a/src/test/java/org/prebid/server/auction/StoredRequestProcessorTest.java b/src/test/java/org/prebid/server/auction/StoredRequestProcessorTest.java
index 4eae1ad5459..fe1911813ec 100644
--- a/src/test/java/org/prebid/server/auction/StoredRequestProcessorTest.java
+++ b/src/test/java/org/prebid/server/auction/StoredRequestProcessorTest.java
@@ -9,6 +9,8 @@
 import com.iab.openrtb.request.Imp.ImpBuilder;
 import com.iab.openrtb.request.Video;
 import io.vertx.core.Future;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.file.FileSystem;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -20,6 +22,7 @@
 import org.prebid.server.exception.InvalidRequestException;
 import org.prebid.server.execution.Timeout;
 import org.prebid.server.execution.TimeoutFactory;
+import org.prebid.server.json.JsonMerger;
 import org.prebid.server.metric.Metrics;
 import org.prebid.server.proto.openrtb.ext.request.ExtImp;
 import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid;
@@ -50,6 +53,7 @@
 import static org.assertj.core.api.Assertions.entry;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.verify;
@@ -57,9 +61,13 @@
 
 public class StoredRequestProcessorTest extends VertxTest {
 
+    private static final int DEFAULT_TIMEOUT = 500;
+
     @Rule
     public final MockitoRule mockitoRule = MockitoJUnit.rule();
 
+    @Mock
+    private FileSystem fileSystem;
     @Mock
     private ApplicationSettings applicationSettings;
     @Mock
@@ -70,12 +78,15 @@ public class StoredRequestProcessorTest extends VertxTest {
     @Before
     public void setUp() {
         final TimeoutFactory timeoutFactory = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault()));
-        storedRequestProcessor = new StoredRequestProcessor(
-                500,
+        storedRequestProcessor = StoredRequestProcessor.create(
+                DEFAULT_TIMEOUT,
+                null,
+                fileSystem,
                 applicationSettings,
                 metrics,
                 timeoutFactory,
-                jacksonMapper);
+                jacksonMapper,
+                new JsonMerger(jacksonMapper));
     }
 
     @Test
@@ -156,6 +167,52 @@ public void shouldReturnMergedBidRequest() throws IOException {
                 .build());
     }
 
+    @Test
+    public void shouldReturnMergedDefaultAndBidRequest() throws IOException {
+        // given
+        given(fileSystem.readFileBlocking(anyString()))
+                .willReturn(Buffer.buffer(mapper.writeValueAsString(BidRequest.builder().at(1).build())));
+
+        final TimeoutFactory timeoutFactory = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault()));
+        storedRequestProcessor = StoredRequestProcessor.create(
+                DEFAULT_TIMEOUT,
+                "path/to/default/request.json",
+                fileSystem,
+                applicationSettings,
+                metrics,
+                timeoutFactory,
+                jacksonMapper,
+                new JsonMerger(jacksonMapper));
+
+        final BidRequest bidRequest = givenBidRequest(builder -> builder
+                .ext(ExtRequest.of(ExtRequestPrebid.builder()
+                        .storedrequest(ExtStoredRequest.of("123"))
+                        .build())));
+
+        final String storedRequestBidRequestJson = mapper.writeValueAsString(BidRequest.builder()
+                .id("test-request-id")
+                .tmax(1000L)
+                .imp(singletonList(Imp.builder().build()))
+                .build());
+        given(applicationSettings.getStoredData(any(), anySet(), anySet(), any()))
+                .willReturn(Future.succeededFuture(
+                        StoredDataResult.of(singletonMap("123", storedRequestBidRequestJson), emptyMap(),
+                                emptyList())));
+
+        // when
+        final Future bidRequestFuture = storedRequestProcessor.processStoredRequests(null, bidRequest);
+
+        // then
+        assertThat(bidRequestFuture.succeeded()).isTrue();
+        assertThat(bidRequestFuture.result()).isEqualTo(BidRequest.builder()
+                .id("test-request-id")
+                .at(1)
+                .tmax(1000L)
+                .imp(singletonList(Imp.builder().build()))
+                .ext(ExtRequest.of(ExtRequestPrebid.builder().storedrequest(ExtStoredRequest.of("123")).build()))
+                .build());
+    }
+
     @Test
     public void shouldReturnAmpRequest() throws IOException {
         // given
@@ -174,6 +231,39 @@ public void shouldReturnAmpRequest() throws IOException {
                 .build());
     }
 
+    @Test
+    public void shouldReturnMergedDefaultAndAmpRequest() throws IOException {
+        // given
+        given(fileSystem.readFileBlocking(anyString()))
+                .willReturn(Buffer.buffer(mapper.writeValueAsString(BidRequest.builder().at(1).build())));
+
+        final TimeoutFactory timeoutFactory = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault()));
+        storedRequestProcessor = StoredRequestProcessor.create(
+                DEFAULT_TIMEOUT,
+                "path/to/default/request.json",
+                fileSystem,
+                applicationSettings,
+                metrics,
+                timeoutFactory,
+                jacksonMapper,
+                new JsonMerger(jacksonMapper));
+
+        given(applicationSettings.getAmpStoredData(any(), anySet(), anySet(), any()))
+                .willReturn(Future.succeededFuture(StoredDataResult.of(
+                        singletonMap("123", mapper.writeValueAsString(
+                                BidRequest.builder().id("test-request-id").build())), emptyMap(), emptyList())));
+
+        // when
+        final Future bidRequestFuture = storedRequestProcessor.processAmpRequest(null, "123");
+
+        // then
+        assertThat(bidRequestFuture.succeeded()).isTrue();
+        assertThat(bidRequestFuture.result()).isEqualTo(BidRequest.builder()
+                .id("test-request-id")
+                .at(1)
+                .build());
+    }
+
     @Test
     public void shouldReturnFailedFutureWhenStoredBidRequestJsonIsNotValid() {
         // given
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/auction/TargetingKeywordsCreatorTest.java b/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java
index 48860242eb9..f1618347f69 100644
--- a/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java
+++ b/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java
@@ -8,6 +8,7 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange;
 import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity;
 import org.prebid.server.proto.response.Bid;
+import org.prebid.server.proto.response.MediaType;
 
 import java.math.BigDecimal;
 import java.util.Map;
@@ -29,7 +30,8 @@ public class TargetingKeywordsCreatorTest {
     @Test
     public void shouldReturnTargetingKeywordsForOrdinaryBid() {
         // given
-        final Bid bid = Bid.builder().bidder("bidder1").price(BigDecimal.ONE).dealId("dealId1").cacheId("cacheId1")
+        final Bid bid = Bid.builder().bidder("bidder1").price(BigDecimal.ONE).dealId("dealId1")
+                .mediaType(MediaType.banner).cacheId("cacheId1")
                 .width(50).height(100).build();
 
         // when
@@ -40,6 +42,7 @@ public void shouldReturnTargetingKeywordsForOrdinaryBid() {
                 true,
                 true,
                 false,
+                false,
                 0,
                 null,
                 null,
@@ -69,11 +72,12 @@ public void shouldReturnTargetingKeywordsForOrdinaryBidOpenrtb() {
                 true,
                 true,
                 false,
+                false,
                 0,
                 null,
                 null,
                 null)
-                .makeFor(bid, "bidder1", false, null, null);
+                .makeFor(bid, "bidder1", false, null, null, null);
 
         // then
         assertThat(keywords).containsOnly(
@@ -87,7 +91,7 @@ public void shouldReturnTargetingKeywordsForOrdinaryBidOpenrtb() {
     public void shouldReturnTargetingKeywordsWithEntireKeys() {
         // given
         final Bid bid = Bid.builder().bidder("veryververyverylongbidder1").price(BigDecimal.ONE).dealId("dealId1")
-                .cacheId("cacheId1").width(50).height(100).build();
+                .cacheId("cacheId1").mediaType(MediaType.banner).width(50).height(100).build();
 
         // when
         final Map keywords = TargetingKeywordsCreator.create(
@@ -97,6 +101,7 @@ public void shouldReturnTargetingKeywordsWithEntireKeys() {
                 true,
                 true,
                 false,
+                false,
                 0,
                 null,
                 null,
@@ -126,11 +131,12 @@ public void shouldReturnTargetingKeywordsWithEntireKeysOpenrtb() {
                 true,
                 true,
                 false,
+                false,
                 0,
                 null,
                 null,
                 null)
-                .makeFor(bid, "veryververyverylongbidder1", false, null, null);
+                .makeFor(bid, "veryververyverylongbidder1", false, null, null, null);
 
         // then
         assertThat(keywords).containsOnly(
@@ -148,7 +154,7 @@ public void shouldReturnTargetingKeywordsForWinningBid() {
                 .price(BigDecimal.ONE)
                 .dealId("dealId1")
                 .cacheId("cacheId1")
-                .width(50)
+                .mediaType(MediaType.banner).width(50)
                 .height(100)
                 .build();
 
@@ -159,6 +165,7 @@ public void shouldReturnTargetingKeywordsForWinningBid() {
                         singletonList(ExtGranularityRange.of(BigDecimal.valueOf(5), BigDecimal.valueOf(0.5)))),
                 true,
                 true,
+                true,
                 false,
                 0,
                 null,
@@ -177,7 +184,9 @@ public void shouldReturnTargetingKeywordsForWinningBid() {
                 entry("hb_bidder", "bidder1"),
                 entry("hb_cache_id", "cacheId1"),
                 entry("hb_size", "50x100"),
-                entry("hb_deal", "dealId1"));
+                entry("hb_deal", "dealId1"),
+                entry("hb_format", "banner"),
+                entry("hb_format_bidder1", "banner"));
     }
 
     @Test
@@ -197,12 +206,13 @@ public void shouldReturnTargetingKeywordsForWinningBidOpenrtb() {
                         singletonList(ExtGranularityRange.of(BigDecimal.valueOf(5), BigDecimal.valueOf(0.5)))),
                 true,
                 true,
+                true,
                 false,
                 0,
                 null,
                 null,
                 null)
-                .makeFor(bid, "bidder1", true, "cacheId1", "videoCacheId1");
+                .makeFor(bid, "bidder1", true, "cacheId1", "banner", "videoCacheId1");
 
         // then
         assertThat(keywords).containsOnly(
@@ -217,30 +227,46 @@ public void shouldReturnTargetingKeywordsForWinningBidOpenrtb() {
                 entry("hb_cache_id", "cacheId1"),
                 entry("hb_cache_id_bidder1", "cacheId1"),
                 entry("hb_uuid", "videoCacheId1"),
-                entry("hb_uuid_bidder1", "videoCacheId1"));
+                entry("hb_uuid_bidder1", "videoCacheId1"),
+                entry("hb_format", "banner"),
+                entry("hb_format_bidder1", "banner"));
     }
 
     @Test
     public void shouldFallbackToDefaultPriceIfInvalidPriceGranularity() {
         // given
-        final Bid bid = Bid.builder().bidder("").price(BigDecimal.valueOf(3.87)).build();
+        final Bid bid = Bid.builder().bidder("").price(BigDecimal.valueOf(3.87)).mediaType(MediaType.banner).build();
 
         // when
         final Map keywords = TargetingKeywordsCreator.create(
-                "invalid", true, true, false, 0)
+                "invalid", true, true, false, false, 0)
                 .makeFor(bid, true);
 
         // then
         assertThat(keywords).contains(entry("hb_pb", StringUtils.EMPTY));
     }
 
+    @Test
+    public void shouldIncludeFormat() {
+        // given
+        final Bid bid = Bid.builder().bidder("").price(BigDecimal.valueOf(3.87)).mediaType(MediaType.banner).build();
+
+        // when
+        final Map keywords = TargetingKeywordsCreator.create(
+                "invalid", true, true, true, false, 0)
+                .makeFor(bid, true);
+
+        // then
+        assertThat(keywords).contains(entry("hb_format", "banner"));
+    }
+
     @Test
     public void shouldUseDefaultPriceGranularity() {
         // given
-        final Bid bid = Bid.builder().bidder("").price(BigDecimal.valueOf(3.87)).build();
+        final Bid bid = Bid.builder().bidder("").price(BigDecimal.valueOf(3.87)).mediaType(MediaType.banner).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, 0)
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, false, 0)
                 .makeFor(bid, true);
 
         // then
@@ -254,20 +280,34 @@ public void shouldUseDefaultPriceGranularityOpenrtb() {
                 .price(BigDecimal.valueOf(3.87)).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, 0)
-                .makeFor(bid, "", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, false, 0)
+                .makeFor(bid, "", true, null, null, null);
 
         // then
         assertThat(keywords).contains(entry("hb_pb", "3.80"));
     }
 
+    @Test
+    public void shouldIncludeFormatOpenrtb() {
+        // given
+        final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder()
+                .price(BigDecimal.valueOf(3.87)).build();
+
+        // when
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, true, false, 0)
+                .makeFor(bid, "", true, null, "banner", null);
+
+        // then
+        assertThat(keywords).contains(entry("hb_format", "banner"));
+    }
+
     @Test
     public void shouldNotIncludeCacheIdAndDealIdAndSize() {
         // given
-        final Bid bid = Bid.builder().bidder("bidder").price(BigDecimal.ONE).build();
+        final Bid bid = Bid.builder().bidder("bidder").mediaType(MediaType.banner).price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, 0)
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, false, 0)
                 .makeFor(bid, true);
 
         // then
@@ -281,8 +321,8 @@ public void shouldNotIncludeCacheIdAndDealIdAndSizeOpenrtb() {
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, 0)
-                .makeFor(bid, "bidder", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, false, 0)
+                .makeFor(bid, "bidder", true, null, null, null);
 
         // then
         assertThat(keywords).doesNotContainKeys("hb_cache_id_bidder", "hb_deal_bidder", "hb_size_bidder",
@@ -292,10 +332,10 @@ public void shouldNotIncludeCacheIdAndDealIdAndSizeOpenrtb() {
     @Test
     public void shouldReturnEnvKeyForAppRequest() {
         // given
-        final Bid bid = Bid.builder().bidder("bidder").price(BigDecimal.ONE).build();
+        final Bid bid = Bid.builder().bidder("bidder").mediaType(MediaType.banner).price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, true, 0)
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, true, 0)
                 .makeFor(bid, true);
 
         // then
@@ -310,8 +350,8 @@ public void shouldReturnEnvKeyForAppRequestOpenrtb() {
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, true, 0)
-                .makeFor(bid, "bidder", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, true, 0)
+                .makeFor(bid, "bidder", true, null, null, null);
 
         // then
         assertThat(keywords).contains(
@@ -325,8 +365,8 @@ public void shouldNotIncludeWinningBidTargetingIfIncludeWinnersFlagIsFalse() {
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, 0)
-                .makeFor(bid, "bidder1", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, false, 0)
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).doesNotContainKeys("hb_bidder", "hb_pb");
@@ -338,8 +378,8 @@ public void shouldIncludeWinningBidTargetingIfIncludeWinnersFlagIsTrue() {
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, 0)
-                .makeFor(bid, "bidder1", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, false, false, 0)
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).containsKeys("hb_bidder", "hb_pb");
@@ -351,8 +391,8 @@ public void shouldNotIncludeBidderKeysTargetingIfIncludeBidderKeysFlagIsFalse()
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, false, false, false, 0)
-                .makeFor(bid, "bidder1", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, false, false, false, false, 0)
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).doesNotContainKeys("hb_bidder_bidder1", "hb_pb_bidder1");
@@ -364,8 +404,8 @@ public void shouldIncludeBidderKeysTargetingIfIncludeBidderKeysFlagIsTrue() {
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, 0)
-                .makeFor(bid, "bidder1", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, false, 0)
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).containsKeys("hb_bidder_bidder1", "hb_pb_bidder1");
@@ -377,8 +417,8 @@ public void shouldTruncateTargetingBidderKeywordsIfTruncateAttrCharsIsDefined()
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, 20)
-                .makeFor(bid, "someVeryLongBidderName", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, false, 20)
+                .makeFor(bid, "someVeryLongBidderName", true, null, null, null);
 
         // then
         assertThat(keywords).hasSize(2)
@@ -391,22 +431,37 @@ public void shouldTruncateTargetingWithoutBidderSuffixKeywordsIfTruncateAttrChar
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, true, false, false, 7)
-                .makeFor(bid, "bidder", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, true, false, false, false, 7)
+                .makeFor(bid, "bidder", true, null, null, null);
 
         // then
         assertThat(keywords).hasSize(2)
                 .containsKeys("hb_bidd", "hb_pb");
     }
 
+    @Test
+    public void shouldTruncateTargetingAndDropDuplicatedWhenTruncateIsTooShort() {
+        // given
+        final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
+
+        // when
+        final Map keywords = TargetingKeywordsCreator.create(null, true, true, true, true, 6)
+                .makeFor(bid, "bidder", true, null, null, null);
+
+        // then
+        // Without truncating: "hb_bidder", "hb_bidder_bidder", "hb_env", "hb_env_bidder", "hb_pb", "hb_pb_bidder"
+        assertThat(keywords).hasSize(4)
+                .containsKeys("hb_bid", "hb_env", "hb_pb", "hb_pb_");
+    }
+
     @Test
     public void shouldNotTruncateTargetingKeywordsIfTruncateAttrCharsIsNotDefined() {
         // given
         final com.iab.openrtb.response.Bid bid = com.iab.openrtb.response.Bid.builder().price(BigDecimal.ONE).build();
 
         // when
-        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, 0)
-                .makeFor(bid, "someVeryLongBidderName", true, null, null);
+        final Map keywords = TargetingKeywordsCreator.create(null, false, true, false, false, 0)
+                .makeFor(bid, "someVeryLongBidderName", true, null, null, null);
 
         // then
         assertThat(keywords).hasSize(2)
@@ -432,11 +487,12 @@ public void shouldTruncateKeysFromResolver() {
                 true,
                 true,
                 false,
+                false,
                 20,
                 null,
                 null,
                 resolver)
-                .makeFor(bid, "bidder1", true, null, null);
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).contains(entry("key_longer_than_twen", "value1"));
@@ -461,11 +517,12 @@ public void shouldIncludeKeywordsFromResolver() {
                 true,
                 true,
                 false,
+                false,
                 0,
                 null,
                 null,
                 resolver)
-                .makeFor(bid, "bidder1", true, null, null);
+                .makeFor(bid, "bidder1", true, null, null, null);
 
         // then
         assertThat(keywords).contains(entry("keyword1", "value1"));
diff --git a/src/test/java/org/prebid/server/auction/VideoResponseFactoryTest.java b/src/test/java/org/prebid/server/auction/VideoResponseFactoryTest.java
index 6916a5b9e10..1b78d21ef6e 100644
--- a/src/test/java/org/prebid/server/auction/VideoResponseFactoryTest.java
+++ b/src/test/java/org/prebid/server/auction/VideoResponseFactoryTest.java
@@ -35,9 +35,9 @@ public void setUp() {
     public void shouldReturnExpectedVideoResponse() {
         // given
         final Map targeting = new HashMap<>();
-        targeting.put("hb_uuid", "value1");
-        targeting.put("hb_pb", "hb_pb");
-        targeting.put("hb_pb_cat_dur", "hb_pb_cat_dur");
+        targeting.put("hb_uuid_appnexus", "hb_uuidVal");
+        targeting.put("hb_pb_appnexus", "hb_pbVal");
+        targeting.put("hb_pb_cat_dur_appnexus", "hb_pb_cat_durVal");
 
         final Bid bid0 = Bid.builder()
                 .impid("0_0")
@@ -73,9 +73,9 @@ public void shouldReturnExpectedVideoResponse() {
 
         // then
         final ExtAdPod expectedExtAdPod0 = ExtAdPod.of(0,
-                singletonList(ExtResponseVideoTargeting.of("hb_pb", "hb_pb_cat_dur", "value1")), null);
+                singletonList(ExtResponseVideoTargeting.of("hb_pbVal", "hb_pb_cat_durVal", "hb_uuidVal")), null);
         final ExtAdPod expectedExtAdPod1 = ExtAdPod.of(
-                1, singletonList(ExtResponseVideoTargeting.of("hb_pb", "hb_pb_cat_dur", "value1")), null);
+                1, singletonList(ExtResponseVideoTargeting.of("hb_pbVal", "hb_pb_cat_durVal", "hb_uuidVal")), null);
         final ExtAdPod expectedErrorExtAdPod3 = ExtAdPod.of(3, null, singletonList("Error"));
         final List expectedAdPodResponse = Arrays.asList(expectedExtAdPod0, expectedExtAdPod1,
                 expectedErrorExtAdPod3);
diff --git a/src/test/java/org/prebid/server/auction/VideoStoredRequestProcessorTest.java b/src/test/java/org/prebid/server/auction/VideoStoredRequestProcessorTest.java
index 82eaa36795a..ca604d7b3cd 100644
--- a/src/test/java/org/prebid/server/auction/VideoStoredRequestProcessorTest.java
+++ b/src/test/java/org/prebid/server/auction/VideoStoredRequestProcessorTest.java
@@ -1,5 +1,6 @@
 package org.prebid.server.auction;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.Content;
 import com.iab.openrtb.request.Imp;
@@ -12,6 +13,8 @@
 import com.iab.openrtb.request.video.PodError;
 import com.iab.openrtb.request.video.Podconfig;
 import io.vertx.core.Future;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.file.FileSystem;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -22,6 +25,7 @@
 import org.prebid.server.auction.model.WithPodErrors;
 import org.prebid.server.exception.InvalidRequestException;
 import org.prebid.server.execution.TimeoutFactory;
+import org.prebid.server.json.JsonMerger;
 import org.prebid.server.metric.Metrics;
 import org.prebid.server.proto.openrtb.ext.ExtIncludeBrandCategory;
 import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
@@ -46,6 +50,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.never;
@@ -58,6 +63,8 @@ public class VideoStoredRequestProcessorTest extends VertxTest {
     @Rule
     public final MockitoRule mockitoRule = MockitoJUnit.rule();
 
+    @Mock
+    private FileSystem fileSystem;
     @Mock
     private ApplicationSettings applicationSettings;
     @Mock
@@ -72,9 +79,24 @@ public class VideoStoredRequestProcessorTest extends VertxTest {
     private VideoStoredRequestProcessor target;
 
     @Before
-    public void setUp() {
-        target = new VideoStoredRequestProcessor(applicationSettings, validator, false, emptyList(),
-                BidRequest.builder().build(), metrics, timeoutFactory, timeoutResolver, 2000L, "USD", jacksonMapper);
+    public void setUp() throws JsonProcessingException {
+        given(fileSystem.readFileBlocking(anyString()))
+                .willReturn(Buffer.buffer(mapper.writeValueAsString(BidRequest.builder().at(1).build())));
+
+        target = VideoStoredRequestProcessor.create(
+                false,
+                emptyList(),
+                2000L,
+                "USD",
+                "path/to/default/request.json",
+                fileSystem,
+                applicationSettings,
+                validator,
+                metrics,
+                timeoutFactory,
+                timeoutResolver,
+                jacksonMapper,
+                new JsonMerger(jacksonMapper));
     }
 
     @Test
@@ -161,6 +183,7 @@ public void shouldReturnFutureWithMergedStoredAndDefaultRequest() {
                 .build();
         final BidRequest expectedMergedRequest = BidRequest.builder()
                 .id("bid_id")
+                .at(1)
                 .imp(Arrays.asList(expectedImp1, expectedImp2))
                 .user(User.builder().buyeruid("appnexus").yob(123).gender("gender").keywords("keywords").build())
                 .site(Site.builder().id("siteId").content(content).build())
diff --git a/src/test/java/org/prebid/server/auction/model/GeneratedBidIdsTest.java b/src/test/java/org/prebid/server/auction/model/GeneratedBidIdsTest.java
new file mode 100644
index 00000000000..f704d68d962
--- /dev/null
+++ b/src/test/java/org/prebid/server/auction/model/GeneratedBidIdsTest.java
@@ -0,0 +1,157 @@
+package org.prebid.server.auction.model;
+
+import com.iab.openrtb.response.Bid;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.bidder.model.BidderSeatBid;
+import org.prebid.server.identity.IdGenerator;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+
+public class GeneratedBidIdsTest {
+
+    @Rule
+    public final MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private IdGenerator idGenerator;
+
+    private BidderResponse bidderResponse1;
+
+    private BidderResponse bidderResponse2;
+
+    @Before
+    public void setup() {
+        bidderResponse1 = givenBidderResponse("bidder1", asList(
+                givenBidderBid("bidId1", "impId1"), givenBidderBid("bidId1", "impId2")));
+        bidderResponse2 = givenBidderResponse("bidder2", asList(givenBidderBid("bidId1", "impId1"),
+                givenBidderBid("bidId2", "impId2")));
+
+        given(idGenerator.generateId())
+                .willReturn("20dc17a4-4030-4c9f-a2b2-0a95a14af7e9", "a09fb35b-9406-4d1e-9ec3-10c5d33dd249",
+                        "b90a06ad-cfd9-4e6e-bc44-4cf723ef5677", "8269d3fc-3304-4ecd-8221-4e0304bf11c2");
+    }
+
+    @Test
+    public void shouldCreateGeneratedBidIdsTestFromBidderResponses() {
+        // when
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // then
+        final Map> expectedResult =
+                doubleMap("bidder1",
+                        doubleMap("bidId1-impId1", "20dc17a4-4030-4c9f-a2b2-0a95a14af7e9",
+                                "bidId1-impId2", "a09fb35b-9406-4d1e-9ec3-10c5d33dd249"),
+                        "bidder2",
+                        doubleMap("bidId1-impId1", "b90a06ad-cfd9-4e6e-bc44-4cf723ef5677",
+                                "bidId2-impId2", "8269d3fc-3304-4ecd-8221-4e0304bf11c2"));
+        assertThat(generatedBidIds.getBidderToBidIds())
+                .containsAllEntriesOf(expectedResult);
+    }
+
+    @Test
+    public void getBidderForBidShouldReturnOptionalWithBidderIfExists() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // when
+        final Optional result = generatedBidIds.getBidderForBid("bidId1", "impId2");
+        // then
+        assertThat(result).isPresent();
+        assertThat(result.get()).isEqualTo("bidder1");
+    }
+
+    @Test
+    public void getBidderForBidShouldReturnEmptyOptionalIfBidderNotExists() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // when
+        final Optional result = generatedBidIds.getBidderForBid("bidId1", "impId3");
+
+        // then
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void getGeneratedIdShouldReturnGeneratedId() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // when
+        final String result = generatedBidIds.getGeneratedId("bidder1", "bidId1", "impId1");
+
+        // then
+        assertThat(result).isEqualTo("20dc17a4-4030-4c9f-a2b2-0a95a14af7e9");
+    }
+
+    @Test
+    public void getGeneratedIdShouldReturnNullIfBidWithBidIdAndImpIdPairWasNotFound() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // when
+        final String result = generatedBidIds.getGeneratedId("bidder1", "bidId1", "impId3");
+
+        // then
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void getGeneratedIdShouldReturnNullIfBidderNotFound() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.of(asList(bidderResponse1, bidderResponse2),
+                (ignored1, ignored2) -> idGenerator.generateId());
+
+        // when
+        final String result = generatedBidIds.getGeneratedId("bidder3", "bidId1", "impId1");
+
+        // then
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void getGeneratedIdShouldReturnNullIfGeneratedIdCreatedFromEmptyMap() {
+        // given
+        final GeneratedBidIds generatedBidIds = GeneratedBidIds.empty();
+
+        // when
+        final String result = generatedBidIds.getGeneratedId("bidder3", "bidId1", "impId1");
+
+        // then
+        assertThat(result).isNull();
+    }
+
+    private static BidderBid givenBidderBid(String bidId, String impId) {
+        return BidderBid.of(Bid.builder().id(bidId).impid(impId).build(), null, null);
+    }
+
+    private static BidderResponse givenBidderResponse(String bidder, List bidderBids) {
+        return BidderResponse.of(bidder, BidderSeatBid.of(bidderBids, null, null), 0);
+    }
+
+    private  Map doubleMap(K key1, V value1, K key2, V value2) {
+        final Map result = new HashMap<>();
+        result.put(key1, value1);
+        result.put(key2, value2);
+        return result;
+    }
+
+}
diff --git a/src/test/java/org/prebid/server/bidder/adform/AdformAdapterTest.java b/src/test/java/org/prebid/server/bidder/adform/AdformAdapterTest.java
index 79b359c98ba..2de96f35a32 100644
--- a/src/test/java/org/prebid/server/bidder/adform/AdformAdapterTest.java
+++ b/src/test/java/org/prebid/server/bidder/adform/AdformAdapterTest.java
@@ -23,7 +23,6 @@
 import org.prebid.server.exception.PreBidException;
 import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.request.PreBidRequest;
 import org.prebid.server.proto.response.Bid;
 import org.prebid.server.proto.response.MediaType;
@@ -76,7 +75,6 @@ public void makeHttpRequestsShouldReturnAdapterHttpRequestWithCorrectUrlAndHeade
                         .user(User.builder()
                                 .ext(ExtUser.builder()
                                         .consent("consent")
-                                        .digitrust(ExtUserDigiTrust.of("id", 123, 1))
                                         .build())
                                 .build())
                         .build())
@@ -114,9 +112,7 @@ public void makeHttpRequestsShouldReturnAdapterHttpRequestWithCorrectUrlAndHeade
                         tuple(HttpUtil.X_REQUEST_AGENT_HEADER.toString(), "PrebidAdapter 0.1.3"),
                         tuple(HttpUtil.REFERER_HEADER.toString(), "www.example.com"),
                         // Base64 encoded {"id":"id","version":1,"keyv":123,"privacy":{"optout":true}}
-                        tuple(HttpUtil.COOKIE_HEADER.toString(),
-                                "uid=buyeruid;DigiTrust.v1.identity=eyJpZCI6ImlkIiwidmVyc2lvbiI6MSwia2V5diI6MTIzLCJw"
-                                        + "cml2YWN5Ijp7Im9wdG91dCI6dHJ1ZX19"));
+                        tuple(HttpUtil.COOKIE_HEADER.toString(), "uid=buyeruid"));
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/bidder/adform/AdformBidderTest.java b/src/test/java/org/prebid/server/bidder/adform/AdformBidderTest.java
index fb5852b825e..ed8909ec01f 100644
--- a/src/test/java/org/prebid/server/bidder/adform/AdformBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/adform/AdformBidderTest.java
@@ -25,14 +25,12 @@
 import org.prebid.server.proto.openrtb.ext.ExtPrebid;
 import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEid;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUid;
 import org.prebid.server.proto.openrtb.ext.request.adform.ExtImpAdform;
 import org.prebid.server.proto.openrtb.ext.response.BidType;
 import org.prebid.server.util.HttpUtil;
 
-import java.math.BigDecimal;
 import java.util.Base64;
 import java.util.List;
 import java.util.Map;
@@ -69,7 +67,6 @@ public void makeHttpRequestsShouldReturnHttpRequestWithoutErrors() {
                         .buyeruid("buyeruid")
                         .ext(ExtUser.builder()
                                 .consent("consent")
-                                .digitrust(ExtUserDigiTrust.of("id", 123, 1))
                                 .eids(singletonList(ExtUserEid.of("test.com", "some_user_id",
                                         singletonList(ExtUserEidUid.of("uId", 1, null)), null)))
                                 .build())
@@ -104,9 +101,7 @@ public void makeHttpRequestsShouldReturnHttpRequestWithoutErrors() {
                         tuple(HttpUtil.X_REQUEST_AGENT_HEADER.toString(), "PrebidAdapter 0.1.3"),
                         tuple(HttpUtil.REFERER_HEADER.toString(), "www.example.com"),
                         // Base64 encoded {"id":"id","version":1,"keyv":123,"privacy":{"optout":true}}
-                        tuple(HttpUtil.COOKIE_HEADER.toString(),
-                                "uid=buyeruid;DigiTrust.v1.identity=eyJpZCI6ImlkIiwidmVyc2lvbiI6MSwia2V5diI6MTIzLCJwcml"
-                                        + "2YWN5Ijp7Im9wdG91dCI6dHJ1ZX19"));
+                        tuple(HttpUtil.COOKIE_HEADER.toString(), "uid=buyeruid"));
     }
 
     @Test
@@ -185,7 +180,6 @@ public void makeHttpRequestsShouldReturnHttpsUrlIfAtLeastOneImpIsSecured() {
                         .buyeruid("buyeruid")
                         .ext(ExtUser.builder()
                                 .consent("consent")
-                                .digitrust(ExtUserDigiTrust.of("id", 123, 1))
                                 .eids(singletonList(ExtUserEid.of("test.com", "some_user_id",
                                         singletonList(ExtUserEidUid.of("uId", 1, null)), null)))
                                 .build())
@@ -248,11 +242,10 @@ public void makeBidsShouldReturnEmptyListIfResponseTypeIsNotBanner() throws Json
     }
 
     @Test
-    public void makeBidsShouldReturnBidderBid() throws JsonProcessingException {
+    public void makeBidsShouldReturnBidderBidIfResponseIsBannerAndBannerIsPresent() throws JsonProcessingException {
         // given
         final String adformResponse = mapper.writeValueAsString(AdformBid.builder().banner("admBanner")
-                .response("banner").winCur("currency").dealId("dealId").height(300).width(400).winCrid("gross")
-                .winBid(BigDecimal.ONE).build());
+                .response("banner").winCur("currency").build());
 
         final HttpCall httpCall = givenHttpCall(adformResponse);
         final BidRequest bidRequest = BidRequest.builder().imp(singletonList(Imp.builder().id("id").build())).build();
@@ -263,8 +256,41 @@ public void makeBidsShouldReturnBidderBid() throws JsonProcessingException {
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(1).containsOnly(BidderBid.of(
-                Bid.builder().id("id").impid("id").price(BigDecimal.ONE).adm("admBanner").w(400).h(300).dealid("dealId")
-                        .crid("gross").build(), BidType.banner, "currency"));
+                Bid.builder().id("id").impid("id").adm("admBanner").build(), BidType.banner, "currency"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnVideoBidIfResponseEqualsVastContent() throws JsonProcessingException {
+        // given
+        final String adformResponse = mapper.writeValueAsString(
+                AdformBid.builder().winCur("currency").vastContent("admVastContent").response("vast_content").build());
+
+        final HttpCall httpCall = givenHttpCall(adformResponse);
+        final BidRequest bidRequest = BidRequest.builder().imp(singletonList(Imp.builder().id("id").build())).build();
+
+        // when
+        final Result> result = adformBidder.makeBids(httpCall, bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1).containsOnly(BidderBid.of(Bid.builder().id("id").impid("id")
+                .adm("admVastContent").build(), BidType.video, "currency"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyResultIfVastContentOrBannerIsNotPresent() throws JsonProcessingException {
+        // given
+        final String adformResponse = mapper.writeValueAsString(AdformBid.builder().build());
+
+        final HttpCall httpCall = givenHttpCall(adformResponse);
+        final BidRequest bidRequest = BidRequest.builder().imp(singletonList(Imp.builder().id("id").build())).build();
+
+        // when
+        final Result> result = adformBidder.makeBids(httpCall, bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).isEmpty();
     }
 
     @Test
@@ -302,7 +328,6 @@ public void makeHttpRequestsShouldPassMultipleUserIds() {
                         .buyeruid("buyeruid")
                         .ext(ExtUser.builder()
                                 .consent("consent")
-                                .digitrust(ExtUserDigiTrust.of("id", 123, 1))
                                 .eids(asList(ExtUserEid.of("test.com", "some_user_id",
                                         singletonList(ExtUserEidUid.of("uId", 1, null)), null),
                                         ExtUserEid.of("test.com", "some_user_id",
@@ -319,8 +344,9 @@ public void makeHttpRequestsShouldPassMultipleUserIds() {
         // then
         assertThat(result.getValue()).hasSize(1)
                 .extracting(HttpRequest::getUri)
-                .containsExactly("https://adform.com/openrtb2d?CC=1&eids=eyJ0ZXN0LmNvbSI6eyJ1SWQiOlsxLDJdfSwidGVzdC5uZXQiOnsiaWRfc29tZV91c2VyIjpbM119fQ&fd=1&gdpr="
-                        + "&gdpr_consent=consent&ip=&rp=4&stid=tid&bWlkPTE1JnJjdXI9VVNE");
+                .containsExactly(
+                        "https://adform.com/openrtb2d?CC=1&eids=eyJ0ZXN0LmNvbSI6eyJ1SWQiOlsxLDJdfSwidGVzdC5uZXQiOnsiaWRfc29tZV91c2VyIjpbM119fQ&fd=1&gdpr="
+                                + "&gdpr_consent=consent&ip=&rp=4&stid=tid&bWlkPTE1JnJjdXI9VVNE");
     }
 
     private static HttpCall givenHttpCall(String body) {
diff --git a/src/test/java/org/prebid/server/bidder/adform/AdformHttpUtilTest.java b/src/test/java/org/prebid/server/bidder/adform/AdformHttpUtilTest.java
index bda3754eec2..3b5c320b764 100644
--- a/src/test/java/org/prebid/server/bidder/adform/AdformHttpUtilTest.java
+++ b/src/test/java/org/prebid/server/bidder/adform/AdformHttpUtilTest.java
@@ -5,8 +5,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.prebid.server.VertxTest;
-import org.prebid.server.bidder.adform.model.AdformDigitrust;
-import org.prebid.server.bidder.adform.model.AdformDigitrustPrivacy;
 import org.prebid.server.bidder.adform.model.UrlParameters;
 import org.prebid.server.util.HttpUtil;
 
@@ -27,7 +25,7 @@ public class AdformHttpUtilTest extends VertxTest {
 
     @Before
     public void setUp() {
-        httpUtil = new AdformHttpUtil(jacksonMapper);
+        httpUtil = new AdformHttpUtil();
     }
 
     @Test
@@ -38,8 +36,7 @@ public void buildAdformHeadersShouldReturnAllHeaders() {
                 "userAgent",
                 "ip",
                 "www.example.com",
-                "buyeruid",
-                AdformDigitrust.of("id", 1, 123, AdformDigitrustPrivacy.of(true)));
+                "buyeruid");
 
         // then
         assertThat(headers).hasSize(7)
@@ -50,10 +47,7 @@ public void buildAdformHeadersShouldReturnAllHeaders() {
                         tuple(HttpUtil.X_FORWARDED_FOR_HEADER.toString(), "ip"),
                         tuple(HttpUtil.X_REQUEST_AGENT_HEADER.toString(), "PrebidAdapter 0.1.0"),
                         tuple(HttpUtil.REFERER_HEADER.toString(), "www.example.com"),
-                        tuple(HttpUtil.COOKIE_HEADER.toString(),
-                                // Base64 encoded {"id":"id","version":1,"keyv":123,"privacy":{"optout":true}}
-                                "uid=buyeruid;DigiTrust.v1.identity=eyJpZCI6ImlkIiwidmVyc2lvbiI6MSwia2V5diI6MTIzLC"
-                                        + "Jwcml2YWN5Ijp7Im9wdG91dCI6dHJ1ZX19"));
+                        tuple(HttpUtil.COOKIE_HEADER.toString(), "uid=buyeruid"));
     }
 
     @Test
@@ -64,63 +58,41 @@ public void buildAdformHeadersShouldNotContainRefererHeaderIfRefererIsEmpty() {
                 "userAgent",
                 "ip",
                 "",
-                "buyeruid",
-                AdformDigitrust.of("id", 1, 123, AdformDigitrustPrivacy.of(true)));
+                "buyeruid");
 
         // then
         assertThat(headers).extracting(Map.Entry::getKey).doesNotContain(HttpUtil.REFERER_HEADER.toString());
     }
 
     @Test
-    public void buildAdformHeadersShouldNotContainCookieHeaderIfUserIdAndDigiTrustAreEmpty() {
+    public void buildAdformHeadersShouldNotContainCookieHeaderIfUserIdIsEmpty() {
         // when
         final MultiMap headers = httpUtil.buildAdformHeaders(
                 "0.1.0",
                 "userAgent",
                 "ip",
                 "referer",
-                "",
-                null);
+                "");
 
         // then
         assertThat(headers).extracting(Map.Entry::getKey).doesNotContain(HttpUtil.COOKIE_HEADER.toString());
     }
 
     @Test
-    public void buildAdformHeaderShouldContainCookieHeaderOnlyWithUserIdIfUserIdPresentAndDigitrustAbsent() {
+    public void buildAdformHeaderShouldContainCookieHeaderOnlyWithUserIdIfUserIdPresent() {
         // when
         final MultiMap headers = httpUtil.buildAdformHeaders(
                 "0.1.0",
                 "userAgent",
                 "ip",
                 "referer",
-                "buyeruid",
-                null);
+                "buyeruid");
 
         // then
         assertThat(headers).extracting(Map.Entry::getKey, Map.Entry::getValue)
                 .contains(tuple(HttpUtil.COOKIE_HEADER.toString(), "uid=buyeruid"));
     }
 
-    @Test
-    public void buildAdformHeaderShouldContainCookieHeaderOnlyWithDigitrustIfUserIsAbsentAndDigitrustPresent() {
-        // when
-        final MultiMap headers = httpUtil.buildAdformHeaders(
-                "0.1.0",
-                "userAgent",
-                "ip",
-                "referer",
-                "",
-                AdformDigitrust.of("id", 1, 123, AdformDigitrustPrivacy.of(true)));
-
-        // then
-        assertThat(headers).extracting(Map.Entry::getKey, Map.Entry::getValue)
-                .contains(tuple(HttpUtil.COOKIE_HEADER.toString(),
-                        // Base64 encoded {"id":"id","version":1,"keyv":123,"privacy":{"optout":true}}
-                        "DigiTrust.v1.identity=eyJpZCI6ImlkIiwidmVyc2lvbiI6MSwia2V5diI6MTIzLCJwcml2YWN5Ijp7Im9wdG91dC"
-                                + "I6dHJ1ZX19"));
-    }
-
     @Test
     public void buildAdformUrlShouldReturnCorrectUrl() {
         // when
diff --git a/src/test/java/org/prebid/server/bidder/adform/AdformRequestUtilTest.java b/src/test/java/org/prebid/server/bidder/adform/AdformRequestUtilTest.java
index 4cf50a36201..46afb44585d 100644
--- a/src/test/java/org/prebid/server/bidder/adform/AdformRequestUtilTest.java
+++ b/src/test/java/org/prebid/server/bidder/adform/AdformRequestUtilTest.java
@@ -4,11 +4,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.prebid.server.VertxTest;
-import org.prebid.server.bidder.adform.model.AdformDigitrust;
-import org.prebid.server.bidder.adform.model.AdformDigitrustPrivacy;
 import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -92,44 +89,4 @@ public void getConsentShouldReturnConsent() {
         // then
         assertThat(consent).isEqualTo("consent");
     }
-
-    @Test
-    public void getAdformDigiTrustShouldReturnNullIfUserExtIsNull() {
-        // given and when
-        final AdformDigitrust adformDigitrust = requestUtil.getAdformDigitrust(null);
-
-        // then
-        assertThat(adformDigitrust).isNull();
-    }
-
-    @Test
-    public void getAdformDigiTrustShouldReturnNullIfUserExtDigitrustIsNull() {
-        // given and when
-        final AdformDigitrust adformDigitrust = requestUtil.getAdformDigitrust(ExtUser.builder().build());
-
-        // then
-        assertThat(adformDigitrust).isNull();
-    }
-
-    @Test
-    public void getAdformDigiTrustShouldReturnAdformDigitrustWithOptOutFalseIfPrefIsZero() {
-        // given and when
-        final AdformDigitrust adformDigitrust = requestUtil.getAdformDigitrust(ExtUser.builder()
-                .digitrust(ExtUserDigiTrust.of("id", 123, 0))
-                .build());
-
-        // then
-        assertThat(adformDigitrust).isEqualTo(AdformDigitrust.of("id", 1, 123, AdformDigitrustPrivacy.of(false)));
-    }
-
-    @Test
-    public void getAdformDigiTrustShouldReturnAdformDigitrustWithOptOutTrueIfPrefIsNotZero() {
-        // given and when
-        final AdformDigitrust adformDigitrust = requestUtil.getAdformDigitrust(ExtUser.builder()
-                .digitrust(ExtUserDigiTrust.of("id", 123, 1))
-                .build());
-
-        // then
-        assertThat(adformDigitrust).isEqualTo(AdformDigitrust.of("id", 1, 123, AdformDigitrustPrivacy.of(true)));
-    }
 }
diff --git a/src/test/java/org/prebid/server/bidder/adhese/AdheseBidderTest.java b/src/test/java/org/prebid/server/bidder/adhese/AdheseBidderTest.java
index 67b32cada5c..7cb67c1739b 100644
--- a/src/test/java/org/prebid/server/bidder/adhese/AdheseBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/adhese/AdheseBidderTest.java
@@ -17,6 +17,7 @@
 import org.prebid.server.VertxTest;
 import org.prebid.server.bidder.adhese.model.AdheseBid;
 import org.prebid.server.bidder.adhese.model.AdheseOriginData;
+import org.prebid.server.bidder.adhese.model.AdheseRequestBody;
 import org.prebid.server.bidder.adhese.model.AdheseResponseExt;
 import org.prebid.server.bidder.adhese.model.Cpm;
 import org.prebid.server.bidder.adhese.model.CpmValues;
@@ -33,9 +34,12 @@
 import org.prebid.server.proto.openrtb.ext.response.BidType;
 
 import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
@@ -94,7 +98,8 @@ public void makeHttpRequestsShouldReturnErrorWhenImpExtCouldNotBeParsed() {
     }
 
     @Test
-    public void makeHttpRequestsShouldModifyIncomingRequestAndSetExpectedHttpRequestUri() {
+    public void makeHttpRequestsShouldModifyIncomingRequestAndSetExpectedHttpRequestUri()
+            throws JsonProcessingException {
         // given
         Map> targets = new HashMap<>();
         targets.put("ci", asList("gent", "brussels"));
@@ -105,8 +110,11 @@ public void makeHttpRequestsShouldModifyIncomingRequestAndSetExpectedHttpRequest
                         .ext(ExtUser.builder().consent("dummy").build())
                         .build())
                 .imp(singletonList(Imp.builder()
-                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdhese.of("demo",
-                                "_adhese_prebid_demo_", "leaderboard", mapper.convertValue(targets, JsonNode.class)))))
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdhese.of(
+                                "demo",
+                                "_adhese_prebid_demo_",
+                                "leaderboard",
+                                mapper.convertValue(targets, JsonNode.class)))))
                         .build()))
                 .device(Device.builder().ifa("dum-my").build())
                 .build();
@@ -117,12 +125,27 @@ public void makeHttpRequestsShouldModifyIncomingRequestAndSetExpectedHttpRequest
         // then
         assertThat(result.getValue())
                 .extracting(HttpRequest::getUri)
-                .containsOnly("https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels"
-                        + "/tlall/xtdummy/xzdum-my");
+                .containsOnly("https://ads-demo.adhese.com/json");
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getBody)
+                .containsOnly(jacksonMapper.mapper().writeValueAsString(
+                        AdheseRequestBody
+                        .builder()
+                        .slots(Collections.singletonList(
+                                AdheseRequestBody.Slot.builder()
+                                        .slotname("_adhese_prebid_demo_-leaderboard")
+                                        .build()))
+                        .parameters(new TreeMap>() {{
+                                put("ag", Arrays.asList("55"));
+                                put("ci", Arrays.asList("gent", "brussels"));
+                                put("tl", Arrays.asList("all"));
+                                put("xt", Arrays.asList("dummy"));
+                                put("xz", Arrays.asList("dum-my")); }})
+                        .build()));
     }
 
     @Test
-    public void makeHttpRequestsShouldModifyIncomingRequestWithIfaParameter() {
+    public void makeHttpRequestsShouldModifyIncomingRequestWithIfaParameter() throws JsonProcessingException {
         // given
         final BidRequest bidRequest = BidRequest.builder()
                 .device(Device.builder().ifa("ifaValue").build())
@@ -139,11 +162,23 @@ public void makeHttpRequestsShouldModifyIncomingRequestWithIfaParameter() {
         // then
         assertThat(result.getValue())
                 .extracting(HttpRequest::getUri)
-                .containsOnly("https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/xzifaValue");
+                .containsOnly("https://ads-demo.adhese.com/json");
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getBody)
+                .containsOnly(jacksonMapper.mapper().writeValueAsString(
+                        AdheseRequestBody
+                                .builder()
+                                .slots(Collections.singletonList(
+                                        AdheseRequestBody.Slot.builder()
+                                                .slotname("_adhese_prebid_demo_-leaderboard")
+                                                .build()))
+                                .parameters(new TreeMap<>(Collections.singletonMap(
+                                        "xz", Arrays.asList("ifaValue"))))
+                                .build()));
     }
 
     @Test
-    public void makeHttpRequestsShouldModifyIncomingRequestWithRefererParameter() {
+    public void makeHttpRequestsShouldModifyIncomingRequestWithRefererParameter() throws JsonProcessingException {
         // given
         final BidRequest bidRequest = BidRequest.builder()
                 .site(Site.builder().page("pageValue").build())
@@ -160,19 +195,34 @@ public void makeHttpRequestsShouldModifyIncomingRequestWithRefererParameter() {
         // then
         assertThat(result.getValue())
                 .extracting(HttpRequest::getUri)
-                .containsOnly("https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/xfpageValue");
+                .containsOnly("https://ads-demo.adhese.com/json");
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getBody)
+                .containsOnly(jacksonMapper.mapper().writeValueAsString(
+                        AdheseRequestBody
+                                .builder()
+                                .slots(Collections.singletonList(
+                                        AdheseRequestBody.Slot.builder()
+                                                .slotname("_adhese_prebid_demo_-leaderboard")
+                                                .build()))
+                                .parameters(new TreeMap<>(Collections.singletonMap(
+                                        "xf", Arrays.asList("pageValue"))))
+                                .build()));
     }
 
     @Test
-    public void makeHttpRequestsShouldNotModifyIncomingRequestIfTargetsNotPresent() {
+    public void makeHttpRequestsShouldNotModifyIncomingRequestIfTargetsNotPresent() throws JsonProcessingException {
         // given
         final BidRequest bidRequest = BidRequest.builder()
                 .user(User.builder()
                         .ext(ExtUser.builder().consent("dummy").build())
                         .build())
                 .imp(singletonList(Imp.builder()
-                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdhese.of("demo",
-                                "_adhese_prebid_demo_", "leaderboard", mapper.convertValue(null, JsonNode.class)))))
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdhese.of(
+                                "demo",
+                                "_adhese_prebid_demo_",
+                                "leaderboard",
+                                mapper.convertValue(null, JsonNode.class)))))
                         .build()))
                 .build();
 
@@ -182,7 +232,19 @@ public void makeHttpRequestsShouldNotModifyIncomingRequestIfTargetsNotPresent()
         // then
         assertThat(result.getValue())
                 .extracting(HttpRequest::getUri)
-                .containsOnly("https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/xtdummy");
+                .containsOnly("https://ads-demo.adhese.com/json");
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getBody)
+                .containsOnly(jacksonMapper.mapper().writeValueAsString(
+                        AdheseRequestBody
+                                .builder()
+                                .slots(Collections.singletonList(
+                                        AdheseRequestBody.Slot.builder()
+                                                .slotname("_adhese_prebid_demo_-leaderboard")
+                                                .build()))
+                                .parameters(new TreeMap<>(Collections.singletonMap(
+                                        "xt", Arrays.asList("dummy"))))
+                                .build()));
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidderTest.java b/src/test/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidderTest.java
index 0f9cc726a66..c3591e0a899 100644
--- a/src/test/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/adkerneladn/AdkernelAdnBidderTest.java
@@ -44,7 +44,7 @@
 
 public class AdkernelAdnBidderTest extends VertxTest {
 
-    private static final String ENDPOINT_URL = "http://test.domain.com/rtbpub?account=";
+    private static final String ENDPOINT_URL = "http://{{Host}}/test?account={{PublisherID}}";
 
     private AdkernelAdnBidder adkernelAdnBidder;
 
@@ -149,7 +149,7 @@ public void makeHttpRequestsShouldFillMethodAndUrlAndExpectedHeaders() {
         // then
         assertThat(result.getValue()).hasSize(1).element(0).isNotNull()
                 .returns(HttpMethod.POST, HttpRequest::getMethod)
-                .returns("http://test.domain.com/rtbpub?account=50357", HttpRequest::getUri);
+                .returns("http://tag.adkernel.com/test?account=50357", HttpRequest::getUri);
         assertThat(result.getValue().get(0).getHeaders()).isNotNull()
                 .extracting(Map.Entry::getKey, Map.Entry::getValue)
                 .containsOnly(
@@ -172,27 +172,7 @@ public void makeHttpRequestShouldChangeDomainIfHostIsSpecified() {
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(1)
                 .extracting(HttpRequest::getUri)
-                .containsOnly("http://different.domanin.com/rtbpub?account=50357");
-    }
-
-    @Test
-    public void makeHttpRequestShouldRemovePortIfHostIsSpecified() {
-        // given
-        final String urlWithPort = "http://test:8080/rtbpub?account=";
-        adkernelAdnBidder = new AdkernelAdnBidder(urlWithPort, jacksonMapper);
-
-        final BidRequest bidRequest = givenBidRequest(
-                identity(),
-                extImpAdkernelAdnBuilder -> extImpAdkernelAdnBuilder.host("different.domanin.com"));
-
-        // when
-        final Result>> result = adkernelAdnBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(HttpRequest::getUri)
-                .containsOnly("http://different.domanin.com/rtbpub?account=50357");
+                .containsOnly("http://different.domanin.com/test?account=50357");
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/bidder/adman/AdmanBidderTest.java b/src/test/java/org/prebid/server/bidder/adman/AdmanBidderTest.java
index fe78fb7ab5d..d32963a99d9 100644
--- a/src/test/java/org/prebid/server/bidder/adman/AdmanBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/adman/AdmanBidderTest.java
@@ -1,9 +1,11 @@
 package org.prebid.server.bidder.adman;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.iab.openrtb.request.Audio;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.Imp;
+import com.iab.openrtb.request.Native;
 import com.iab.openrtb.request.Video;
 import com.iab.openrtb.response.Bid;
 import com.iab.openrtb.response.BidResponse;
@@ -27,7 +29,6 @@
 import static java.util.Collections.singletonList;
 import static java.util.function.Function.identity;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
 
@@ -42,29 +43,6 @@ public void setUp() {
         admanBidder = new AdmanBidder(ENDPOINT_URL, jacksonMapper);
     }
 
-    @Test
-    public void creationShouldFailOnInvalidEndpointUrl() {
-        assertThatIllegalArgumentException().isThrownBy(() -> new AdmanBidder("invalid_url", jacksonMapper));
-    }
-
-    @Test
-    public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
-        // given
-        final BidRequest bidRequest = BidRequest.builder()
-                .imp(singletonList(Imp.builder()
-                        .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))
-                        .build()))
-                .build();
-
-        // when
-        final Result>> result = admanBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance");
-        assertThat(result.getValue()).isEmpty();
-    }
-
     @Test
     public void makeHttpRequestsShouldReturnExpectedBidRequest() {
         // given
@@ -80,7 +58,7 @@ public void makeHttpRequestsShouldReturnExpectedBidRequest() {
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(1)
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
-                .containsOnly(expectedRequest);
+                .containsExactly(expectedRequest);
     }
 
     @Test
@@ -103,98 +81,115 @@ public void makeHttpRequestsShouldMakeOneRequestPerImp() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp).hasSize(2)
                 .extracting(Imp::getTagid)
-                .containsOnly("tagidString", "otherTagId");
+                .containsExactly("tagidString", "otherTagId");
     }
 
     @Test
-    public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
+    public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException {
         // given
-        final HttpCall httpCall = givenHttpCall(null, "invalid");
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder().imp(singletonList(Imp.builder().id("123").build())).build(),
+                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response);
-        assertThat(result.getValue()).isEmpty();
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
-    public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
+    public void makeBidsShouldReturnVideoBidIfNoBannerAndHasVideo() throws JsonProcessingException {
         // given
-        final HttpCall httpCall = givenHttpCall(null, mapper.writeValueAsString(null));
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().video(Video.builder().build()).id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).isEmpty();
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
     }
 
     @Test
-    public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException {
+    public void makeBidsShouldReturnBannerBidIfHasBothBannerAndVideo() throws JsonProcessingException {
         // given
-        final HttpCall httpCall = givenHttpCall(null,
-                mapper.writeValueAsString(BidResponse.builder().build()));
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(givenImp(identity())))
+                        .build(),
+                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).isEmpty();
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
-    public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException {
+    public void makeBidsShouldReturnBannerBidIfNativeIsPresentInRequestImp() throws JsonProcessingException {
         // given
         final HttpCall httpCall = givenHttpCall(
-                BidRequest.builder().imp(singletonList(Imp.builder().id("123").build())).build(),
-                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").xNative(Native.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
-    public void makeBidsShouldReturnVideoBidIfNoBannerAndHasVideo() throws JsonProcessingException {
+    public void makeBidsShouldReturnBannerBidIfAudioIsPresentInRequestImp() throws JsonProcessingException {
         // given
         final HttpCall httpCall = givenHttpCall(
                 BidRequest.builder()
-                        .imp(singletonList(Imp.builder().video(Video.builder().build()).id("123").build()))
+                        .imp(singletonList(Imp.builder().id("123").audio(Audio.builder().build()).build()))
                         .build(),
-                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
-    public void makeBidsShouldReturnBannerBidIfHasBothBannerAndVideo() throws JsonProcessingException {
+    public void makeBidsShouldReturnErrorWithUnknownBidTypeIfNotSupportedBidType() throws JsonProcessingException {
         // given
-        final HttpCall httpCall = givenHttpCall(
-                BidRequest.builder()
-                        .imp(singletonList(givenImp(identity())))
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
                         .build(),
-                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("125"))));
 
         // when
         final Result> result = admanBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+        assertThat(result.getErrors()).hasSize(1)
+                .containsExactly(BidderError.badServerResponse("Failed to find impression 125"));
+        assertThat(result.getValue()).isEmpty();
     }
 
     private static BidRequest givenBidRequest(
diff --git a/src/test/java/org/prebid/server/bidder/admixer/AdmixerBidderTest.java b/src/test/java/org/prebid/server/bidder/admixer/AdmixerBidderTest.java
index 84bfc3f3c18..462a7d78710 100644
--- a/src/test/java/org/prebid/server/bidder/admixer/AdmixerBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/admixer/AdmixerBidderTest.java
@@ -23,7 +23,7 @@
 import org.prebid.server.proto.openrtb.ext.ExtPrebid;
 import org.prebid.server.proto.openrtb.ext.request.admixer.ExtImpAdmixer;
 
-import java.util.Collections;
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -55,18 +55,79 @@ public void creationShouldFailOnInvalidEndpointUrl() {
     }
 
     @Test
-    public void makeHttpRequestsShouldReturnErrorIfImpressionListSizeIsZero() {
+    public void shouldSetBidfloorToZeroIfExtImpFloorValuIsNull() {
         // given
         final BidRequest bidRequest = BidRequest.builder()
-                .imp(emptyList())
+                .imp(singletonList(Imp.builder()
+                        .id("123")
+                        .ext(mapper.valueToTree(ExtPrebid.of(null,
+                                ExtImpAdmixer.of("tentententtentententtentententetetet", null,
+                                        givenCustomParams("foo1", singletonList("bar1"))))))
+                        .build()))
                 .build();
 
         // when
         final Result>> result = admixerBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput("No valid impressions in the bid request"));
+        final Imp expectedImp = Imp.builder()
+                .id("123")
+                .tagid("tentententtentententtentententetetet")
+                .bidfloor(BigDecimal.ZERO)
+                .ext(mapper.valueToTree(ExtImpAdmixer.of(null, null,
+                                givenCustomParams("foo1", singletonList("bar1")))))
+                .build();
+        assertThat(result.getErrors()).hasSize(0);
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
+                .flatExtracting(BidRequest::getImp)
+                .containsExactly(expectedImp);
+    }
+
+    @Test
+    public void shouldSetExtToNullIfCustomParamsAreNotPresent() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .id("123")
+                        .ext(mapper.valueToTree(ExtPrebid.of(null,
+                                ExtImpAdmixer.of("tentententtentententtentententetetet", null, null))))
+                        .build()))
+                .build();
+
+        // when
+        final Result>> result = admixerBidder.makeHttpRequests(bidRequest);
+
+        // then
+        final Imp expectedImp = Imp.builder()
+                .id("123")
+                .tagid("tentententtentententtentententetetet")
+                .bidfloor(BigDecimal.ZERO)
+                .ext(null)
+                .build();
+        assertThat(result.getErrors()).hasSize(0);
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
+                .flatExtracting(BidRequest::getImp)
+                .containsExactly(expectedImp);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .id("123")
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))).build()))
+                .build();
+
+        // when
+        final Result>> result = admixerBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors()).allMatch(error -> error.getType() == BidderError.Type.bad_input
+                && error.getMessage().startsWith("Wrong Admixer bidder ext in imp with id : 123"));
     }
 
     @Test
@@ -84,7 +145,7 @@ public void makeHttpRequestsShouldReturnErrorIfZoneIdNotHaveLength() {
 
         // then
         assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput("ZoneId must be UUID/GUID"));
+                .containsExactly(BidderError.badInput("ZoneId must be UUID/GUID"));
     }
 
     @Test
@@ -96,7 +157,22 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
         final Result> result = admixerBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response);
+        assertThat(result.getErrors()).allMatch(error -> error.getType() == BidderError.Type.bad_server_response
+                && error.getMessage().startsWith("Failed to decode:"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyResponseWhenResponseIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall =
+                givenHttpCall(mapper.writeValueAsString(null));
+
+        // when
+        final Result> result = admixerBidder.makeBids(httpCall, BidRequest.builder().build());
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -111,9 +187,7 @@ public void makeBidsShouldReturnErrorsWhenSeatBidIsEmptyList() throws JsonProces
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result).isNotNull()
-                .extracting(Result::getValue, Result::getErrors)
-                .containsOnly(Collections.emptyList(), Collections.emptyList());
+        assertThat(result.getValue()).isEmpty();
     }
 
     @Test
@@ -131,9 +205,26 @@ public void makeBidsShouldReturnErrorsWhenBidsEmptyList()
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result).isNotNull()
-                .extracting(Result::getValue, Result::getErrors)
-                .containsOnly(Collections.emptyList(), Collections.emptyList());
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = admixerBidder.makeBids(httpCall,
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .build());
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
@@ -152,7 +243,7 @@ public void makeBidsShouldReturnBannerBidIfBannerIsPresentInRequestImp() throws
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
     }
 
     @Test
@@ -171,7 +262,7 @@ public void makeBidsShouldReturnBannerBidIfVideoIsPresentInRequestImp() throws J
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
     }
 
     @Test
@@ -190,7 +281,7 @@ public void makeBidsShouldReturnBannerBidIfNativeIsPresentInRequestImp() throws
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
     }
 
     @Test
@@ -209,7 +300,7 @@ public void makeBidsShouldReturnBannerBidIfAudioIsPresentInRequestImp() throws J
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), audio, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), audio, "USD"));
     }
 
     private static BidResponse givenBidResponse(Function bidCustomizer) {
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 4500272bdbc..9fbc71e9990 100644
--- a/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/adoppler/AdopplerBidderTest.java
@@ -4,7 +4,6 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
-import com.iab.openrtb.request.Format;
 import com.iab.openrtb.request.Imp;
 import com.iab.openrtb.request.Video;
 import com.iab.openrtb.response.Bid;
@@ -25,7 +24,7 @@
 import org.prebid.server.proto.openrtb.ext.request.adoppler.ExtImpAdoppler;
 import org.prebid.server.util.HttpUtil;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -39,7 +38,7 @@
 
 public class AdopplerBidderTest extends VertxTest {
 
-    private static final String ENDPOINT_URL = "https://test.endpoint.com";
+    private static final String ENDPOINT_URL = "http://{{AccountID}}.test.com/some/path/{{AdUnit}}";
 
     private AdopplerBidder adopplerBidder;
 
@@ -58,26 +57,35 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 impBuilder -> impBuilder
-                        .banner(Banner.builder()
-                                .format(singletonList(Format.builder().w(300).h(500).build()))
-                                .build())
-                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of(null)))));
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of(null, null)))));
         // when
         final Result>> result = adopplerBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("$.imp.ext.adoppler.adunit required");
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("adunit parameter is required for adoppler bidder"));
     }
 
     @Test
     public void makeHttpRequestsShouldCreateCorrectURL() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(identity());
+
+        // when
+        final Result>> result = adopplerBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1);
+        assertThat(result.getValue().get(0).getUri()).isEqualTo("http://clientId.test.com/some/path/adUnit");
+    }
+
+    @Test
+    public void makeHttpRequestsShouldCreateUrlWithDefaultAppParamIfClientIsMissing() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 impBuilder -> impBuilder
-                        .banner(Banner.builder()
-                                .format(singletonList(Format.builder().w(300).h(500).build()))
-                                .build()));
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit", "")))));
 
         // when
         final Result>> result = adopplerBidder.makeHttpRequests(bidRequest);
@@ -85,11 +93,11 @@ public void makeHttpRequestsShouldCreateCorrectURL() {
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(1);
-        assertThat(result.getValue().get(0).getUri()).isEqualTo("https://test.endpoint.com/processHeaderBid/adUnit");
+        assertThat(result.getValue().get(0).getUri()).isEqualTo("http://app.test.com/some/path/adUnit");
     }
 
     @Test
-    public void makeHttpRequestsShouldSetExpectedRequestUrlAndDefaultHeaders() {
+    public void makeHttpRequestsShouldSetExpectedHeaders() {
         // given
         final BidRequest bidRequest = givenBidRequest(identity());
 
@@ -100,7 +108,7 @@ public void makeHttpRequestsShouldSetExpectedRequestUrlAndDefaultHeaders() {
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue().get(0).getHeaders()).isNotNull()
                 .extracting(Map.Entry::getKey, Map.Entry::getValue)
-                .containsOnly(tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"),
+                .containsExactlyInAnyOrder(tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"),
                         tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE),
                         tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()));
     }
@@ -110,11 +118,9 @@ public void makeBidsShouldReturnErrorIfDuplicateId() throws JsonProcessingExcept
         // given
         final Imp imp1 = Imp.builder().id("impId").banner(Banner.builder().build()).build();
         final Imp imp2 = Imp.builder().id("impId").video(Video.builder().build()).build();
-        final List imps = new ArrayList();
-        imps.add(imp1);
-        imps.add(imp2);
-        BidRequest bidRequest = BidRequest.builder()
-                .imp(imps)
+
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(Arrays.asList(imp1, imp2))
                 .build();
         final HttpCall httpCall = givenHttpCall(
                 bidRequest, mapper.writeValueAsString(
@@ -124,10 +130,8 @@ public void makeBidsShouldReturnErrorIfDuplicateId() throws JsonProcessingExcept
         final Result> result = adopplerBidder.makeBids(httpCall, bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage())
-                .startsWith("duplicate $.imp.id impId");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("duplicate $.imp.id impId"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -150,10 +154,9 @@ public void makeBidsShouldReturnErrorIfEmptyImp() throws JsonProcessingException
         final Result> result = adopplerBidder.makeBids(httpCall, bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage())
-                .startsWith("one of $.imp.banner, $.imp.video, $.imp.audio and $.imp.native field required");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("one of $.imp.banner, $.imp.video, "
+                        + "$.imp.audio and $.imp.native field required"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -173,10 +176,8 @@ public void makeBidsShouldReturnErrorIfBidIdEmpty() throws JsonProcessingExcepti
         final Result> result = adopplerBidder.makeBids(httpCall, bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage())
-                .startsWith("unknown impid: null");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("unknown impId: null"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -184,8 +185,7 @@ public void makeBidsShouldReturnErrorIfBidIdEmpty() throws JsonProcessingExcepti
     public void makeBidsShouldReturnErrorIfExtEmpty() throws JsonProcessingException {
         // given
         final Imp imp = Imp.builder().id("impId").video(Video.builder().build()).build();
-        final List imps = Collections.singletonList(imp);
-        final BidRequest bidRequest = BidRequest.builder().imp(imps).build();
+        final BidRequest bidRequest = BidRequest.builder().imp(Collections.singletonList(imp)).build();
         final ObjectNode ext = mapper.valueToTree(AdopplerResponseExt.of(null));
         final HttpCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString(
                 givenBidResponse(bidBuilder -> bidBuilder
@@ -197,10 +197,8 @@ public void makeBidsShouldReturnErrorIfExtEmpty() throws JsonProcessingException
         final Result> result = adopplerBidder.makeBids(httpCall, bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage())
-                .startsWith("$.seatbid.bid.ext.ads.video required");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("$.seatbid.bid.ext.ads.video required"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -221,7 +219,7 @@ private static Imp givenImp(Function impCustomiz
         return impCustomizer.apply(Imp.builder()
                 .id("123")
                 .banner(Banner.builder().id("banner_id").build())
-                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit")))))
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit", "clientId")))))
                 .build();
     }
 
diff --git a/src/test/java/org/prebid/server/bidder/amx/AmxBidderTest.java b/src/test/java/org/prebid/server/bidder/amx/AmxBidderTest.java
new file mode 100644
index 00000000000..1aafc804b8c
--- /dev/null
+++ b/src/test/java/org/prebid/server/bidder/amx/AmxBidderTest.java
@@ -0,0 +1,273 @@
+package org.prebid.server.bidder.amx;
+
+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.Banner;
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Imp;
+import com.iab.openrtb.request.Publisher;
+import com.iab.openrtb.request.Site;
+import com.iab.openrtb.response.Bid;
+import com.iab.openrtb.response.BidResponse;
+import com.iab.openrtb.response.SeatBid;
+import org.junit.Before;
+import org.junit.Test;
+import org.prebid.server.VertxTest;
+import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.bidder.model.BidderError;
+import org.prebid.server.bidder.model.HttpCall;
+import org.prebid.server.bidder.model.HttpRequest;
+import org.prebid.server.bidder.model.HttpResponse;
+import org.prebid.server.bidder.model.Result;
+import org.prebid.server.proto.openrtb.ext.ExtPrebid;
+import org.prebid.server.proto.openrtb.ext.request.amx.ExtImpAmx;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+import static java.util.Collections.singletonList;
+import static java.util.function.Function.identity;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
+
+public class AmxBidderTest extends VertxTest {
+
+    private static final String ENDPOINT_URL = "https://test.com/prebid/bid";
+
+    private AmxBidder amxBidder;
+
+    @Before
+    public void setUp() {
+        amxBidder = new AmxBidder(ENDPOINT_URL, jacksonMapper);
+    }
+
+    @Test
+    public void creationShouldFailOnInvalidEndpointUrl() {
+        assertThatIllegalArgumentException().isThrownBy(() -> new AmxBidder("invalid_url", jacksonMapper));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))));
+        // when
+        final Result>> result = amxBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors()).allMatch(error -> error.getType() == BidderError.Type.bad_input
+                && error.getMessage().startsWith("Cannot deserialize instance"));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldCreateCorrectURL() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.banner(Banner.builder().build()));
+
+        // when
+        final Result>> result = amxBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(HttpRequest::getUri)
+                .containsExactly("https://test.com/prebid/bid?v=pbs1.0");
+    }
+
+    @Test
+    public void makeHttpRequestsShouldUpdateRequestAndImps() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                bidRequestBuilder -> bidRequestBuilder.app(App.builder().build()).site(Site.builder().build()),
+                impBuilder -> impBuilder.banner(Banner.builder().build()));
+
+        // when
+        final Result>> result = amxBidder.makeHttpRequests(bidRequest);
+
+        // then
+        final BidRequest expectedBidRequest = BidRequest.builder()
+                .app(App.builder().publisher(Publisher.builder().id("testTagId").build()).build())
+                .site(Site.builder().publisher(Publisher.builder().id("testTagId").build()).build())
+                .imp(singletonList(Imp.builder()
+                        .id("123")
+                        .banner(Banner.builder().build())
+                        .tagid("testAdUnitId")
+                        .ext(mapper.valueToTree(ExtPrebid.of(null,
+                                ExtImpAmx.of("testTagId", "testAdUnitId"))))
+                        .build())).build();
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
+                .containsExactly(expectedBidRequest);
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
+        // given
+        final HttpCall httpCall = givenHttpCall(null, "invalid");
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors()).allMatch(error -> error.getType() == BidderError.Type.bad_server_response
+                && error.getMessage().startsWith("Failed to decode: Unrecognized token"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(null));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(BidResponse.builder().build()));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnBannerBidIfBannerIsBidExtNotPresent() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder().build(), mapper.writeValueAsString(givenBidResponse(identity())));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder().build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnVideoBidIfStartDelayIsPresentInBidExt() throws JsonProcessingException {
+        // given
+        final ObjectNode bidExt = mapper.createObjectNode();
+        bidExt.put("himp", mapper.convertValue(Arrays.asList("someHintVAlue1", "someHintValue2"), JsonNode.class));
+        bidExt.put("startdelay", "2");
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder().build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder
+                                .adm("ExistingAdm")
+                                .nurl("nurlValue")
+                                .ext(bidExt))));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        final String expectedAdm = "ExistingAdm"
+                + ""
+                + ""
+                + "";
+        assertThat(result.getValue())
+                .containsExactly(BidderBid.of(Bid.builder()
+                        .nurl("")
+                        .ext(bidExt)
+                        .adm(expectedAdm)
+                        .build(), video, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfAdmNotContainSearchPoint() throws JsonProcessingException {
+        // given
+        final ObjectNode bidExt = mapper.createObjectNode();
+        bidExt.put("startdelay", "2");
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder().build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder
+                                .id("bidId")
+                                .adm("no_point")
+                                .ext(bidExt))));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors())
+                .containsExactly(
+                        BidderError.badServerResponse("Adm should contain vast search point in bidder: bidId"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfAdmIsNotPresent() throws JsonProcessingException {
+        // given
+        final ObjectNode bidExt = mapper.createObjectNode();
+        bidExt.put("startdelay", "2");
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder().build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder
+                                .id("bidId")
+                                .ext(bidExt))));
+
+        // when
+        final Result> result = amxBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badServerResponse("Adm should not be blank in bidder: bidId"));
+    }
+
+    private static BidRequest givenBidRequest(
+            Function bidRequestCustomizer,
+            Function impCustomizer) {
+
+        return bidRequestCustomizer.apply(BidRequest.builder()
+                .imp(singletonList(givenImp(impCustomizer))))
+                .build();
+    }
+
+    private static BidRequest givenBidRequest(Function impCustomizer) {
+        return givenBidRequest(identity(), impCustomizer);
+    }
+
+    private static Imp givenImp(Function impCustomizer) {
+        return impCustomizer.apply(Imp.builder()
+                .id("123")
+                .banner(Banner.builder().id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpAmx.of("testTagId", "testAdUnitId")))))
+                .build();
+    }
+
+    private static BidResponse givenBidResponse(Function bidCustomizer) {
+        return BidResponse.builder()
+                .cur("USD")
+                .seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
+                        .build()))
+                .build();
+    }
+
+    private static HttpCall givenHttpCall(BidRequest bidRequest, String body) {
+        return HttpCall.success(
+                HttpRequest.builder().payload(bidRequest).build(),
+                HttpResponse.of(200, null, body),
+                null);
+    }
+}
+
diff --git a/src/test/java/org/prebid/server/bidder/appnexus/AppnexusBidderTest.java b/src/test/java/org/prebid/server/bidder/appnexus/AppnexusBidderTest.java
index cca21834b53..e3a6e5f8745 100644
--- a/src/test/java/org/prebid/server/bidder/appnexus/AppnexusBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/appnexus/AppnexusBidderTest.java
@@ -299,11 +299,11 @@ public void makeHttpRequestsShouldSetRequestExtAppnexusTrueWhenPrimaryAdserverIs
     }
 
     @Test
-    public void makeHttpRequestsShouldUpdateRequestExtAppnexusTrueWhenPrimaryAdserverIsNotZero() {
+    public void makeHttpRequestsShouldUpdateRequestExtAppnexusTrueWhenPrimaryAdserverIsNotNull() {
         // given
         final ExtRequestPrebid requestPrebid = ExtRequestPrebid.builder()
                 .targeting(ExtRequestTargeting.builder()
-                        .includebrandcategory(ExtIncludeBrandCategory.of(-120, null, null))
+                        .includebrandcategory(ExtIncludeBrandCategory.of(null, null, null))
                         .build())
                 .build();
 
@@ -327,11 +327,11 @@ public void makeHttpRequestsShouldUpdateRequestExtAppnexusTrueWhenPrimaryAdserve
     }
 
     @Test
-    public void makeHttpRequestsShouldNotUpdateRequestExtAppnexusWhenPrimaryAdserverIsZero() {
+    public void makeHttpRequestsShouldNotUpdateRequestExtAppnexusWhenIncludeBrandCategoryIsNull() {
         // given
         final ExtRequestPrebid requestPrebid = ExtRequestPrebid.builder()
                 .targeting(ExtRequestTargeting.builder()
-                        .includebrandcategory(ExtIncludeBrandCategory.of(0, null, null))
+                        .includebrandcategory(null)
                         .build())
                 .build();
 
diff --git a/src/test/java/org/prebid/server/bidder/conversant/ConversantBidderTest.java b/src/test/java/org/prebid/server/bidder/conversant/ConversantBidderTest.java
index 7e011b10a2d..990a964ef2e 100644
--- a/src/test/java/org/prebid/server/bidder/conversant/ConversantBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/conversant/ConversantBidderTest.java
@@ -23,6 +23,7 @@
 import org.prebid.server.proto.openrtb.ext.request.conversant.ExtImpConversant;
 
 import java.math.BigDecimal;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
 
@@ -38,17 +39,20 @@
 public class ConversantBidderTest extends VertxTest {
 
     private static final String ENDPOINT_URL = "https://test.endpoint.com";
+    private static final String UUID_REGEX = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}"
+            + "-[0-9a-fA-F]{12}";
 
     private ConversantBidder conversantBidder;
 
     @Before
     public void setUp() {
-        conversantBidder = new ConversantBidder(ENDPOINT_URL, jacksonMapper);
+        conversantBidder = new ConversantBidder(ENDPOINT_URL, false, jacksonMapper);
     }
 
     @Test
     public void creationShouldFailOnInvalidEndpointUrl() {
-        assertThatIllegalArgumentException().isThrownBy(() -> new ConversantBidder("invalid_url", jacksonMapper));
+        assertThatIllegalArgumentException()
+                .isThrownBy(() -> new ConversantBidder("invalid_url", false, jacksonMapper));
     }
 
     @Test
@@ -64,9 +68,6 @@ public void makeHttpRequestsShouldSkipInvalidImpAndAddErrorIfImpHasNoBannerOrVid
         final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput(
-                        "Invalid MediaType. Conversant supports only Banner and Video. Ignoring ImpID=123"));
         assertThat(result.getValue()).hasSize(1);
     }
 
@@ -81,8 +82,8 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
         final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(2);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance");
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("Impression[0] missing ext.bidder object"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -100,8 +101,8 @@ public void makeHttpRequestsShouldReturnErrorIfRequestHasSiteAndImpExtSiteIdIsNu
         final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput("Missing site id"));
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("Impression[0] requires ext.bidder.site_id"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -122,7 +123,7 @@ public void makeHttpRequestsShouldSetSiteIdFromImpExtForSiteRequest() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .extracting(BidRequest::getSite)
                 .extracting(Site::getId)
-                .containsOnly("site id");
+                .containsExactly("site id");
     }
 
     @Test
@@ -142,7 +143,7 @@ public void makeHttpRequestsShouldSetAppIdFromExtSiteIdIfAppIdIsNullOrEmpty() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .extracting(BidRequest::getApp)
                 .extracting(App::getId)
-                .containsOnly("site id");
+                .containsExactly("site id");
     }
 
     @Test
@@ -163,31 +164,11 @@ public void makeHttpRequestsShouldSetSiteIdFromExtSiteIdIfSiteIdIsNullOrEmpty()
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .extracting(BidRequest::getSite)
                 .extracting(Site::getId)
-                .containsOnly("site id");
-    }
-
-    @Test
-    public void makeHttpRequestsShouldSetSiteMobileFromImpExtIfPresent() {
-        // given
-        final BidRequest bidRequest = givenBidRequest(
-                requestBuilder -> requestBuilder.site(Site.builder().build()),
-                identity(),
-                extBuilder -> extBuilder.mobile(1));
-
-        // when
-        final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
-                .extracting(BidRequest::getSite)
-                .extracting(Site::getMobile)
-                .containsOnly(1);
+                .containsExactly("site id");
     }
 
     @Test
-    public void makeHttpRequestsShouldSetImpDisplaymanagerAndDisplaymanagerver() {
+    public void makeHttpRequestsShouldSetImpDisplayManagerAndDisplayManagerVer() {
         // given
         final BidRequest bidRequest = givenBidRequest(identity());
 
@@ -200,11 +181,11 @@ public void makeHttpRequestsShouldSetImpDisplaymanagerAndDisplaymanagerver() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getDisplaymanager, Imp::getDisplaymanagerver)
-                .containsOnly(tuple("prebid-s2s", "1.0.1"));
+                .containsExactly(tuple("prebid-s2s", "2.0.0"));
     }
 
     @Test
-    public void makeHttpRequestsShouldSetImpBidfloorFromImpExtIfPresent() {
+    public void makeHttpRequestsShouldSetImpBidFloorFromImpExtIfPresent() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 identity(),
@@ -219,7 +200,7 @@ public void makeHttpRequestsShouldSetImpBidfloorFromImpExtIfPresent() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getBidfloor)
-                .containsOnly(BigDecimal.valueOf(9.99));
+                .containsExactly(BigDecimal.valueOf(9.99));
     }
 
     @Test
@@ -238,7 +219,7 @@ public void makeHttpRequestsShouldSetImpTagIdFromImpExtIfPresent() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getTagid)
-                .containsOnly("tag id");
+                .containsExactly("tag id");
     }
 
     @Test
@@ -257,7 +238,7 @@ public void makeHttpRequestsShouldChangeImpSecureFromImpExtIfPresent() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getSecure)
-                .containsOnly(1);
+                .containsExactly(1);
     }
 
     @Test
@@ -276,14 +257,15 @@ public void makeHttpRequestsShouldNotChangeImpSecure() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getSecure)
-                .containsOnly(1);
+                .containsExactly(1);
     }
 
     @Test
-    public void makeHttpRequestsShouldSetImpBannerAndVideoPosFromImpExtIfPresent() {
+    public void makeHttpRequestsShouldSetImpForBannerOnlyFromImpExtWhenVideoIsPresent() {
         // given
+        final Video requestVideo = Video.builder().pos(1).build();
         final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder.video(Video.builder().pos(1).build()),
+                impBuilder -> impBuilder.banner(Banner.builder().pos(1).build()).video(requestVideo),
                 extBuilder -> extBuilder.position(5));
 
         // when
@@ -295,16 +277,35 @@ public void makeHttpRequestsShouldSetImpBannerAndVideoPosFromImpExtIfPresent() {
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getBanner, Imp::getVideo)
-                .containsOnly(tuple(
+                .containsExactly(tuple(
                         Banner.builder().pos(5).build(),
-                        Video.builder().pos(5).build()));
+                        requestVideo));
     }
 
     @Test
-    public void makeHttpRequestsShouldNotChangeVideoPosFromImpExtIfNotInRange() {
+    public void makeHttpRequestsShouldSetPosBannerToNullIfExtPosIsNull() {
         // given
         final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder.video(Video.builder().pos(7).build()),
+                impBuilder -> impBuilder.banner(Banner.builder().pos(23).build()),
+                extBuilder -> extBuilder.position(null));
+
+        // when
+        final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getBanner)
+                .containsExactly(Banner.builder().pos(null).build());
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetPosBannerToNullIfExtPosIsNotValid() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.banner(Banner.builder().pos(23).build()),
                 extBuilder -> extBuilder.position(9));
 
         // when
@@ -315,9 +316,27 @@ public void makeHttpRequestsShouldNotChangeVideoPosFromImpExtIfNotInRange() {
         assertThat(result.getValue()).hasSize(1)
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
-                .extracting(Imp::getVideo)
-                .extracting(Video::getPos)
-                .containsOnly(7);
+                .extracting(Imp::getBanner)
+                .containsExactly(Banner.builder().pos(null).build());
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetPosBannerToPosFromExt() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.banner(Banner.builder().pos(23).build()),
+                extBuilder -> extBuilder.position(7));
+
+        // when
+        final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getBanner)
+                .containsExactly(Banner.builder().pos(7).build());
     }
 
     @Test
@@ -357,7 +376,27 @@ public void makeHttpRequestsShouldSetVideoMimesFromImpExtIfPresent() {
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getVideo)
                 .flatExtracting(Video::getMimes)
-                .containsOnly("mime");
+                .containsExactly("mime");
+    }
+
+    @Test
+    public void makeHttpRequestsShouldNotSetMimesFromExtIfExtMimesNotPresent() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.video(Video.builder().mimes(singletonList("videoMimes")).build()),
+                extBuilder -> extBuilder.mimes(Collections.emptyList()));
+
+        // when
+        final Result>> result = conversantBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getVideo)
+                .flatExtracting(Video::getMimes)
+                .containsExactly("videoMimes");
     }
 
     @Test
@@ -377,7 +416,7 @@ public void makeHttpRequestsShouldSetVideoMaxdurationFromImpExtIfPresent() {
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getVideo)
                 .extracting(Video::getMaxduration)
-                .containsOnly(1000);
+                .containsExactly(1000);
     }
 
     @Test
@@ -397,7 +436,7 @@ public void makeHttpRequestsShouldChangeVideoApiFromImpExtIfPresent() {
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getVideo)
                 .flatExtracting(Video::getApi)
-                .containsOnly(2, 5);
+                .containsExactly(2, 5);
     }
 
     @Test
@@ -437,7 +476,7 @@ public void makeHttpRequestsShouldChangeVideoProtocolsFromImpExtIfPresent() {
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getVideo)
                 .flatExtracting(Video::getProtocols)
-                .containsOnly(1, 10);
+                .containsExactly(1, 10);
     }
 
     @Test
@@ -469,9 +508,11 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
         final Result> result = conversantBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response);
+        assertThat(result.getErrors()).hasSize(1)
+                .allSatisfy(error -> {
+                    assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response);
+                    assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token");
+                });
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -485,7 +526,8 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces
         final Result> result = conversantBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badServerResponse("Empty bid request"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -499,7 +541,8 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso
         final Result> result = conversantBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badServerResponse("Empty bid request"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -518,7 +561,22 @@ public void makeBidsShouldReturnBannerBid() throws JsonProcessingException {
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfNoBidsFromSeatArePresent() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(BidResponse.builder()
+                        .seatbid(singletonList(SeatBid.builder().build())).build()));
+
+        // when
+        final Result> result = conversantBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).containsOnly(BidderError.badServerResponse("Empty bids array"));
+        assertThat(result.getValue()).isEmpty();
     }
 
     @Test
@@ -537,7 +595,29 @@ public void makeBidsShouldReturnVideoBidIfRequestImpHasVideo() throws JsonProces
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldUpdateBidWithUUIDIfGenerateBidIdIsTrue() throws JsonProcessingException {
+        // given
+        conversantBidder = new ConversantBidder(ENDPOINT_URL, true, jacksonMapper);
+        final HttpCall httpCall = givenHttpCall(
+                givenBidRequest(builder -> builder.id("123")
+                        .banner(Banner.builder().build())),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = conversantBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .extracting(BidderBid::getBid)
+                .extracting(Bid::getId)
+                .element(0)
+                .matches(id -> id.matches(UUID_REGEX), "matches UUID format");
     }
 
     private static BidRequest givenBidRequest(
@@ -570,7 +650,6 @@ private static Imp givenImp(
 
         return impCustomizer.apply(Imp.builder()
                 .id("123")
-                .banner(Banner.builder().build())
                 .ext(mapper.valueToTree(ExtPrebid.of(null,
                         extCustomizer.apply(ExtImpConversant.builder().siteId("site id")).build()))))
                 .build();
diff --git a/src/test/java/org/prebid/server/bidder/deepintent/DeepintentBidderTest.java b/src/test/java/org/prebid/server/bidder/deepintent/DeepintentBidderTest.java
index 14a98370c59..b10e41233d0 100644
--- a/src/test/java/org/prebid/server/bidder/deepintent/DeepintentBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/deepintent/DeepintentBidderTest.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Format;
 import com.iab.openrtb.request.Imp;
 import com.iab.openrtb.request.Native;
 import com.iab.openrtb.request.Video;
@@ -30,7 +31,6 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
-import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
 
 public class DeepintentBidderTest extends VertxTest {
 
@@ -56,18 +56,78 @@ public void creationShouldFailOnInvalidEndpointUrl() {
     public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
         // given
         final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))));
+                impBuilder -> impBuilder
+                        .id("impId")
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))));
 
         // when
         final Result>> result = deepintentBidder.makeHttpRequests(bidRequest);
 
         // then
         assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
+        assertThat(result.getErrors()).allSatisfy(bidderError -> {
+            assertThat(bidderError.getType()).isEqualTo(BidderError.Type.bad_input);
+            assertThat(bidderError.getMessage()).isEqualTo("Impression id=impId, has invalid Ext");
+        });
     }
 
     @Test
-    public void shouldSetDislplayManagerAndVersionAndTagIdToRequestImp() {
+    public void makeHttpRequestsShouldReturnErrorIfImpBannerIsNull() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.id("impId").banner(null));
+
+        // when
+        final Result>> result = deepintentBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors()).allSatisfy(bidderError -> {
+            assertThat(bidderError.getType()).isEqualTo(BidderError.Type.bad_input);
+            assertThat(bidderError.getMessage()).isEqualTo("We need a Banner Object in the request, imp : impId");
+        });
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfImpBannerHasNoSizeParametersPresent() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.id("impId").banner(Banner.builder().build()));
+
+        // when
+        final Result>> result = deepintentBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors()).allSatisfy(bidderError -> {
+            assertThat(bidderError.getType()).isEqualTo(BidderError.Type.bad_input);
+            assertThat(bidderError.getMessage()).isEqualTo("At least one size is required, imp : impId");
+        });
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetMissedBannerSizeFromBannerFormat() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder
+                .banner(Banner.builder().format(singletonList(Format.builder().w(77).h(88).build())).build()));
+
+        // when
+        final Result>> result = deepintentBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(0);
+        final Imp expectedImp = expectedImp(impBuilder -> impBuilder
+                .banner(Banner.builder().w(77).h(88)
+                        .format(singletonList(Format.builder().w(77).h(88).build()))
+                        .build()));
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
+                .flatExtracting(BidRequest::getImp)
+                .containsExactly(expectedImp);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetDislplayManagerAndVersionAndTagIdToRequestImp() {
         // given
         final BidRequest bidRequest = givenBidRequest(identity());
 
@@ -76,12 +136,7 @@ public void shouldSetDislplayManagerAndVersionAndTagIdToRequestImp() {
 
         // then
         assertThat(result.getErrors()).hasSize(0);
-        final Imp expectedImp = Imp.builder()
-                .displaymanager(DISPLAY_MANAGER)
-                .displaymanagerver(DISPLAY_MANAGER_VERSION)
-                .tagid(IMP_EXT_TAG_ID)
-                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of(IMP_EXT_TAG_ID))))
-                .build();
+        final Imp expectedImp = expectedImp(identity());
         assertThat(result.getValue())
                 .extracting(HttpRequest::getPayload)
                 .flatExtracting(BidRequest::getImp)
@@ -92,6 +147,7 @@ public void shouldSetDislplayManagerAndVersionAndTagIdToRequestImp() {
     public void makeRequestShouldCreateRequestForEveryValidImp() {
         // given
         final Imp firstImp = Imp.builder()
+                .banner(Banner.builder().w(23).h(25).build())
                 .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of(IMP_EXT_TAG_ID))))
                 .build();
         final Imp secondImp = Imp.builder()
@@ -106,11 +162,7 @@ public void makeRequestShouldCreateRequestForEveryValidImp() {
 
         // then
         assertThat(result.getErrors()).hasSize(1);
-        final Imp expectedFirstImp = firstImp.toBuilder()
-                .displaymanager(DISPLAY_MANAGER)
-                .displaymanagerver(DISPLAY_MANAGER_VERSION)
-                .tagid(IMP_EXT_TAG_ID)
-                .build();
+        final Imp expectedFirstImp = expectedImp(identity());
 
         assertThat(result.getValue())
                 .hasSize(1)
@@ -122,12 +174,11 @@ public void makeRequestShouldCreateRequestForEveryValidImp() {
     @Test
     public void makeRequestShouldCreateSeparateRequestForEveryImp() {
         // given
-        final Imp firstImp = Imp.builder()
-                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of("firstImpTagId"))))
-                .build();
-        final Imp secondImp = Imp.builder()
-                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of("secondImpTagId"))))
-                .build();
+        final Imp firstImp = givenImp(impBuilder ->
+                impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of("firstImpTagId")))));
+        final Imp secondImp = givenImp(impBuilder ->
+                impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of("secondImpTagId")))));
+
         final BidRequest bidRequest = BidRequest.builder()
                 .imp(Arrays.asList(firstImp, secondImp))
                 .build();
@@ -137,16 +188,17 @@ public void makeRequestShouldCreateSeparateRequestForEveryImp() {
 
         // then
         assertThat(result.getErrors()).hasSize(0);
-        final Imp expectedFirstImp = firstImp.toBuilder()
-                .displaymanager(DISPLAY_MANAGER)
-                .displaymanagerver(DISPLAY_MANAGER_VERSION)
-                .tagid("firstImpTagId")
-                .build();
-        final Imp expectedSecondImp = secondImp.toBuilder()
-                .displaymanager(DISPLAY_MANAGER)
-                .displaymanagerver(DISPLAY_MANAGER_VERSION)
-                .tagid("secondImpTagId")
-                .build();
+        final Imp expectedFirstImp =
+                expectedImp(impBuilder ->
+                        impBuilder.ext(mapper.valueToTree(
+                                ExtPrebid.of(null, ExtImpDeepintent.of("firstImpTagId"))))
+                                .tagid("firstImpTagId"));
+
+        final Imp expectedSecondImp =
+                expectedImp(impBuilder ->
+                        impBuilder.ext(mapper.valueToTree(
+                                ExtPrebid.of(null, ExtImpDeepintent.of("secondImpTagId"))))
+                                .tagid("secondImpTagId"));
 
         assertThat(result.getValue())
                 .hasSize(2)
@@ -165,10 +217,10 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
 
         // then
         assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token");
-        assertThat(result.getErrors())
-                .extracting(BidderError::getType)
-                .containsExactly(BidderError.Type.bad_server_response);
+        assertThat(result.getErrors()).allSatisfy(error -> {
+            assertThat(error.getMessage()).contains("Failed to decode: Unrecognized token");
+            assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response);
+        });
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -200,43 +252,6 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso
         assertThat(result.getValue()).isEmpty();
     }
 
-    @Test
-    public void makeBidsShouldReturnBannerBidIfBannerIsPresentInRequestImp() throws JsonProcessingException {
-        // given
-        final HttpCall httpCall = givenHttpCall(
-                BidRequest.builder()
-                        .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build()))
-                        .build(),
-                mapper.writeValueAsString(
-                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
-
-        // when
-        final Result> result = deepintentBidder.makeBids(httpCall, null);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, CURRENCY));
-    }
-
-    @Test
-    public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws JsonProcessingException {
-        // given
-        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
-                        .imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build()))
-                        .build(),
-                mapper.writeValueAsString(
-                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
-
-        // when
-        final Result> result = deepintentBidder.makeBids(httpCall, null);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, CURRENCY));
-    }
-
     @Test
     public void shouldReturnErrorIfBidImpIdNotFoundInImps() throws JsonProcessingException {
         // given
@@ -251,7 +266,7 @@ public void shouldReturnErrorIfBidImpIdNotFoundInImps() throws JsonProcessingExc
 
         // then
         assertThat(result.getErrors())
-                .containsExactly(BidderError.badInput("Failed to find impression with id: notFoundId"));
+                .containsExactly(BidderError.badServerResponse("Failed to find impression with id: notFoundId"));
     }
 
     @Test
@@ -288,6 +303,17 @@ private static BidRequest givenBidRequest(Function impCustomizer) {
         return impCustomizer.apply(Imp.builder()
+                .banner(Banner.builder().w(23).h(25).build())
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of(IMP_EXT_TAG_ID)))))
+                .build();
+    }
+
+    private Imp expectedImp(Function impCustomizer) {
+        return impCustomizer.apply(Imp.builder()
+                .banner(Banner.builder().w(23).h(25).build())
+                .displaymanager(DISPLAY_MANAGER)
+                .displaymanagerver(DISPLAY_MANAGER_VERSION)
+                .tagid(IMP_EXT_TAG_ID)
                 .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpDeepintent.of(IMP_EXT_TAG_ID)))))
                 .build();
     }
diff --git a/src/test/java/org/prebid/server/bidder/dmx/DmxBidderTest.java b/src/test/java/org/prebid/server/bidder/dmx/DmxBidderTest.java
index 663d5ce69e3..b11e21a00ee 100644
--- a/src/test/java/org/prebid/server/bidder/dmx/DmxBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/dmx/DmxBidderTest.java
@@ -1,7 +1,6 @@
 package org.prebid.server.bidder.dmx;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.Format;
@@ -24,6 +23,7 @@
 import org.prebid.server.proto.openrtb.ext.request.dmx.ExtImpDmx;
 import org.prebid.server.proto.openrtb.ext.response.BidType;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.function.Function;
 
@@ -32,7 +32,6 @@
 import static java.util.function.Function.identity;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.assertj.core.api.Assertions.tuple;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
 
@@ -96,22 +95,58 @@ public void makeHttpRequestsShouldReturnErrorIfUserIdIsEmpty() {
     @Test
     public void makeHttpRequestsShouldModifyImpIfBannerFormatIsNotEmpty() {
         // given
+        final Imp givenImp = Imp.builder()
+                .id("id")
+                .bidfloor(BigDecimal.ONE)
+                .banner(Banner.builder()
+                        .format(singletonList(Format.builder().w(300).h(500).build()))
+                        .build())
+                .ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpDmx.builder()
+                                .tagId("tagId")
+                                .dmxId("dmxId")
+                                .memberId("memberId")
+                                .publisherId("publisherId")
+                                .sellerId("sellerId")
+                                .build())))
+                .build();
         final BidRequest bidRequest = BidRequest.builder()
-                .imp(singletonList(
-                        Imp.builder()
-                                .id("id")
-                                .banner(Banner.builder()
-                                        .format(singletonList(Format.builder().w(300).h(500).build()))
-                                        .build())
-                                .ext(mapper.valueToTree(ExtPrebid.of(null,
-                                        ExtImpDmx.builder()
-                                                .tagId("tagId")
-                                                .dmxId("dmxId")
-                                                .memberId("memberId")
-                                                .publisherId("publisherId")
-                                                .sellerId("sellerId")
-                                                .build())))
-                                .build()))
+                .imp(singletonList(givenImp))
+                .user(User.builder().id("userId").build())
+                .build();
+
+        // when
+        final Result>> result = dmxBidder.makeHttpRequests(bidRequest);
+
+        // then
+        final Imp expectedImp = givenImp.toBuilder()
+                .tagid("dmxId")
+                .secure(1)
+                .build();
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .containsExactly(expectedImp);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldWriteTagIdToImpIfItIsPresentAndDmxIsMissing() {
+        // given
+        final Imp givenImp = Imp.builder()
+                .banner(Banner.builder()
+                        .format(singletonList(Format.builder().build()))
+                        .build())
+                .ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpDmx.builder()
+                                .tagId("tagId")
+                                .dmxId("")
+                                .memberId("memberId")
+                                .publisherId("publisherId")
+                                .build())))
+                .build();
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(givenImp))
                 .user(User.builder().id("userId").build())
                 .build();
 
@@ -120,19 +155,11 @@ public void makeHttpRequestsShouldModifyImpIfBannerFormatIsNotEmpty() {
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        final JsonNode expectedImpExt = mapper.valueToTree(ExtPrebid.of(null,
-                ExtImpDmx.builder()
-                        .tagId("tagId")
-                        .dmxId("dmxId")
-                        .memberId("memberId")
-                        .publisherId("publisherId")
-                        .sellerId("sellerId")
-                        .build()));
         assertThat(result.getValue()).hasSize(1)
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
-                .extracting(Imp::getId, Imp::getTagid, Imp::getExt, Imp::getSecure)
-                .containsOnly(tuple("id", "dmxId", expectedImpExt, 1));
+                .extracting(Imp::getTagid)
+                .containsExactly("tagId");
     }
 
     @Test
diff --git a/src/test/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidderTest.java b/src/test/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidderTest.java
index b8ed5088440..b522c3228f8 100644
--- a/src/test/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/emxdigital/EmxDigitalBidderTest.java
@@ -1,12 +1,14 @@
 package org.prebid.server.bidder.emxdigital;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.iab.openrtb.request.App;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.Device;
 import com.iab.openrtb.request.Format;
 import com.iab.openrtb.request.Imp;
 import com.iab.openrtb.request.Site;
+import com.iab.openrtb.request.Video;
 import com.iab.openrtb.response.Bid;
 import com.iab.openrtb.response.BidResponse;
 import com.iab.openrtb.response.SeatBid;
@@ -36,6 +38,7 @@
 import static org.assertj.core.api.Assertions.tuple;
 import static org.assertj.core.api.Assertions.within;
 import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
 
 public class EmxDigitalBidderTest extends VertxTest {
 
@@ -217,6 +220,7 @@ public void makeHttpRequestsShouldModifyImpWhenExtImpEmaDigitalContainsRequiredV
 
         // then
         final Imp expectedImp = Imp.builder()
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
                 .banner(Banner.builder().w(100).h(100).build())
                 .tagid("123")
                 .secure(1)
@@ -231,6 +235,187 @@ public void makeHttpRequestsShouldModifyImpWhenExtImpEmaDigitalContainsRequiredV
                 .containsOnly(expectedImp);
     }
 
+    @Test
+    public void makeHttpRequestsShouldRemoveVast40ProtocolFromVideo() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .video(Video.builder()
+                                .mimes(Collections.singletonList("someMime"))
+                                .protocols(Arrays.asList(1, 7, 2))
+                                .w(100)
+                                .h(100)
+                                .build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        final Imp expectedImp = Imp.builder()
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                .video(Video.builder()
+                        .mimes(Collections.singletonList("someMime"))
+                        .protocols(Arrays.asList(1, 2))
+                        .w(100)
+                        .h(100)
+                        .build())
+                .tagid("123")
+                .secure(0)
+                .bidfloor(new BigDecimal("2"))
+                .bidfloorcur("USD")
+                .build();
+
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .containsOnly(expectedImp);
+    }
+
+    @Test
+    public void shouldThrowExceptionIfVideoDoNotHaveAtLeastOneSizeParameter() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .video(Video.builder().mimes(Collections.singletonList("someMime")).build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .containsOnly(BidderError.badInput("Video: Need at least one size to build request"));
+    }
+
+    @Test
+    public void shouldThrowExceptionIfVideoDoNotHaveAnyMimeParameter() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .video(Video.builder().build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .containsOnly(BidderError.badInput("Video: missing required field mimes"));
+    }
+
+    @Test
+    public void requestSecureShouldBeOneIfPageStartsWithHttps() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .banner(Banner.builder().w(100).h(100).build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .site(Site.builder().page("https://exmaple/").build())
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .extracting(request -> request.getImp().get(0).getSecure())
+                .containsOnly(1);
+    }
+
+    @Test
+    public void requestSecureShouldBeOneIfUrlStartsWithHttps() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .banner(Banner.builder().w(100).h(100).build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .app(App.builder().domain("https://exmaple/").build())
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getSecure)
+                .containsOnly(1);
+    }
+
+    @Test
+    public void requestSecureShouldBe1IfStoreUrlStartsWithHttps() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .banner(Banner.builder().w(100).h(100).build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .app(App.builder().storeurl("https://exmaple/").build())
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getSecure)
+                .containsOnly(1);
+    }
+
+    @Test
+    public void requestSecureShouldBe0IfPageDoNotStartsWithHttps() {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder()
+                        .banner(Banner.builder().w(100).h(100).build())
+                        .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("123", "2"))))
+                        .build()))
+                .tmax(1000L)
+                .site(Site.builder().page("http://exmaple/").build())
+                .build();
+
+        // when
+        final Result>> result = emxDigitalBidder
+                .makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getSecure)
+                .containsOnly(0);
+    }
+
     @Test
     public void makeHttpRequestsShouldModifyBannerFormatAndWidthAndHeightWhenRequestBannerWidthAndHeightIsNull() {
         // given
@@ -255,6 +440,7 @@ public void makeHttpRequestsShouldModifyBannerFormatAndWidthAndHeightWhenRequest
                 .format(singletonList(Format.builder().h(30).w(31).build())).build();
 
         final Imp expectedImp = Imp.builder()
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpEmxDigital.of("1", "asd"))))
                 .banner(expectedBanner)
                 .tagid("1")
                 .secure(0)
@@ -395,7 +581,7 @@ public void makeBidsShouldReturnEmptyListWhenBidResponseSeatBidIsNull()
     }
 
     @Test
-    public void makeBidsShouldAlwaysReturnBannerBidWithChangedBidImpId() throws JsonProcessingException {
+    public void makeBidsShouldReturnBannerBidWithChangedBidImpId() throws JsonProcessingException {
         // given
         final HttpCall httpCall = givenHttpCall(
                 BidRequest.builder()
@@ -413,6 +599,46 @@ public void makeBidsShouldAlwaysReturnBannerBidWithChangedBidImpId() throws Json
                 .containsOnly(BidderBid.of(Bid.builder().id("321").impid("321").build(), banner, "USD"));
     }
 
+    @Test
+    public void makeBidsShouldReturnVideoBidIfAdmContainsVastPrefix() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.id("321").adm("> result = emxDigitalBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().id("321").adm(" httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.id("321").adm("> result = emxDigitalBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().id("321").adm(" bidCustomizer) {
         return BidResponse.builder()
diff --git a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java
index efb7e949868..30eba2a7588 100644
--- a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java
@@ -54,7 +54,7 @@ public void creationShouldFailOnInvalidEndpointUrl() {
     }
 
     @Test
-    public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
+    public void makeHttpRequestsShouldReturnErrorsIfImpExtCouldNotBeParsed() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 impBuilder -> impBuilder
@@ -64,8 +64,15 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
         final Result>> result = gumgumBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(2);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance");
+        assertThat(result.getErrors()).hasSize(2)
+                .anySatisfy(error -> {
+                    assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
+                    assertThat(error.getMessage()).isEqualTo("No valid impressions");
+                })
+                .anySatisfy(error -> {
+                    assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
+                    assertThat(error.getMessage()).startsWith("Cannot deserialize instance");
+                });
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -82,8 +89,8 @@ public void makeHttpRequestsShouldReturnErrorIfNoValidImpressions() {
         final Result>> result = gumgumBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput("No valid impressions"));
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("No valid impressions"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -101,9 +108,9 @@ public void makeHttpRequestsShouldReturnErrorIfVideoFieldsAreNotValid() {
         final Result>> result = gumgumBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(2);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Invalid or missing video field(s)");
-        assertThat(result.getErrors().get(1).getMessage()).startsWith("No valid impressions");
+        assertThat(result.getErrors())
+                .containsExactlyInAnyOrder(BidderError.badInput("Invalid or missing video field(s)"),
+                        BidderError.badInput("No valid impressions"));
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -121,12 +128,37 @@ public void makeHttpRequestsShouldSkipImpressionsWithoutBannerOrVideo() {
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getBanner)
                 .extracting(Banner::getId)
-                .containsOnly("banner_id");
+                .containsExactly("banner_id");
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetEmtyStringIfZoneIsNull() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(bidRequestBuilder ->
+                        bidRequestBuilder.site(Site.builder().build()),
+                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpGumgum.of(null)))));
+        BidRequest.builder()
+                .site(Site.builder().build())
+                .imp(asList(
+                        givenImp(impBuilder -> impBuilder.banner(null).video(null).audio(Audio.builder().build())),
+                        givenImp(identity())))
+                .build();
+
+        // when
+        final Result>> result = gumgumBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
+                .extracting(BidRequest::getSite)
+                .extracting(Site::getId)
+                .containsExactly("");
     }
 
     @Test
@@ -144,20 +176,19 @@ public void makeHttpRequestsShouldNotChangeBannerWidthAndHeightIfPresent() {
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
+        assertThat(result.getValue())
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getBanner)
                 .extracting(Banner::getW, Banner::getH)
-                .containsOnly(tuple(600, 900));
+                .containsExactly(tuple(600, 900));
     }
 
     @Test
     public void makeHttpRequestsShouldSetBannerWidthAndHeightFromfirstFormatIfAbsent() {
         // given
         final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder
-                        .banner(Banner.builder()
+                impBuilder -> impBuilder.banner(Banner.builder()
                                 .format(singletonList(Format.builder().w(300).h(450).build()))
                                 .build()));
 
@@ -166,12 +197,12 @@ public void makeHttpRequestsShouldSetBannerWidthAndHeightFromfirstFormatIfAbsent
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
                 .flatExtracting(BidRequest::getImp)
                 .extracting(Imp::getBanner)
                 .extracting(Banner::getW, Banner::getH)
-                .containsOnly(tuple(300, 450));
+                .containsExactly(tuple(300, 450));
     }
 
     @Test
@@ -185,7 +216,7 @@ public void makeHttpRequestsShouldNotMakeSiteIfAbsent() {
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .extracting(HttpRequest::getPayload)
                 .extracting(BidRequest::getSite)
                 .containsNull();
     }
@@ -211,11 +242,11 @@ public void makeHttpRequestsShouldSetSiteIdFromLastValidImpExtZone() {
 
         // then
         assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+        assertThat(result.getValue())
+                .extracting(HttpRequest::getPayload)
                 .extracting(BidRequest::getSite)
                 .extracting(Site::getId)
-                .containsOnly("zone");
+                .containsExactly("zone");
     }
 
     @Test
@@ -227,9 +258,11 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
         final Result> result = gumgumBidder.makeBids(httpCall, null);
 
         // then
-        assertThat(result.getErrors()).hasSize(1);
-        assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token");
-        assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response);
+        assertThat(result.getErrors()).hasSize(1)
+                .allSatisfy(error -> {
+                    assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response);
+                    assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token");
+                });
         assertThat(result.getValue()).isEmpty();
     }
 
@@ -284,7 +317,7 @@ public void makeBidsShouldReturnVideoBid() throws JsonProcessingException {
                 .build();
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(expectedBid, video, "USD"));
+                .containsExactly(BidderBid.of(expectedBid, video, "USD"));
     }
 
     @Test
@@ -302,7 +335,31 @@ public void makeBidsShouldReturnBannerBid() throws JsonProcessingException {
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+                .containsExactly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldTolerateWithNullSeatOrBidValues() throws JsonProcessingException {
+        // given
+        final BidRequest bidRequest = BidRequest.builder()
+                .imp(singletonList(Imp.builder().banner(Banner.builder().build()).id("123").build()))
+                .build();
+
+        final BidResponse bidResponse = BidResponse.builder()
+                .cur("USD")
+                .seatbid(asList(SeatBid.builder()
+                        .bid(asList(Bid.builder().id("123").build(), null))
+                        .build(), null))
+                .build();
+
+        final HttpCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString(bidResponse));
+
+        // when
+        final Result> result = gumgumBidder.makeBids(httpCall, bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1);
     }
 
     private static BidRequest givenBidRequest(
diff --git a/src/test/java/org/prebid/server/bidder/ix/IxAdapterTest.java b/src/test/java/org/prebid/server/bidder/ix/IxAdapterTest.java
index 4832fe493c2..ad5c3b4175e 100644
--- a/src/test/java/org/prebid/server/bidder/ix/IxAdapterTest.java
+++ b/src/test/java/org/prebid/server/bidder/ix/IxAdapterTest.java
@@ -1,6 +1,5 @@
 package org.prebid.server.bidder.ix;
 
-import com.iab.openrtb.request.App;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.BidRequest.BidRequestBuilder;
@@ -108,18 +107,6 @@ public void makeHttpRequestsShouldReturnRequestsWithExpectedHeaders() {
                         tuple("Accept", "application/json"));
     }
 
-    @Test
-    public void makeHttpRequestsShouldFailIfAppIsPresentInPreBidRequest() {
-        // given
-        preBidRequestContext = givenPreBidRequestContext(identity(), builder -> builder
-                .app(App.builder().build()));
-
-        // when and then
-        assertThatThrownBy(() -> adapter.makeHttpRequests(adapterRequest, preBidRequestContext))
-                .isExactlyInstanceOf(PreBidException.class)
-                .hasMessage("ix doesn't support apps");
-    }
-
     @Test
     public void makeHttpRequestsShouldFailIfParamsMissingInAtLeastOneAdUnitBid() {
         // given
diff --git a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java
index b269524b212..0fc5e634268 100644
--- a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java
@@ -1,7 +1,6 @@
 package org.prebid.server.bidder.ix;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.iab.openrtb.request.App;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.Format;
@@ -34,12 +33,12 @@
 import static java.util.function.Function.identity;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
 
 public class IxBidderTest extends VertxTest {
 
     private static final String ENDPOINT_URL = "http://exchange.org/";
     private static final int REQUEST_LIMIT = 20;
+    private static final String SITE_ID = "site id";
 
     private IxBidder ixBidder;
 
@@ -53,39 +52,6 @@ public void creationShouldFailOnInvalidEndpointUrl() {
         assertThatIllegalArgumentException().isThrownBy(() -> new IxBidder("invalid_url", jacksonMapper));
     }
 
-    @Test
-    public void makeHttpRequestsShouldReturnErrorIfRequestHasApp() {
-        // given
-        final BidRequest bidRequest = BidRequest.builder()
-                .app(App.builder().build())
-                .build();
-
-        // when
-        final Result>> result = ixBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).hasSize(1)
-                .containsOnly(BidderError.badInput("ix doesn't support apps"));
-        assertThat(result.getValue()).isEmpty();
-    }
-
-    @Test
-    public void makeHttpRequestsShouldReturnErrorIfImpHasNoBanner() {
-        // given
-        final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder.banner(null).video(Video.builder().build()));
-
-        // when
-        final Result>> result = ixBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).hasSize(2)
-                .containsOnly(
-                        BidderError.badInput("Invalid MediaType. Ix supports only Banner type. Ignoring ImpID=123"),
-                        BidderError.badInput("No valid impressions in the bid request"));
-        assertThat(result.getValue()).isEmpty();
-    }
-
     @Test
     public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
         // given
@@ -103,30 +69,25 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
     }
 
     @Test
-    public void makeHttpRequestsShouldReturnErrorIfImpExtSiteIdIsNullOrBlank() {
+    public void makeHttpRequestsShouldReturnWhenImpHasNoBanner() {
         // given
-        final BidRequest bidRequest = BidRequest.builder()
-                .imp(asList(
-                        givenImp(impBuilder -> impBuilder.ext(
-                                mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(null))))),
-                        givenImp(impBuilder -> impBuilder.ext(
-                                mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of("")))))))
-                .build();
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.banner(null).video(Video.builder().build()));
 
         // when
         final Result>> result = ixBidder.makeHttpRequests(bidRequest);
 
         // then
-        assertThat(result.getErrors()).hasSize(3)
-                .containsOnly(BidderError.badInput("Missing siteId param"),
-                        BidderError.badInput("No valid impressions in the bid request"));
-        assertThat(result.getValue()).isEmpty();
+        assertThat(result.getErrors()).isEmpty();
     }
 
     @Test
-    public void makeHttpRequestsShouldSetSitePublisherIdFromImpExt() {
+    public void makeHttpRequestsShouldSetSitePublisherIdFromImpExtWhenSitePresent() {
         // given
-        final BidRequest bidRequest = givenBidRequest(identity());
+        final Banner banner = Banner.builder().w(100).h(200).build();
+        final BidRequest bidRequest = givenBidRequest(
+                builder -> builder.site(Site.builder().build()),
+                impBuilder -> impBuilder.banner(banner));
 
         // when
         final Result>> result = ixBidder.makeHttpRequests(bidRequest);
@@ -138,28 +99,7 @@ public void makeHttpRequestsShouldSetSitePublisherIdFromImpExt() {
                 .extracting(BidRequest::getSite)
                 .extracting(Site::getPublisher)
                 .extracting(Publisher::getId)
-                .containsOnly("site id");
-    }
-
-    @Test
-    public void makeHttpRequestsShouldSetImpTagIdFromImpId() {
-        // given
-        final BidRequest bidRequest = givenBidRequest(
-                impBuilder -> impBuilder.banner(Banner.builder()
-                        .id("123")
-                        .format(singletonList(Format.builder().w(300).h(200).build()))
-                        .build()));
-
-        // when
-        final Result>> result = ixBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1)
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
-                .flatExtracting(BidRequest::getImp)
-                .extracting(Imp::getTagid)
-                .containsOnly("123");
+                .containsOnly(SITE_ID);
     }
 
     @Test
@@ -213,6 +153,7 @@ public void makeHttpRequestsShouldCreateOneRequestPerImp() {
         final BidRequest bidRequest = BidRequest.builder()
                 .imp(asList(
                         givenImp(impBuilder -> impBuilder
+                                .id("123")
                                 .banner(Banner.builder()
                                         .format(singletonList(Format.builder().w(300).h(200).build())).build())),
                         givenImp(impBuilder -> impBuilder
@@ -238,7 +179,8 @@ public void makeHttpRequestsShouldCreateOneRequestPerBannerFormat() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 impBuilder -> impBuilder.banner(Banner.builder()
-                        .format(asList(Format.builder().w(300).h(200).build(),
+                        .format(asList(
+                                Format.builder().w(300).h(200).build(),
                                 Format.builder().w(600).h(400).build()))
                         .build()));
 
@@ -249,11 +191,8 @@ public void makeHttpRequestsShouldCreateOneRequestPerBannerFormat() {
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue()).hasSize(2)
                 .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
-                .extracting(BidRequest::getSite)
-                .extracting(Site::getPublisher)
-                .extracting(Publisher::getId)
-                // both from same imp (same imp.ext.siteId)
-                .containsOnly("site id");
+                .flatExtracting(BidRequest::getImp)
+                .hasSize(2);
     }
 
     @Test
@@ -285,8 +224,7 @@ public void makeHttpRequestsShouldLimitImpsAmount() {
             imps.add(givenImp(impBuilder -> impBuilder.banner(Banner.builder()
                     .format(singletonList(Format.builder().w(value).h(value).build())).build())));
         }
-        final BidRequest bidRequest = givenBidRequest(requestBuilder -> requestBuilder.imp(imps),
-                identity());
+        final BidRequest bidRequest = givenBidRequest(requestBuilder -> requestBuilder.imp(imps), identity());
 
         // when
         final Result>> result = ixBidder.makeHttpRequests(bidRequest);
@@ -303,7 +241,8 @@ public void makeHttpRequestsShouldLimitTotalAmountOfRequests() {
         for (int i = 0; i < 21; i++) {
             int value = i;
             imps.add(givenImp(impBuilder -> impBuilder.banner(Banner.builder()
-                    .format(asList(Format.builder().w(value).h(value).build(),
+                    .format(asList(
+                            Format.builder().w(value).h(value).build(),
                             Format.builder().w(value + 1).h(value).build()))
                     .build())));
         }
@@ -322,16 +261,16 @@ public void makeHttpRequestsShouldLimitTotalAmountOfRequests() {
     public void makeHttpRequestsShouldPrioritizeFirstFormatPerImpOverOtherFormats() {
         // given
         final List imps = new ArrayList<>();
-        for (int i = 0; i < 21; i++) {
+        for (int i = 0; i <= REQUEST_LIMIT; i++) {
             int priority = i;
             int other = i + 25;
             imps.add(givenImp(impBuilder -> impBuilder.banner(Banner.builder()
-                    .format(asList(Format.builder().w(priority).h(priority).build(),
+                    .format(asList(
+                            Format.builder().w(priority).h(priority).build(),
                             Format.builder().w(other).h(other).build()))
                     .build())));
         }
-        final BidRequest bidRequest = givenBidRequest(requestBuilder -> requestBuilder.imp(imps),
-                identity());
+        final BidRequest bidRequest = givenBidRequest(requestBuilder -> requestBuilder.imp(imps), identity());
 
         // when
         final Result>> result = ixBidder.makeHttpRequests(bidRequest);
@@ -398,9 +337,10 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso
     @Test
     public void makeBidsShouldReturnBannerBidWithOriginalBidSize() throws JsonProcessingException {
         // given
+        final Video video = Video.builder().build();
         final HttpCall httpCall = givenHttpCall(
                 BidRequest.builder()
-                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .imp(singletonList(Imp.builder().id("123").video(video).build()))
                         .build(),
                 mapper.writeValueAsString(
                         givenBidResponse(bidBuilder -> bidBuilder.impid("123").w(300).h(200))));
@@ -411,7 +351,7 @@ public void makeBidsShouldReturnBannerBidWithOriginalBidSize() throws JsonProces
         // then
         assertThat(result.getErrors()).isEmpty();
         assertThat(result.getValue())
-                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(300).h(200).build(), banner, "EUR"));
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(300).h(200).build(), BidType.video, "EUR"));
     }
 
     @Test
@@ -450,7 +390,7 @@ private static Imp givenImp(Function impCustomiz
         return impCustomizer.apply(Imp.builder()
                 .id("123")
                 .banner(Banner.builder().build())
-                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of("site id")))))
+                .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(SITE_ID, null)))))
                 .build();
     }
 
diff --git a/src/test/java/org/prebid/server/bidder/nobid/NobidBidderTest.java b/src/test/java/org/prebid/server/bidder/nobid/NobidBidderTest.java
new file mode 100644
index 00000000000..f24d9357f7d
--- /dev/null
+++ b/src/test/java/org/prebid/server/bidder/nobid/NobidBidderTest.java
@@ -0,0 +1,196 @@
+package org.prebid.server.bidder.nobid;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.iab.openrtb.request.Banner;
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Imp;
+import com.iab.openrtb.request.Native;
+import com.iab.openrtb.request.Video;
+import com.iab.openrtb.response.Bid;
+import com.iab.openrtb.response.BidResponse;
+import com.iab.openrtb.response.SeatBid;
+import org.junit.Before;
+import org.junit.Test;
+import org.prebid.server.VertxTest;
+import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.bidder.model.BidderError;
+import org.prebid.server.bidder.model.HttpCall;
+import org.prebid.server.bidder.model.HttpRequest;
+import org.prebid.server.bidder.model.HttpResponse;
+import org.prebid.server.bidder.model.Result;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
+
+public class NobidBidderTest extends VertxTest {
+
+    private static final String ENDPOINT_URL = "https://test.com/prebid/bid&key={{AccountID}}";
+
+    private NobidBidder nobidBidder;
+
+    @Before
+    public void setUp() {
+        nobidBidder = new NobidBidder(ENDPOINT_URL, jacksonMapper);
+    }
+
+    @Test
+    public void creationShouldFailOnInvalidEndpointUrl() {
+        assertThatIllegalArgumentException().isThrownBy(() -> new NobidBidder("invalid_url", jacksonMapper));
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
+        // given
+        final HttpCall httpCall = givenHttpCall(null, "invalid");
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1);
+        assertThat(result.getErrors())
+                .allMatch(error -> error.getType() == BidderError.Type.bad_server_response
+                        && error.getMessage().startsWith("Failed to decode: Unrecognized token"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(null));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(BidResponse.builder().build()));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnBannerBidIfBannerIsPresentInRequestImp() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnBidFromEverySeatBid() throws JsonProcessingException {
+        // given
+        final SeatBid firstSeatBId = SeatBid.builder()
+                .bid(singletonList(Bid.builder()
+                        .impid("123")
+                        .build()))
+                .build();
+
+        final SeatBid secondSeatBid = SeatBid.builder()
+                .bid(singletonList(Bid.builder()
+                        .impid("456")
+                        .build()))
+                .build();
+
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(Arrays.asList(Imp.builder().id("123").banner(Banner.builder().build()).build(),
+                                Imp.builder().id("456").video(Video.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(BidResponse.builder()
+                        .cur("USD")
+                        .seatbid(Arrays.asList(firstSeatBId, secondSeatBid))
+                        .build()));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"),
+                        BidderBid.of(Bid.builder().impid("456").build(), video, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfImpWasNotFound() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").xNative(Native.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("125"))));
+
+        // when
+        final Result> result = nobidBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("Failed to find impression 125"));
+    }
+
+    private static BidResponse givenBidResponse(Function bidCustomizer) {
+        return BidResponse.builder()
+                .cur("USD")
+                .seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
+                        .build()))
+                .build();
+    }
+
+    private static HttpCall givenHttpCall(BidRequest bidRequest, String body) {
+        return HttpCall.success(
+                HttpRequest.builder().payload(bidRequest).build(),
+                HttpResponse.of(200, null, body),
+                null);
+    }
+}
diff --git a/src/test/java/org/prebid/server/bidder/pubnative/PubnativeBidderTest.java b/src/test/java/org/prebid/server/bidder/pubnative/PubnativeBidderTest.java
index d24ed956c2d..4bbf7a63d75 100644
--- a/src/test/java/org/prebid/server/bidder/pubnative/PubnativeBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/pubnative/PubnativeBidderTest.java
@@ -311,6 +311,137 @@ public void makeBidsShouldReturnNativeBidIfNativeIsPresent() throws JsonProcessi
                 .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
     }
 
+    @Test
+    public void makeBidsShouldResolveBidSizeFromBannerIfWAndHArePresent() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder().w(100).h(100).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(100).h(100).build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldResolveBidSizeForBannerWhenWAndHNotNullAndFormatHasSingleElementWithSameSize()
+            throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder().w(100).h(100)
+                                        .format(singletonList(Format.builder().w(100).h(100).build())).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(100).h(100).build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldNotResolveBidSizeForBannerWhenWAndHNotNullAndFormatHasSingleElementWithDifferentSize()
+            throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder().w(100).h(100)
+                                        .format(singletonList(Format.builder().w(150).h(150).build())).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldNotResolveBidSizeForBannerWhenWAndHNotNullAndFormatHasMultipleElements()
+            throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder().w(100).h(100)
+                                        .format(singletonList(Format.builder().w(100).h(100).build())).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(100).h(100).build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldResolveBidSizeForBannerWhenWAndHAreNullAndFormatHasSingleElements()
+            throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder()
+                                        .format(singletonList(Format.builder().w(150).h(150).build())).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").w(150).h(150).build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldNotResolveBidSizeForBannerWhenWAndHAreNullAndFormatMultipleElements()
+            throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder()
+                                .banner(Banner.builder()
+                                        .format(asList(Format.builder().w(100).h(100).build(),
+                                                Format.builder().w(150).h(150).build())).build())
+                                .id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = pubnativeBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
     private static BidRequest givenBidRequest(
             Function impModifier,
             Function requestModifier) {
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 595f2742b47..4896ac6f8a5 100644
--- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java
@@ -3,6 +3,7 @@
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.iab.openrtb.request.App;
+import com.iab.openrtb.request.Audio;
 import com.iab.openrtb.request.Banner;
 import com.iab.openrtb.request.BidRequest;
 import com.iab.openrtb.request.BidRequest.BidRequestBuilder;
@@ -13,6 +14,7 @@
 import com.iab.openrtb.request.Imp;
 import com.iab.openrtb.request.Imp.ImpBuilder;
 import com.iab.openrtb.request.Metric;
+import com.iab.openrtb.request.Native;
 import com.iab.openrtb.request.Publisher;
 import com.iab.openrtb.request.Regs;
 import com.iab.openrtb.request.Site;
@@ -38,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;
@@ -56,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;
@@ -65,12 +64,12 @@
 import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
 import org.prebid.server.proto.openrtb.ext.request.ExtSite;
 import org.prebid.server.proto.openrtb.ext.request.ExtUser;
-import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust;
 import org.prebid.server.proto.openrtb.ext.request.ExtUserEid;
 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.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;
@@ -139,6 +138,49 @@ public void makeHttpRequestsShouldFillMethodAndUrlAndExpectedHeaders() {
                         tuple(HttpUtil.USER_AGENT_HEADER.toString(), "prebid-server/1.0"));
     }
 
+    @Test
+    public void makeHttpRequestsShouldFilterImpressionsWithInvalidTypes() {
+        // given
+        final Imp imp1 = givenImp(builder -> builder.video(Video.builder().build()));
+        final Imp imp2 = givenImp(builder -> builder.id("2").xNative(Native.builder().build()));
+        final Imp imp3 = givenImp(builder -> builder.id("3").audio(Audio.builder().build()));
+        final BidRequest bidRequest = BidRequest.builder().imp(asList(imp1, imp2, imp3)).build();
+
+        // when
+        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(2)
+                .containsOnly(
+                        BidderError.of("Impression with id 2 rejected with invalid type `xNative`."
+                                + " Allowed types are banner and video.", BidderError.Type.bad_input),
+                        BidderError.of("Impression with id 3 rejected with invalid type `audio`."
+                                + " Allowed types are banner and video.", BidderError.Type.bad_input));
+        assertThat(result.getValue()).hasSize(1);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldFilterAllImpressionsAndReturnErrorMeessagesWithoutRequests() {
+        // given
+        final Imp imp1 = givenImp(builder -> builder.id("1").xNative(Native.builder().build()));
+        final Imp imp2 = givenImp(builder -> builder.id("2").audio(Audio.builder().build()));
+        final BidRequest bidRequest = BidRequest.builder().imp(asList(imp1, imp2)).build();
+
+        // when
+        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(3)
+                .containsOnly(
+                        BidderError.of("Impression with id 1 rejected with invalid type `xNative`."
+                                + " Allowed types are banner and video.", BidderError.Type.bad_input),
+                        BidderError.of("Impression with id 2 rejected with invalid type `audio`."
+                                + " Allowed types are banner and video.", BidderError.Type.bad_input),
+                        BidderError.of("There are no valid impressions to create bid request to rubicon bidder",
+                                BidderError.Type.bad_input));
+        assertThat(result.getValue()).isEmpty();
+    }
+
     @Test
     public void makeHttpRequestsShouldReplaceDefaultParametersWithExtPrebidBiddersBidder() {
         // given
@@ -376,6 +418,75 @@ public void makeHttpRequestsShouldCreateVideoRequestIfImpHasBannerAndVideoButAll
                                 .maxduration(60).linearity(2).api(singletonList(3)).build()));
     }
 
+    @Test
+    public void shouldSetSizeIdTo201IfPlacementIs1AndSizeIdIsNotPresent() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                builder -> builder.instl(1).video(Video.builder().placement(1).build()),
+                builder -> builder.video(RubiconVideoParams.builder().sizeId(null).build()));
+
+        // when
+        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1).doesNotContainNull()
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getVideo).doesNotContainNull()
+                .extracting(Video::getExt).doesNotContainNull()
+                .extracting(ext -> mapper.treeToValue(ext, RubiconVideoExt.class))
+                .extracting(RubiconVideoExt::getRp)
+                .extracting(RubiconVideoExtRp::getSizeId)
+                .containsOnly(201);
+    }
+
+    @Test
+    public void shouldSetSizeIdTo203IfPlacementIs3AndSizeIdIsNotPresent() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                builder -> builder.instl(1).video(Video.builder().placement(3).build()),
+                builder -> builder.video(RubiconVideoParams.builder().sizeId(null).build()));
+
+        // when
+        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1).doesNotContainNull()
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getVideo).doesNotContainNull()
+                .extracting(Video::getExt).doesNotContainNull()
+                .extracting(ext -> mapper.treeToValue(ext, RubiconVideoExt.class))
+                .extracting(RubiconVideoExt::getRp)
+                .extracting(RubiconVideoExtRp::getSizeId)
+                .containsOnly(203);
+    }
+
+    @Test
+    public void shouldSetSizeIdTo202UsingInstlFlagIfPlacementAndSizeIdAreNotPresent() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                builder -> builder.instl(1).video(Video.builder().placement(null).build()),
+                builder -> builder.video(RubiconVideoParams.builder().sizeId(null).build()));
+
+        // when
+        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1).doesNotContainNull()
+                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
+                .flatExtracting(BidRequest::getImp)
+                .extracting(Imp::getVideo).doesNotContainNull()
+                .extracting(Video::getExt).doesNotContainNull()
+                .extracting(ext -> mapper.treeToValue(ext, RubiconVideoExt.class))
+                .extracting(RubiconVideoExt::getRp)
+                .extracting(RubiconVideoExtRp::getSizeId)
+                .containsOnly(202);
+    }
+
     @Test
     public void makeHttpRequestsShouldFillVideoExt() {
         // given
@@ -575,32 +686,6 @@ public void makeHttpRequestsShouldNotFillUserExtRpWhenVisitorAndInventoryIsEmpty
                 .containsOnly(User.builder().id("id").build());
     }
 
-    @Test
-    public void makeHttpRequestsShouldFillUserExtIfUserAndDigiTrustPresent() {
-        // given
-        final BidRequest bidRequest = givenBidRequest(
-                builder -> builder.user(User.builder()
-                        .ext(ExtUser.builder()
-                                .digitrust(ExtUserDigiTrust.of("id", 123, 0))
-                                .build())
-                        .build()),
-                builder -> builder.video(Video.builder().build()),
-                identity());
-        // when
-        final Result>> result = rubiconBidder.makeHttpRequests(bidRequest);
-
-        // then
-        assertThat(result.getErrors()).isEmpty();
-        assertThat(result.getValue()).hasSize(1).doesNotContainNull()
-                .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class))
-                .extracting(BidRequest::getUser).doesNotContainNull()
-                .containsOnly(User.builder()
-                        .ext(ExtUser.builder()
-                                .digitrust(ExtUserDigiTrust.of("id", 123, 0))
-                                .build())
-                        .build());
-    }
-
     @Test
     public void makeHttpRequestsShouldFillUserIfUserAndConsentArePresent() {
         // given
@@ -786,7 +871,7 @@ public void makeHttpRequestsShouldNormalizeAndCopyUserExtDataFieldsToUserExtRp()
     }
 
     @Test
-    public void makeHttpRequestsShouldNotCreateUserIfVisitorAndDigiTrustAndConsentNotPresent() {
+    public void makeHttpRequestsShouldNotCreateUserIfVisitorAndConsentNotPresent() {
         // given
         final BidRequest bidRequest = givenBidRequest(
                 identity(),
@@ -1956,6 +2041,7 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
                 .imp(asList(
                         givenImp(builder -> builder.video(Video.builder().build())),
                         Imp.builder()
+                                .banner(Banner.builder().build())
                                 .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))
                                 .build()))
                 .build();
@@ -2268,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/bidder/silvermob/SilvermobBidderTest.java b/src/test/java/org/prebid/server/bidder/silvermob/SilvermobBidderTest.java
new file mode 100644
index 00000000000..df4d82cf394
--- /dev/null
+++ b/src/test/java/org/prebid/server/bidder/silvermob/SilvermobBidderTest.java
@@ -0,0 +1,303 @@
+package org.prebid.server.bidder.silvermob;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.iab.openrtb.request.Banner;
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.Device;
+import com.iab.openrtb.request.Imp;
+import com.iab.openrtb.request.Native;
+import com.iab.openrtb.request.Video;
+import com.iab.openrtb.response.Bid;
+import com.iab.openrtb.response.BidResponse;
+import com.iab.openrtb.response.SeatBid;
+import io.vertx.core.MultiMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.prebid.server.VertxTest;
+import org.prebid.server.bidder.model.BidderBid;
+import org.prebid.server.bidder.model.BidderError;
+import org.prebid.server.bidder.model.HttpCall;
+import org.prebid.server.bidder.model.HttpRequest;
+import org.prebid.server.bidder.model.HttpResponse;
+import org.prebid.server.bidder.model.Result;
+import org.prebid.server.proto.openrtb.ext.ExtPrebid;
+import org.prebid.server.proto.openrtb.ext.request.silvermob.ExtImpSilvermob;
+import org.prebid.server.util.HttpUtil;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static java.util.Collections.singletonList;
+import static java.util.function.Function.identity;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.banner;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.video;
+import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative;
+
+public class SilvermobBidderTest extends VertxTest {
+
+    private static final String ENDPOINT_URL = "http://{{Host}}.test/some/path/{{ZoneID}}";
+
+    private SilvermobBidder silvermobBidder;
+
+    @Before
+    public void setUp() {
+        silvermobBidder = new SilvermobBidder(ENDPOINT_URL, jacksonMapper);
+    }
+
+    @Test
+    public void creationShouldFailOnInvalidEndpointUrl() {
+        assertThatIllegalArgumentException().isThrownBy(() -> new SilvermobBidder("invalid_url", jacksonMapper));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))));
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .allMatch(error ->
+                        error.getMessage().startsWith("error unmarshalling imp.ext.bidder: Cannot deserialize instance")
+                                && error.getType() == BidderError.Type.bad_input);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfHostIsEmptyOrNull() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpSilvermob.of("testZoneId", "")))));
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("host is a required silvermob ext.imp param"));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnErrorIfZoneIdIsEmptyOrNull() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                impBuilder -> impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpSilvermob.of(null, "testHost")))));
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors())
+                .containsExactly(BidderError.badInput("zoneId is a required silvermob ext.imp param"));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldCreateSingleRequestForEveryImpression() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(bidRequestBuilder ->
+                bidRequestBuilder.imp(Arrays.asList(givenImp(identity()), givenImp(identity()))), identity());
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getValue()).hasSize(2);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldReturnRequestForEveryValidImpressionAndErrorForNotValidImp() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(bidRequestBuilder ->
+                bidRequestBuilder.imp(Arrays.asList(givenImp(impBuilder ->
+                                impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null,
+                                        ExtImpSilvermob.of(null, "testHost"))))),
+                        givenImp(identity()))), identity());
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getValue()).hasSize(1);
+        assertThat(result.getErrors()).hasSize(1);
+    }
+
+    @Test
+    public void makeHttpRequestsShouldCreateCorrectURL() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.banner(Banner.builder().build()));
+
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(HttpRequest::getUri)
+                .containsOnly("http://testHostId.test/some/path/testZoneId");
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() {
+        // given
+        final HttpCall httpCall = givenHttpCall(null, "invalid");
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .allMatch(error -> error.getMessage().startsWith("Error unmarshalling server "
+                        + "Response: Failed to decode: Unrecognized token")
+                        && error.getType() == BidderError.Type.bad_server_response);
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(null));
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).containsExactly(BidderError.badServerResponse("Response in not present"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
+    @Test
+    public void makeBidsShouldReturnErrorIfBidResponseSeatBidIsNullOrEmpty() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(null,
+                mapper.writeValueAsString(BidResponse.builder().build()));
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).containsExactly(BidderError.badServerResponse("Empty SeatBid array"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnBannerBidIfBannerIsPresentInRequestImp() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").video(Video.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "USD"));
+    }
+
+    @Test
+    public void makeHttpRequestsShouldSetAdditionalHeadersIfDeviceFieldsAreNotEmpty() {
+        // given
+        final BidRequest bidRequest = givenBidRequest(
+                requestBuilder -> requestBuilder
+                        .device(Device.builder().ua("user_agent").ip("test_ip").build()),
+                identity());
+
+        // when
+        final Result>> result = silvermobBidder.makeHttpRequests(bidRequest);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue()).hasSize(1)
+                .extracting(HttpRequest::getHeaders)
+                .flatExtracting(MultiMap::entries)
+                .extracting(Map.Entry::getKey, Map.Entry::getValue)
+                .containsExactlyInAnyOrder(
+                        tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), "application/json;charset=utf-8"),
+                        tuple(HttpUtil.ACCEPT_HEADER.toString(), "application/json"),
+                        tuple(HttpUtil.USER_AGENT_HEADER.toString(), "user_agent"),
+                        tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"),
+                        tuple(HttpUtil.X_FORWARDED_FOR_HEADER.toString(), "test_ip"));
+    }
+
+    @Test
+    public void makeBidsShouldReturnNativeBidIfNativeIsPresentInRequestImp() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(
+                BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").xNative(Native.builder().build()).build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))));
+
+        // when
+        final Result> result = silvermobBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).isEmpty();
+        assertThat(result.getValue())
+                .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), xNative, "USD"));
+    }
+
+    private static BidRequest givenBidRequest(
+            Function bidRequestCustomizer,
+            Function impCustomizer) {
+
+        return bidRequestCustomizer.apply(BidRequest.builder()
+                .imp(singletonList(givenImp(impCustomizer))))
+                .build();
+    }
+
+    private static BidRequest givenBidRequest(Function impCustomizer) {
+        return givenBidRequest(identity(), impCustomizer);
+    }
+
+    private static Imp givenImp(Function impCustomizer) {
+        return impCustomizer.apply(Imp.builder()
+                .id("123")
+                .banner(Banner.builder().id("banner_id").build())
+                .ext(mapper.valueToTree(ExtPrebid.of(null,
+                        ExtImpSilvermob.of("testZoneId", "testHostId")))))
+                .build();
+    }
+
+    private static BidResponse givenBidResponse(Function bidCustomizer) {
+        return BidResponse.builder()
+                .cur("USD")
+                .seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build()))
+                        .build()))
+                .build();
+    }
+
+    private static HttpCall givenHttpCall(BidRequest bidRequest, String body) {
+        return HttpCall.success(
+                HttpRequest.builder().payload(bidRequest).build(),
+                HttpResponse.of(200, null, body),
+                null);
+    }
+}
diff --git a/src/test/java/org/prebid/server/bidder/smaato/SmaatoBidderTest.java b/src/test/java/org/prebid/server/bidder/smaato/SmaatoBidderTest.java
index 28354f611f1..d23ebbcf1ef 100644
--- a/src/test/java/org/prebid/server/bidder/smaato/SmaatoBidderTest.java
+++ b/src/test/java/org/prebid/server/bidder/smaato/SmaatoBidderTest.java
@@ -249,6 +249,21 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces
         assertThat(result.getValue()).isEmpty();
     }
 
+    @Test
+    public void makeBidsShouldReturnErrorIfNoBidAdm() throws JsonProcessingException {
+        // given
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder().build(),
+                mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.id("test").impid("123"))), null);
+
+        // when
+        final Result> result = smaatoBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .containsOnly(BidderError.badInput("Empty ad markup in bid with id: test"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
     @Test
     public void makeBidsShouldReturnErrorIfNotSupportedMarkupType() throws JsonProcessingException {
         // given
@@ -257,7 +272,7 @@ public void makeBidsShouldReturnErrorIfNotSupportedMarkupType() throws JsonProce
                         .imp(singletonList(Imp.builder().id("123").build()))
                         .build(),
                 mapper.writeValueAsString(
-                        givenBidResponse(bidBuilder -> bidBuilder.impid("123"))), headers);
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123").adm("adm"))), headers);
 
         // when
         final Result> result = smaatoBidder.makeBids(httpCall, null);
@@ -287,6 +302,25 @@ public void makeBidsShouldReturnErrorIfMarkupTypeIsBlank() throws JsonProcessing
         assertThat(result.getValue()).isEmpty();
     }
 
+    @Test
+    public void makeBidsShouldReturnErrorIfAdmIsInvalid() throws JsonProcessingException {
+        // given
+        final MultiMap headers = MultiMap.caseInsensitiveMultiMap().set("X-SMT-ADTYPE", "");
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123").adm("{\"image\": invalid"))), headers);
+
+        // when
+        final Result> result = smaatoBidder.makeBids(httpCall, null);
+
+        // then
+        assertThat(result.getErrors()).hasSize(1)
+                .containsOnly(BidderError.badInput("Invalid ad markup {\"image\": invalid"));
+        assertThat(result.getValue()).isEmpty();
+    }
+
     @Test
     public void makeBidsShouldReturnCorrectBidIfAdMarkTypeIsReachmedia() throws JsonProcessingException {
         // given
@@ -353,6 +387,41 @@ public void makeBidsShouldReturnCorrectBidIfAdMarkTypeIsImg() throws JsonProcess
                 .containsOnly(BidderBid.of(expectedBid, banner, "USD"));
     }
 
+    @Test
+    public void makeBidsShouldReturnCorrectBidIfAdMarkTypeIsImgAndParametersAreEmpty() throws JsonProcessingException {
+        // given
+        final MultiMap headers = MultiMap.caseInsensitiveMultiMap().set("X-SMT-ADTYPE", "Img");
+        final HttpCall httpCall = givenHttpCall(BidRequest.builder()
+                        .imp(singletonList(Imp.builder().id("123").build()))
+                        .build(),
+                mapper.writeValueAsString(
+                        givenBidResponse(bidBuilder -> bidBuilder.impid("123").adm("{\"image\":{\"img\":{\"url\":\""
+                                + "//prebid-test.smaatolabs.net/img/320x50.jpg\",\"ctaurl\":\""
+                                + "//prebid-test.smaatolabs.net/track/ctaurl/1\"},\"impressiontrackers\":[\""
+                                + "//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/"
+                                + "imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\""
+                                + "//prebid-test.smaatolabs.net/track/click/2\"]}}"))), headers);
+
+        // when
+        final Result> result = smaatoBidder.makeBids(httpCall, null);
+
+        // then
+        final Bid expectedBid = Bid.builder()
+                .impid("123")
+                .adm("
\"\"\"\"
") + .build(); + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsOnly(BidderBid.of(expectedBid, banner, "USD")); + } + @Test public void makeBidsShouldReturnCorrectBidIfAdMarkTypeIsVideo() throws JsonProcessingException { // given diff --git a/src/test/java/org/prebid/server/bidder/smartadserver/SmartadserverBidderTest.java b/src/test/java/org/prebid/server/bidder/smartadserver/SmartadserverBidderTest.java index 3e44d89262e..fe54c02a0fc 100644 --- a/src/test/java/org/prebid/server/bidder/smartadserver/SmartadserverBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/smartadserver/SmartadserverBidderTest.java @@ -20,6 +20,7 @@ import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.smartadserver.ExtImpSmartadserver; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -31,7 +32,7 @@ public class SmartadserverBidderTest extends VertxTest { - private static final String ENDPOINT_URL = "https://test.endpoint.com/"; + private static final String ENDPOINT_URL = "https://test.endpoint.com/path?testParam=testVal"; private SmartadserverBidder smartadserverBidder; @@ -49,25 +50,25 @@ public void creationShouldFailOnInvalidEndpointUrl() { public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // given final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) - .build())) + .imp(singletonList( + Imp.builder() + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) + .build())) .build(); + // when final Result>> result = smartadserverBidder.makeHttpRequests(bidRequest); // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance"); + assertThat(result.getErrors()) + .containsExactly(BidderError.badInput("Error parsing smartadserverExt parameters")); } @Test public void makeHttpRequestsShouldCreateCorrectURL() { // given final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpSmartadserver.of(1, 2, 3, 4)))) - .build())) + .imp(singletonList(givenImp(Function.identity()))) .build(); // when @@ -76,7 +77,54 @@ public void makeHttpRequestsShouldCreateCorrectURL() { // then assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).hasSize(1); - assertThat(result.getValue().get(0).getUri()).isEqualTo("https://test.endpoint.com/?callerId=5"); + assertThat(result.getValue().get(0).getUri()) + .isEqualTo("https://test.endpoint.com/path/api/bid?testParam=testVal&callerId=5"); + } + + @Test + public void makeHttpRequestsShouldCreateRequestForEveryValidImp() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(Arrays.asList(givenImp(Function.identity()), + givenImp(impBuilder -> impBuilder.id("456")) + )) + .build(); + + // when + final Result>> result = smartadserverBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .flatExtracting(Imp::getId) + .containsExactly("123", "456"); + } + + @Test + public void makeHttpRequestsShouldCreateRequestForValidImpAndSaveErrorForInvalid() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(Arrays.asList(givenImp(impBuilder -> impBuilder.id("456")), + Imp.builder() + .id("invalidImp") + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))) + .build() + )) + .build(); + + // when + final Result>> result = smartadserverBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badInput("Error parsing smartadserverExt parameters")); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .flatExtracting(Imp::getId) + .containsExactly("456"); } @Test @@ -88,9 +136,9 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { final Result> result = smartadserverBidder.makeBids(httpCall, null); // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token"); - assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(result.getErrors()) + .allMatch(error -> error.getMessage().startsWith("Failed to decode: Unrecognized token") + && error.getType() == BidderError.Type.bad_server_response); assertThat(result.getValue()).isEmpty(); } @@ -141,6 +189,25 @@ public void makeBidsShouldReturnBannerBidIfBannerIsPresent() throws JsonProcessi .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "EUR")); } + @Test + public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException { + // given + final HttpCall httpCall = givenHttpCall( + BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build())) + .build(), + mapper.writeValueAsString( + givenBidResponse(Function.identity()))); + + // when + final Result> result = smartadserverBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsOnly(BidderBid.of(Bid.builder().build(), banner, "EUR")); + } + @Test public void makeBidsShouldReturnVideoBidIfVideoIsPresent() throws JsonProcessingException { // given @@ -160,6 +227,15 @@ public void makeBidsShouldReturnVideoBidIfVideoIsPresent() throws JsonProcessing .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), video, "EUR")); } + private static Imp givenImp(Function impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("123")) + .banner(Banner.builder().build()) + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpSmartadserver.of(1, 2, 3, 4)))) + .build(); + } + private static BidResponse givenBidResponse(Function bidCustomizer) { return BidResponse.builder() .cur("EUR") diff --git a/src/test/java/org/prebid/server/bidder/smartyads/SmartyAdsBidderTest.java b/src/test/java/org/prebid/server/bidder/smartyads/SmartyAdsBidderTest.java index 3d03d777b50..6dcffbdab37 100644 --- a/src/test/java/org/prebid/server/bidder/smartyads/SmartyAdsBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/smartyads/SmartyAdsBidderTest.java @@ -3,12 +3,14 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Imp; import com.iab.openrtb.request.Native; import com.iab.openrtb.request.Video; import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; +import io.vertx.core.MultiMap; import org.junit.Before; import org.junit.Test; import org.prebid.server.VertxTest; @@ -20,16 +22,19 @@ import org.prebid.server.bidder.model.Result; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.smartyads.ExtImpSmartyAds; +import org.prebid.server.util.HttpUtil; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Function; import static java.util.Collections.singletonList; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.tuple; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; import static org.prebid.server.proto.openrtb.ext.response.BidType.video; import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative; @@ -65,6 +70,49 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { assertThat(result.getErrors().get(0).getMessage()).startsWith("ext.bidder not provided"); } + @Test + public void makeHttpRequestsShouldReturnErrorIfExtBidderAccountIdParamIsMissed() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpSmartyAds.of("", "testSourceId", "testHost"))))); + + // when + final Result>> result = smartyAdsBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badInput("accountId is a required ext.bidder param")); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfExtBidderSourceIdParamIsMissed() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpSmartyAds.of("testAccountId", "", "testHost"))))); + + // when + final Result>> result = smartyAdsBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).containsExactly(BidderError.badInput("sourceId is a required ext.bidder param")); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfExtBidderHostParamIsMissed() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpSmartyAds.of("testAccountId", "testSourceId", ""))))); + + // when + final Result>> result = smartyAdsBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).containsExactly(BidderError.badInput("host is a required ext.bidder param")); + } + @Test public void makeHttpRequestsShouldCreateCorrectURL() { // given @@ -112,9 +160,7 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { final Result> result = smartyAdsBidder.makeBids(httpCall, null); // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token"); - assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(result.getErrors()).containsExactly(BidderError.badServerResponse("Bad Server Response")); assertThat(result.getValue()).isEmpty(); } @@ -128,7 +174,7 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces final Result> result = smartyAdsBidder.makeBids(httpCall, null); // then - assertThat(result.getErrors()).isEmpty(); + assertThat(result.getErrors()).containsExactly(BidderError.badServerResponse("Bad Server Response")); assertThat(result.getValue()).isEmpty(); } @@ -142,7 +188,7 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso final Result> result = smartyAdsBidder.makeBids(httpCall, null); // then - assertThat(result.getErrors()).isEmpty(); + assertThat(result.getErrors()).containsExactly(BidderError.badServerResponse("Empty SeatBid array")); assertThat(result.getValue()).isEmpty(); } @@ -165,6 +211,27 @@ public void makeBidsShouldReturnBannerBidIfBannerIsPresentInRequestImp() throws .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD")); } + @Test + public void makeBidsShouldReturnEmptyValueIfBidsAreNotPresent() throws JsonProcessingException { + // given + final HttpCall httpCall = givenHttpCall( + BidRequest.builder() + .imp(singletonList(Imp.builder().id("123").banner(Banner.builder().build()).build())) + .build(), + mapper.writeValueAsString(BidResponse.builder() + .seatbid(singletonList(SeatBid.builder() + .bid(null) + .build())) + .build())); + + // when + final Result> result = smartyAdsBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + @Test public void makeBidsShouldWorkWithOnlyFirstSeatBid() throws JsonProcessingException { // given @@ -196,6 +263,38 @@ public void makeBidsShouldWorkWithOnlyFirstSeatBid() throws JsonProcessingExcept .containsOnly(BidderBid.of(Bid.builder().impid("456").build(), banner, "USD")); } + @Test + public void makeHttpRequestsShouldSetAdditionalHeadersIfDeviceFieldsAreNotEmpty() { + // given + final BidRequest bidRequest = givenBidRequest( + requestBuilder -> requestBuilder + .device(Device.builder() + .ua("user_agent") + .ip("test_ip") + .dnt(23) + .language("testLang") + .build()), + identity()); + + // when + final Result>> result = smartyAdsBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getHeaders) + .flatExtracting(MultiMap::entries) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsExactlyInAnyOrder( + tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), "application/json;charset=utf-8"), + tuple(HttpUtil.ACCEPT_HEADER.toString(), "application/json"), + tuple(HttpUtil.ACCEPT_LANGUAGE_HEADER.toString(), "testLang"), + tuple(HttpUtil.DNT_HEADER.toString(), "23"), + tuple(HttpUtil.USER_AGENT_HEADER.toString(), "user_agent"), + tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"), + tuple(HttpUtil.X_FORWARDED_FOR_HEADER.toString(), "test_ip")); + } + @Test public void makeBidsShouldReturnVideoBidIfVideoIsPresentInRequestImp() throws JsonProcessingException { // given diff --git a/src/test/java/org/prebid/server/bidder/triplelift/TripleliftBidderTest.java b/src/test/java/org/prebid/server/bidder/triplelift/TripleliftBidderTest.java index f797cfcae9e..9acc63382f8 100644 --- a/src/test/java/org/prebid/server/bidder/triplelift/TripleliftBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/triplelift/TripleliftBidderTest.java @@ -115,7 +115,7 @@ public void makeHttpRequestsShouldModifyTagIdFromImpExt() { @Test public void makeHttpRequestsShouldModifyBidFloorFromImpExtWhenFloorIsPresent() { // given - final BigDecimal floor = new BigDecimal(12f); + final BigDecimal floor = BigDecimal.valueOf(12.32); final BidRequest bidRequest = BidRequest.builder() .imp(singletonList(Imp.builder() .bidfloor(new BigDecimal(1)) @@ -155,8 +155,7 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { @Test public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException { // given - final HttpCall httpCall = givenHttpCall(null, - mapper.writeValueAsString(null)); + final HttpCall httpCall = givenHttpCall(null, mapper.writeValueAsString(null)); // when final Result> result = tripleliftBidder.makeBids(httpCall, null); @@ -169,7 +168,8 @@ public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProces @Test public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException { // given - final HttpCall httpCall = givenHttpCall(null, + final HttpCall httpCall = givenHttpCall( + null, mapper.writeValueAsString(BidResponse.builder().build())); // when @@ -214,6 +214,23 @@ public void makeBidsShouldReturnTypeBannerWhenTripleliftResponseExtIsEmpty() thr .containsOnly(BidderBid.of(Bid.builder().ext(ext).build(), banner, "USD")); } + @Test + public void makeBidsShouldReturnTypeBannerWhenTripleliftInnerExtIsNull() throws JsonProcessingException { + // given + final ObjectNode ext = mapper.valueToTree(TripleliftResponseExt.of(TripleliftInnerExt.of(null))); + final HttpCall httpCall = givenHttpCall( + null, + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.ext(ext)))); + + // when + final Result> result = tripleliftBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsOnly(BidderBid.of(Bid.builder().ext(ext).build(), banner, "USD")); + } + @Test public void makeBidsShouldReturnTypeBannerWhenTripleliftInnerExtIsNotEleven() throws JsonProcessingException { // given diff --git a/src/test/java/org/prebid/server/bidder/ttx/TtxBidderTest.java b/src/test/java/org/prebid/server/bidder/ttx/TtxBidderTest.java index a2a414191bf..feceb706a09 100644 --- a/src/test/java/org/prebid/server/bidder/ttx/TtxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/ttx/TtxBidderTest.java @@ -4,7 +4,7 @@ import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.Video; import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; @@ -19,6 +19,8 @@ import org.prebid.server.bidder.model.Result; import org.prebid.server.bidder.ttx.proto.TtxImpExt; import org.prebid.server.bidder.ttx.proto.TtxImpExtTtx; +import org.prebid.server.bidder.ttx.response.TtxBidExt; +import org.prebid.server.bidder.ttx.response.TtxBidExtTtx; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.ttx.ExtImpTtx; @@ -30,7 +32,9 @@ import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.tuple; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; +import static org.prebid.server.proto.openrtb.ext.response.BidType.video; public class TtxBidderTest extends VertxTest { @@ -49,7 +53,7 @@ public void creationShouldFailOnInvalidEndpointUrl() { } @Test - public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { + public void makeHttpRequestsShouldAppendErrorIfImpExtCouldNotBeParsed() { // given final BidRequest bidRequest = givenBidRequest( impBuilder -> impBuilder @@ -60,14 +64,18 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // then assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance"); - assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()) + .allSatisfy(error -> { + assertThat(error.getMessage()).startsWith("Cannot deserialize instance of"); + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + }); } @Test - public void makeHttpRequestsShouldSetSiteIdFromImpExt() { + public void makeHttpRequestsShouldNotUpdateSiteIfSiteNotPresent() { // given - final BidRequest bidRequest = givenBidRequest(identity()); + final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> + bidRequestBuilder.site(null), identity()); // when final Result>> result = ttxBidder.makeHttpRequests(bidRequest); @@ -77,20 +85,13 @@ public void makeHttpRequestsShouldSetSiteIdFromImpExt() { assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) .extracting(BidRequest::getSite) - .extracting(Site::getId) - .containsOnly("siteId"); + .containsNull(); } @Test - public void makeHttpRequestsShouldGetDetailsOnlyFromFirstImpExt() { + public void makeHttpRequestsShouldNotCreateNewSiteIfSiteNotPresentInBidRequest() { // given - final BidRequest bidRequest = BidRequest.builder() - .imp(asList( - givenImp(identity()), - givenImp(impBuilder -> impBuilder - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", "2", "3"))))) - )) - .build(); + final BidRequest bidRequest = givenBidRequest(identity()); // when final Result>> result = ttxBidder.makeHttpRequests(bidRequest); @@ -99,9 +100,8 @@ public void makeHttpRequestsShouldGetDetailsOnlyFromFirstImpExt() { assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) - .extracting(BidRequest::getSite) - .extracting(Site::getId) - .containsOnly("siteId"); + .flatExtracting(BidRequest::getSite) + .containsNull(); } @Test @@ -109,10 +109,7 @@ public void makeHttpRequestsShouldChangeOnlyFirstImpExt() { // given final BidRequest bidRequest = BidRequest.builder() .imp(asList( - givenImp(identity()), - givenImp(impBuilder -> impBuilder - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "3"))))) - )) + givenImp(identity()))) .build(); // when @@ -125,8 +122,111 @@ public void makeHttpRequestsShouldChangeOnlyFirstImpExt() { .flatExtracting(BidRequest::getImp) .extracting(Imp::getExt) .containsExactly( - mapper.valueToTree(TtxImpExt.of(TtxImpExtTtx.of("productId", "zoneId"))), - mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "3")))); + mapper.valueToTree(TtxImpExt.of(TtxImpExtTtx.of("productId", "zoneId")))); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfVideoParamsNotPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList( + givenImp(impBuilder -> impBuilder + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "3"))))))) + .build(); + + // when + final Result>> result = ttxBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badInput("One or more invalid or missing video field(s) w, h, " + + "protocols, mimes, playbackmethod")); + } + + @Test + public void makeHttpRequestsShouldUpdateNotPresentPlacement() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList( + givenImp(impBuilder -> impBuilder + .video(validVideo()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "3"))))))) + .build(); + + // when + final Result>> result = ttxBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getVideo) + .extracting(Video::getPlacement) + .containsExactly(2); + } + + @Test + public void makeHttpRequestsShouldNotUpdatePlacementWhenProductIdIsNotInstreamAndPlacementIsNotZero() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList( + givenImp(impBuilder -> impBuilder + .video(validVideo().toBuilder().placement(23).build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "3"))))))) + .build(); + + // when + final Result>> result = ttxBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getVideo) + .extracting(Video::getPlacement) + .containsExactly(23); + } + + @Test + public void makeHttpRequestsShouldUpdatePlacementAndStartDelayIfProdIsInstream() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList( + givenImp(impBuilder -> impBuilder + .video(validVideo()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTtx.of("11", null, "instream"))))))) + .build(); + + // when + final Result>> result = ttxBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getVideo) + .extracting(Video::getPlacement, Video::getStartdelay) + .containsExactly(tuple(1, 0)); + } + + @Test + public void makeBidsShouldReturnErrorIfNoBannerOrVideoPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(givenImp(impBuilder -> impBuilder.banner(null)))) + .build(); + + // when + final Result>> result = ttxBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badInput("Imp ID 123 must have at least one of [Banner, Video] defined")); + assertThat(result.getValue()).isEmpty(); } @Test @@ -138,9 +238,11 @@ public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { final Result> result = ttxBidder.makeBids(httpCall, null); // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Failed to decode: Unrecognized token"); - assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(result.getErrors()) + .allSatisfy(error -> { + assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token"); + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response); + }); assertThat(result.getValue()).isEmpty(); } @@ -173,7 +275,7 @@ public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws Jso } @Test - public void makeBidsShouldReturnBannerBid() throws JsonProcessingException { + public void makeBidsShouldReturnBannerBidByDefault() throws JsonProcessingException { // given final HttpCall httpCall = givenHttpCall( BidRequest.builder() @@ -191,6 +293,64 @@ public void makeBidsShouldReturnBannerBid() throws JsonProcessingException { .containsOnly(BidderBid.of(Bid.builder().impid("123").build(), banner, "USD")); } + @Test + public void makeBidsShouldReturnVideoBidIfVideoInBidExt() throws JsonProcessingException { + // given + final TtxBidExt ttxBidExt = TtxBidExt.of(TtxBidExtTtx.of("video")); + final HttpCall httpCall = givenHttpCall( + BidRequest.builder() + .imp(singletonList(Imp.builder().build())) + .build(), + mapper.writeValueAsString( + givenBidResponse(bidBuilder -> bidBuilder + .ext(mapper.valueToTree(ttxBidExt))))); + + // when + final Result> result = ttxBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + final Bid expectedBid = Bid.builder() + .ext(mapper.valueToTree(ttxBidExt)) + .build(); + assertThat(result.getValue()) + .containsOnly(BidderBid.of(expectedBid, video, "USD")); + } + + @Test + public void makeBidsShouldReturnBannerBidIfExtNotContainVideoString() throws JsonProcessingException { + // given + final TtxBidExt ttxBidExt = TtxBidExt.of(TtxBidExtTtx.of("notVideo")); + final HttpCall httpCall = givenHttpCall( + BidRequest.builder() + .imp(singletonList(Imp.builder().build())) + .build(), + mapper.writeValueAsString( + givenBidResponse(bidBuilder -> bidBuilder + .ext(mapper.valueToTree(ttxBidExt))))); + + // when + final Result> result = ttxBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + final Bid expectedBid = Bid.builder() + .ext(mapper.valueToTree(ttxBidExt)) + .build(); + assertThat(result.getValue()) + .containsOnly(BidderBid.of(expectedBid, banner, "USD")); + } + + private static Video validVideo() { + return Video.builder() + .w(23) + .h(23) + .mimes(singletonList("mime")) + .protocols(singletonList(23)) + .playbackmethod(singletonList(27)) + .build(); + } + private static BidRequest givenBidRequest( Function bidRequestCustomizer, Function impCustomizer) { diff --git a/src/test/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidderTest.java b/src/test/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidderTest.java index fbab520ee9e..0342a6f11ef 100644 --- a/src/test/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/verizonmedia/VerizonmediaBidderTest.java @@ -1,6 +1,7 @@ package org.prebid.server.bidder.verizonmedia; import com.fasterxml.jackson.core.JsonProcessingException; +import com.iab.openrtb.request.App; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -127,7 +128,24 @@ public void makeHttpRequestsShouldCreateARequestForEachImpAndSkipImpsWithErrors( } @Test - public void makeHttpRequestsShouldAlwaysSetSiteIdAndImpTagIdFromImpExt() { + public void makeHttpRequestsShouldAlwaysSetImpTagIdFromImpExt() { + // given + final BidRequest bidRequest = givenBidRequest(identity(), identity()); + + // when + final Result>> result = verizonmediaBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .flatExtracting(BidRequest::getImp).hasSize(1) + .extracting(Imp::getTagid) + .containsExactly("pos"); + } + + @Test + public void makeHttpRequestsShouldSetSiteIdIfSiteIsPresentInTheRequest() { // given final BidRequest bidRequest = givenBidRequest(identity(), identity()); @@ -140,12 +158,25 @@ public void makeHttpRequestsShouldAlwaysSetSiteIdAndImpTagIdFromImpExt() { .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) .extracting(BidRequest::getSite) .extracting(Site::getId) - .containsOnly("dcn"); - assertThat(result.getValue()) + .containsExactly("dcn"); + } + + @Test + public void makeHttpRequestsShouldSetAppIdIfAppIsPresentInTheRequest() { + // given + final BidRequest bidRequest = givenBidRequest(identity(), + bidRequestBuilder -> bidRequestBuilder.site(null).app(App.builder().build())); + + // when + final Result>> result = verizonmediaBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) - .flatExtracting(BidRequest::getImp).hasSize(1) - .extracting(Imp::getTagid) - .containsOnly("pos"); + .extracting(BidRequest::getApp) + .extracting(App::getId) + .containsExactly("dcn"); } @Test diff --git a/src/test/java/org/prebid/server/bidder/vrtcal/VrtcalBidderTest.java b/src/test/java/org/prebid/server/bidder/vrtcal/VrtcalBidderTest.java index 2a0f8e2735e..1e87c87458c 100644 --- a/src/test/java/org/prebid/server/bidder/vrtcal/VrtcalBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/vrtcal/VrtcalBidderTest.java @@ -6,6 +6,7 @@ import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; +import io.netty.handler.codec.http.HttpHeaderValues; import org.junit.Before; import org.junit.Test; import org.prebid.server.VertxTest; @@ -17,14 +18,17 @@ import org.prebid.server.bidder.model.Result; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.vrtcal.ExtImpVrtcal; +import org.prebid.server.util.HttpUtil; import java.util.List; +import java.util.Map; import java.util.function.Function; import static java.util.Collections.singletonList; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.tuple; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; public class VrtcalBidderTest extends VertxTest { @@ -46,10 +50,8 @@ public void creationShouldFailOnInvalidEndpointUrl() { @Test public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // given - final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))).build())) - .build(); + final BidRequest bidRequest = givenBidRequest(impBuilder -> + impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); // when final Result>> result = vrtcalBidder.makeHttpRequests(bidRequest); @@ -63,13 +65,8 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { @Test public void makeHttpRequestsShouldNotModifyIncomingRequest() { // given - final BidRequest bidRequest = BidRequest.builder() - .imp(singletonList(Imp.builder() - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpVrtcal.of("JustAnUnusedVrtcalParam")))) - .build())) - .id("request_id") - .build(); + final BidRequest bidRequest = givenBidRequest(impBuilder -> + impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpVrtcal.of("JustAnUnusedVrtcalParam"))))); // when final Result>> result = vrtcalBidder.makeHttpRequests(bidRequest); @@ -81,6 +78,23 @@ public void makeHttpRequestsShouldNotModifyIncomingRequest() { .containsOnly(bidRequest); } + @Test + public void makeHttpRequestShouldReturnCorrectHeaders() { + // given + final BidRequest bidRequest = givenBidRequest(Function.identity()); + + // when + final Result>> result = vrtcalBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(0); + assertThat(result.getValue()).flatExtracting(httpRequest -> httpRequest.getHeaders().entries()) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsOnly( + tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE), + tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString())); + } + @Test public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { // given @@ -140,6 +154,25 @@ public void makeBidsShouldAlwaysReturnBannerBid() throws JsonProcessingException .containsOnly(BidderBid.of(Bid.builder().build(), banner, "USD")); } + private static BidRequest givenBidRequest( + Function bidRequestCustomizer, + Function impCustomizer) { + + return bidRequestCustomizer.apply(BidRequest.builder() + .imp(singletonList(givenImp(impCustomizer)))) + .build(); + } + + private static BidRequest givenBidRequest(Function impCustomizer) { + return givenBidRequest(identity(), impCustomizer); + } + + private static Imp givenImp(Function impCustomizer) { + return impCustomizer.apply(Imp.builder() + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpVrtcal.of("JustAnUnusedVrtcalParam"))))) + .build(); + } + private static BidResponse givenBidResponse(Function bidCustomizer) { return BidResponse.builder() .cur("USD") diff --git a/src/test/java/org/prebid/server/bidder/yieldmo/YieldmoBidderTest.java b/src/test/java/org/prebid/server/bidder/yieldmo/YieldmoBidderTest.java index bc77c289379..48695b219dc 100644 --- a/src/test/java/org/prebid/server/bidder/yieldmo/YieldmoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/yieldmo/YieldmoBidderTest.java @@ -8,6 +8,7 @@ import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; +import io.netty.handler.codec.http.HttpHeaderValues; import org.junit.Before; import org.junit.Test; import org.prebid.server.VertxTest; @@ -17,23 +18,27 @@ import org.prebid.server.bidder.model.HttpRequest; import org.prebid.server.bidder.model.HttpResponse; import org.prebid.server.bidder.model.Result; +import org.prebid.server.bidder.yieldmo.proto.YieldmoImpExt; import org.prebid.server.proto.openrtb.ext.ExtPrebid; import org.prebid.server.proto.openrtb.ext.request.yieldmo.ExtImpYieldmo; +import org.prebid.server.util.HttpUtil; import java.util.List; +import java.util.Map; import java.util.function.Function; import static java.util.Collections.singletonList; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.tuple; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; import static org.prebid.server.proto.openrtb.ext.response.BidType.video; public class YieldmoBidderTest extends VertxTest { private static final String ENDPOINT_URL = "https://test.endpoint.com"; - + private static final String PLACEMENT_VALUE = "placementId"; private YieldmoBidder yieldmoBidder; @Before @@ -49,8 +54,8 @@ public void creationShouldFailOnInvalidEndpointUrl() { @Test public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { // given - final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder - .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); + final BidRequest bidRequest = givenBidRequest(impBuilder -> + impBuilder.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); // when final Result>> result = yieldmoBidder.makeHttpRequests(bidRequest); @@ -61,6 +66,42 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { assertThat(result.getValue()).isEmpty(); } + @Test + public void makeHttpRequestsShouldReturnExtPlacementFromYieldmoPlacement() { + // given + final BidRequest bidRequest = givenBidRequest(Function.identity()); + + // when + final Result>> result = yieldmoBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(0); + + final YieldmoImpExt expectedExt = YieldmoImpExt.of(PLACEMENT_VALUE); + assertThat(result.getValue()).hasSize(1) + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getExt) + .containsOnly(mapper.valueToTree(expectedExt)); + } + + @Test + public void makeHttpRequestShouldReturnCorrectHeaders() { + // given + final BidRequest bidRequest = givenBidRequest(Function.identity()); + + // when + final Result>> result = yieldmoBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(0); + assertThat(result.getValue()).flatExtracting(httpRequest -> httpRequest.getHeaders().entries()) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsOnly( + tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE), + tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString())); + } + @Test public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { // given @@ -178,7 +219,7 @@ private static Imp givenImp(Function impCustomiz return impCustomizer.apply(Imp.builder() .id("123") .banner(Banner.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpYieldmo.of("placementId"))))) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpYieldmo.of(PLACEMENT_VALUE))))) .build(); } diff --git a/src/test/java/org/prebid/server/cache/CacheServiceTest.java b/src/test/java/org/prebid/server/cache/CacheServiceTest.java index e898666e5d3..ba7d8f42c27 100644 --- a/src/test/java/org/prebid/server/cache/CacheServiceTest.java +++ b/src/test/java/org/prebid/server/cache/CacheServiceTest.java @@ -15,6 +15,7 @@ import org.mockito.junit.MockitoRule; import org.prebid.server.VertxTest; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.GeneratedBidIds; import org.prebid.server.cache.model.CacheContext; import org.prebid.server.cache.model.CacheHttpRequest; import org.prebid.server.cache.model.CacheInfo; @@ -47,6 +48,7 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeoutException; import java.util.function.UnaryOperator; @@ -55,7 +57,6 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; import static java.util.function.UnaryOperator.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -83,6 +84,10 @@ public class CacheServiceTest extends VertxTest { private EventsService eventsService; @Mock private Metrics metrics; + @Mock + private GeneratedBidIds allBidIds; + @Mock + private GeneratedBidIds videoCachedBidIds; private Clock clock; @@ -367,7 +372,7 @@ public void cacheBidsOpenrtbShouldPerformHttpRequestWithExpectedTimeout() { givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -383,7 +388,7 @@ public void cacheBidsOpenrtbShouldTolerateGlobalTimeoutAlreadyExpired() { givenAuctionContext().toBuilder().timeout(expiredTimeout).build(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -395,9 +400,12 @@ public void cacheBidsOpenrtbShouldTolerateGlobalTimeoutAlreadyExpired() { } @Test - public void cacheBidsOpenrtbShouldStoreWinUrl() { + public void cacheBidsOpenrtbShouldStoreWinUrlWithGeneratedBidId() { // given final com.iab.openrtb.response.Bid bid = givenBidOpenrtb(builder -> builder.id("bidId1").impid("impId1")); + final String generatedBidId = "GeneratedBidId"; + given(allBidIds.getGeneratedId(any(), any(), any())).willReturn(generatedBidId); + given(allBidIds.getBidderForBid(any(), any())).willReturn(Optional.of("bidder")); // when cacheService.cacheBidsOpenrtb( @@ -406,12 +414,12 @@ public void cacheBidsOpenrtbShouldStoreWinUrl() { .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), EventsContext.builder().enabledForAccount(true).enabledForRequest(true).build()); // then - verify(eventsService).winUrl(eq("bidId1"), eq("bidder"), eq("accountId"), isNull(), isNull()); + verify(eventsService).winUrl(eq(generatedBidId), eq("bidder"), eq("accountId"), isNull(), isNull()); } @Test @@ -428,7 +436,7 @@ public void cacheBidsOpenrtbShouldTolerateReadingHttpResponseFails() throws Json .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -458,7 +466,7 @@ public void cacheBidsOpenrtbShouldTolerateResponseCodeIsNot200() throws JsonProc .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -477,7 +485,6 @@ public void cacheBidsOpenrtbShouldTolerateResponseCodeIsNot200() throws JsonProc public void cacheBidsOpenrtbShouldTolerateResponseBodyCouldNotBeParsed() throws JsonProcessingException { // given givenHttpClientReturnsResponse(200, "response"); - final com.iab.openrtb.response.Bid bid = givenBidOpenrtb(builder -> builder.id("bidId1").impid("impId1")); // when @@ -487,7 +494,7 @@ public void cacheBidsOpenrtbShouldTolerateResponseBodyCouldNotBeParsed() throws .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -517,7 +524,7 @@ public void cacheBidsOpenrtbShouldTolerateCacheEntriesNumberDoesNotMatchBidsNumb .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -545,7 +552,7 @@ public void cacheBidsOpenrtbShouldReturnExpectedDebugInfo() throws JsonProcessin .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -571,7 +578,7 @@ public void cacheBidsOpenrtbShouldReturnExpectedCacheBids() { .imp(singletonList(givenImp(builder -> builder.id("impId1"))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder", singletonList("bidId1"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -599,8 +606,8 @@ public void cacheBidsOpenrtbShouldPerformHttpRequestWithExpectedBody() throws IO CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder2", singletonList("bid2"))) - .bidderToBidIds(singletonMap("bidder1", asList("bid1", "bid2"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), EventsContext.builder().auctionTimestamp(1000L).build()); @@ -625,7 +632,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromBi .imp(singletonList(givenImp(buider -> buider.id("impId1").exp(20))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .cacheBidsTtl(30) .build(), eventsContext); @@ -651,7 +658,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromIm .imp(singletonList(givenImp(buider -> buider.exp(10))))), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .cacheBidsTtl(20) .build(), eventsContext); @@ -676,7 +683,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromRe givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .cacheBidsTtl(10) .build(), eventsContext); @@ -715,7 +722,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromAc identity()), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -733,7 +740,6 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromAc @Test public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromMediaTypeTtl() throws IOException { - // given cacheService = new CacheService( CacheTtl.of(10, null), httpClient, @@ -750,7 +756,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromMe givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -768,7 +774,6 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithExpectedTtlAndSetTtlFromMe @Test public void cacheBidsOpenrtbShouldSendCacheRequestWithTtlFromMediaTypeWhenAccountIsEmpty() throws IOException { - // given cacheService = new CacheService( CacheTtl.of(10, null), httpClient, @@ -785,7 +790,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithTtlFromMediaTypeWhenAccoun givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -809,7 +814,7 @@ public void cacheBidsOpenrtbShouldSendCacheRequestWithNoTtlAndSetEmptyTtl() thro givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -836,7 +841,7 @@ public void cacheBidsOpenrtbShouldReturnExpectedResultForBids() { givenAuctionContext(), CacheContext.builder() .shouldCacheBids(true) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -857,7 +862,7 @@ public void cacheBidsOpenrtbShouldReturnExpectedResultForVideoBids() { givenAuctionContext(bidRequestBuilder -> bidRequestBuilder.imp(singletonList(imp))), CacheContext.builder() .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder1", singletonList("bidId1"))) + .bidderToVideoGeneratedBidIdsToModify(allBidIds) .build(), eventsContext); @@ -885,8 +890,8 @@ public void cacheBidsOpenrtbShouldReturnExpectedResultForBidsAndVideoBids() thro CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder1", singletonList("bidId1"))) - .bidderToBidIds(singletonMap("bidder2", singletonList("bidId2"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -911,7 +916,7 @@ public void cacheBidsOpenrtbShouldNotCacheVideoBidWithMissingImpId() { givenAuctionContext(bidRequestBuilder -> bidRequestBuilder.imp(asList(imp1, imp2))), CacheContext.builder() .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder1", singletonList("bidId1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) .build(), eventsContext); @@ -936,8 +941,8 @@ public void cacheBidsOpenrtbShouldWrapEmptyAdmFieldUsingNurlFieldValue() throws CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder1", singletonList("bid1"))) - .bidderToBidIds(singletonMap("bidder1", asList("bid1", "bid2"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -969,8 +974,8 @@ public void cacheBidsOpenrtbShouldNotModifyVastXmlWhenBidIdIsNotInToModifyList() CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder", singletonList("bid2"))) - .bidderToBidIds(singletonMap("bidder", singletonList("bid1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -996,8 +1001,8 @@ public void cacheBidsOpenrtbShouldNotAddTrackingImpToBidAdmWhenXmlDoesNotContain CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder", singletonList("bid2"))) - .bidderToBidIds(singletonMap("bidder", singletonList("bid1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), eventsContext); @@ -1020,8 +1025,14 @@ public void cacheBidsOpenrtbShouldAddTrackingLinkToImpTagWhenItIsEmpty() throws .id("impId1") .video(Video.builder().build())); + final String generatedBidId = "generatedBidId"; + final String bidder = "bidder"; + given(videoCachedBidIds.getGeneratedId(any(), any(), any())).willReturn(generatedBidId); + given(videoCachedBidIds.getBidderForBid(any(), any())).willReturn(Optional.of(bidder)); + + final String vastUrl = String.format("https://test-event.com/event?t=imp&b=%s&f=b&a=accountId", generatedBidId); given(eventsService.vastUrlTracking(anyString(), anyString(), any(), any(), any())) - .willReturn("https://test-event.com/event?t=imp&b=bid1&f=b&a=accountId"); + .willReturn(vastUrl); // when cacheService.cacheBidsOpenrtb( @@ -1030,12 +1041,14 @@ public void cacheBidsOpenrtbShouldAddTrackingLinkToImpTagWhenItIsEmpty() throws CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder", singletonList("bid1"))) - .bidderToBidIds(singletonMap("bidder", singletonList("bid1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), EventsContext.builder().enabledForAccount(true).enabledForRequest(false).build()); // then + verify(eventsService).vastUrlTracking(eq(generatedBidId), eq(bidder), any(), any(), any()); + final BidCacheRequest bidCacheRequest = captureBidCacheRequest(); assertThat(bidCacheRequest.getPuts()).hasSize(2) .containsOnly( @@ -1045,8 +1058,7 @@ public void cacheBidsOpenrtbShouldAddTrackingLinkToImpTagWhenItIsEmpty() throws .build(), PutObject.builder() .type("xml") - .value(new TextNode("")) + .value(new TextNode("")) .build()); } @@ -1062,8 +1074,14 @@ public void cacheBidsOpenrtbShouldAddTrackingImpToBidAdmXmlWhenThatBidShouldBeMo .id("impId1") .video(Video.builder().build())); - given(eventsService.vastUrlTracking(any(), any(), any(), any(), any())) - .willReturn("https://test-event.com/event?t=imp&b=bid1&f=b&a=accountId"); + final String generatedBidId = "generatedBidId"; + final String bidder = "bidder"; + given(videoCachedBidIds.getGeneratedId(any(), any(), any())).willReturn(generatedBidId); + given(videoCachedBidIds.getBidderForBid(any(), any())).willReturn(Optional.of(bidder)); + + final String vastUrl = String.format("https://test-event.com/event?t=imp&b=%s&f=b&a=accountId", generatedBidId); + given(eventsService.vastUrlTracking(anyString(), anyString(), any(), any(), any())) + .willReturn(vastUrl); // when cacheService.cacheBidsOpenrtb( @@ -1072,8 +1090,8 @@ public void cacheBidsOpenrtbShouldAddTrackingImpToBidAdmXmlWhenThatBidShouldBeMo CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder", singletonList("bid1"))) - .bidderToBidIds(singletonMap("bidder", singletonList("bid1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), EventsContext.builder().enabledForAccount(true).enabledForRequest(false).build()); @@ -1087,9 +1105,8 @@ public void cacheBidsOpenrtbShouldAddTrackingImpToBidAdmXmlWhenThatBidShouldBeMo .build(), PutObject.builder() .type("xml") - .value(new TextNode("http:/test.com" - + "" - + "")) + .value(new TextNode("http:/test.com" + + "")) .build()); } @@ -1111,8 +1128,8 @@ public void cacheBidsOpenrtbShouldNotAddTrackingImpWhenEventsNotEnabled() throws CacheContext.builder() .shouldCacheBids(true) .shouldCacheVideoBids(true) - .bidderToVideoBidIdsToModify(singletonMap("bidder", singletonList("bid1"))) - .bidderToBidIds(singletonMap("bidder", singletonList("bid1"))) + .bidderToVideoGeneratedBidIdsToModify(videoCachedBidIds) + .bidderToBidsToGeneratedIds(allBidIds) .build(), EventsContext.builder().enabledForAccount(false).build()); diff --git a/src/test/java/org/prebid/server/handler/AuctionHandlerTest.java b/src/test/java/org/prebid/server/handler/AuctionHandlerTest.java index f8e71396e93..c53ed71eb3a 100644 --- a/src/test/java/org/prebid/server/handler/AuctionHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/AuctionHandlerTest.java @@ -569,7 +569,7 @@ public void shouldIncrementCommonMetrics() { // then verify(metrics).updateRequestTypeMetric(eq(MetricName.legacy), eq(MetricName.ok)); - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), anyBoolean(), eq(1)); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), eq(1)); verify(metrics).updateImpTypesMetrics(singletonMap("banner", 1L)); verify(metrics).updateAccountRequestMetrics(eq("accountId"), eq(MetricName.legacy)); verify(metrics).updateRequestTimeMetric(anyLong()); @@ -594,7 +594,7 @@ public void shouldIncrementNoBidMetrics() { } @Test - public void shouldIncrementSafariAndNoCookieMetrics() { + public void shouldIncrementNoCookieMetrics() { // given givenPreBidRequestContext(identity(), builder -> builder.noLiveUids(true)); @@ -605,7 +605,7 @@ public void shouldIncrementSafariAndNoCookieMetrics() { auctionHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), eq(true), anyInt()); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), anyInt()); } @Test diff --git a/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java b/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java index 17911e2959f..86a771f508c 100644 --- a/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java @@ -625,6 +625,46 @@ public void shouldTolerateBiddersWithoutUsersyncUrl() throws IOException { assertThat(cookieSyncResponse).isEqualTo(CookieSyncResponse.of("ok", emptyList())); } + @Test + public void shouldSkipVendorHostCheckAndContinueWithBiddersCheckWhenHostVendorIdIsMissing() throws IOException { + // given + cookieSyncHandler = new CookieSyncHandler("http://external-url", 2000, uidsCookieService, applicationSettings, + bidderCatalog, tcfDefinerService, privacyEnforcementService, null, false, emptyList(), + analyticsReporter, metrics, timeoutFactory, jacksonMapper); + given(uidsCookieService.parseFromRequest(any())).willReturn(new UidsCookie( + Uids.builder().uids(singletonMap(RUBICON, UidWithExpiry.live("J5VLCWQP-26-CWFT"))).build(), + jacksonMapper)); + + given(routingContext.getBody()) + .willReturn(givenRequestBody( + CookieSyncRequest.builder().bidders(asList(RUBICON, APPNEXUS)).build())); + + rubiconUsersyncer = new Usersyncer(RUBICON, "", null, null, null, false); + appnexusUsersyncer = new Usersyncer(APPNEXUS_COOKIE, "", null, null, null, false); + givenUsersyncersReturningFamilyName(); + + given(bidderCatalog.isActive(RUBICON)).willReturn(true); + given(bidderCatalog.isActive(APPNEXUS)).willReturn(true); + + given(bidderCatalog.bidderInfoByName(APPNEXUS)) + .willReturn(BidderInfo.create(true, null, null, + null, null, 2, true, true, false)); + + givenTcfServiceReturningVendorIdResult(singleton(1)); + givenTcfServiceReturningBidderNamesResult(singleton(RUBICON)); + + // when + cookieSyncHandler.handle(routingContext); + + // then + verify(tcfDefinerService, never()).resultForVendorIds(anySet(), any()); + final CookieSyncResponse cookieSyncResponse = captureCookieSyncResponse(); + assertThat(cookieSyncResponse.getStatus()).isEqualTo("ok"); + assertThat(cookieSyncResponse.getBidderStatus()).hasSize(1) + .extracting(BidderUsersyncStatus::getBidder, BidderUsersyncStatus::getError) + .containsOnly(tuple(APPNEXUS, "Rejected by TCF")); + } + @Test public void shouldUpdateCookieSyncSetAndRejectByTcfMetricForEachRejectedAndSyncedBidder() { // given @@ -685,7 +725,7 @@ public void shouldUpdateCookieSyncMatchesMetricForEachAlreadySyncedBidder() { public void shouldRespondWithNoCookieStatusIfHostVendorRejectedByTcf() throws IOException { // given cookieSyncHandler = new CookieSyncHandler("http://external-url", 2000, uidsCookieService, applicationSettings, - bidderCatalog, tcfDefinerService, privacyEnforcementService, null, false, emptyList(), + bidderCatalog, tcfDefinerService, privacyEnforcementService, 1, false, emptyList(), analyticsReporter, metrics, timeoutFactory, jacksonMapper); given(uidsCookieService.parseFromRequest(any())) @@ -701,7 +741,7 @@ bidderCatalog, tcfDefinerService, privacyEnforcementService, null, false, emptyL given(bidderCatalog.isActive(RUBICON)).willReturn(true); given(bidderCatalog.isActive(APPNEXUS)).willReturn(true); - givenTcfServiceReturningVendorIdResult(singleton(1)); + givenTcfServiceReturningVendorIdResult(emptySet()); givenTcfServiceReturningBidderNamesResult(set(RUBICON, APPNEXUS)); // when diff --git a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java index 9aff7e95951..5cfae4e917d 100644 --- a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java @@ -90,7 +90,7 @@ public class SetuidHandlerTest extends VertxTest { @Before public void setUp() { - final Map vendorIdToGdpr = singletonMap(null, + final Map vendorIdToGdpr = singletonMap(1, PrivacyEnforcementAction.allowAll()); given(privacyEnforcementService.contextFromSetuidRequest(any(), any(), any())) @@ -118,7 +118,7 @@ public void setUp() { bidderCatalog, privacyEnforcementService, tcfDefinerService, - null, + 1, analyticsReporter, metrics, timeoutFactory); @@ -452,6 +452,42 @@ public void shouldRespondWithCookieIfUserIsNotInGdprScope() throws IOException { assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); } + @Test + public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() throws IOException { + // given + final Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + setuidHandler = new SetuidHandler(2000, uidsCookieService, applicationSettings, + bidderCatalog, privacyEnforcementService, tcfDefinerService, null, analyticsReporter, metrics, + new TimeoutFactory(clock)); + given(tcfDefinerService.resultForVendorIds(anySet(), any())) + .willReturn(Future.succeededFuture(TcfResponse.of(false, emptyMap(), null))); + + given(uidsCookieService.parseFromRequest(any())) + .willReturn(new UidsCookie(Uids.builder().uids(emptyMap()).build(), jacksonMapper)); + + // {"tempUIDs":{"rubicon":{"uid":"J5VLCWQP-26-CWFT"}}} + given(uidsCookieService.toCookie(any())).willReturn(Cookie + .cookie("uids", "eyJ0ZW1wVUlEcyI6eyJydWJpY29uIjp7InVpZCI6Iko1VkxDV1FQLTI2LUNXRlQifX19")); + + given(httpRequest.getParam("bidder")).willReturn(RUBICON); + given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); + + given(httpResponse.setStatusCode(anyInt())).willReturn(httpResponse); + + // when + setuidHandler.handle(routingContext); + + // then + verify(tcfDefinerService, never()).resultForVendorIds(anySet(), any()); + verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse).end(); + + final String uidsCookie = getUidsCookie(); + final Uids decodedUids = decodeUids(uidsCookie); + assertThat(decodedUids.getUids()).hasSize(1); + assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + } + @Test public void shouldUpdateOptOutsMetricIfOptedOut() { // given diff --git a/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java b/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java index 32513a625f4..f1992b0110b 100644 --- a/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/openrtb2/AmpHandlerTest.java @@ -471,7 +471,7 @@ public void shouldIncrementAppRequestMetrics() { ampHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), anyBoolean(), anyInt()); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), anyInt()); } @Test @@ -493,7 +493,7 @@ public void shouldIncrementNoCookieMetrics() { ampHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), eq(true), anyInt()); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), anyInt()); } @Test @@ -511,7 +511,7 @@ public void shouldIncrementImpsRequestedMetrics() { ampHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(anyBoolean(), anyBoolean(), anyBoolean(), eq(1)); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(anyBoolean(), anyBoolean(), eq(1)); } @Test diff --git a/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java b/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java index cfca6fb8d69..a355efbd4f0 100644 --- a/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/openrtb2/AuctionHandlerTest.java @@ -369,7 +369,7 @@ public void shouldIncrementAppRequestMetrics() { auctionHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), anyBoolean(), anyInt()); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(true), anyBoolean(), anyInt()); } @Test @@ -390,7 +390,7 @@ public void shouldIncrementNoCookieMetrics() { auctionHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), eq(true), anyInt()); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(eq(false), eq(false), anyInt()); } @Test @@ -407,7 +407,7 @@ public void shouldIncrementImpsRequestedMetrics() { auctionHandler.handle(routingContext); // then - verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(anyBoolean(), anyBoolean(), anyBoolean(), eq(1)); + verify(metrics).updateAppAndNoCookieAndImpsRequestedMetrics(anyBoolean(), anyBoolean(), eq(1)); } @Test diff --git a/src/test/java/org/prebid/server/it/AdformTest.java b/src/test/java/org/prebid/server/it/AdformTest.java index 7cec9b56659..c8181a6d14d 100644 --- a/src/test/java/org/prebid/server/it/AdformTest.java +++ b/src/test/java/org/prebid/server/it/AdformTest.java @@ -49,8 +49,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromAdform() throws IOException, .withHeader("User-Agent", equalTo("userAgent")) .withHeader("X-Request-Agent", equalTo("PrebidAdapter 0.1.3")) .withHeader("X-Forwarded-For", equalTo("193.168.244.1")) - .withHeader("Cookie", equalTo("uid=AF-UID;DigiTrust.v1.identity=eyJpZCI6ImlkIiwidmVyc2l" - + "vbiI6MSwia2V5diI6MTIzLCJwcml2YWN5Ijp7Im9wdG91dCI6ZmFsc2V9fQ")) + .withHeader("Cookie", equalTo("uid=AF-UID")) .withRequestBody(absent()) .willReturn(aResponse().withBody(jsonFrom("openrtb2/adform/test-adform-bid-response-1.json")))); @@ -99,9 +98,7 @@ public void auctionShouldRespondWithBidsFromAdform() throws IOException { .withHeader("User-Agent", equalTo("userAgent")) .withHeader("X-Request-Agent", equalTo("PrebidAdapter 0.1.3")) .withHeader("X-Forwarded-For", equalTo("193.168.244.1")) - .withHeader("Cookie", equalTo("uid=AF-UID;DigiTrust.v1.identity" - //{"id":"id","version":1,"keyv":123,"privacy":{"optout":true}} - + "=eyJpZCI6ImlkIiwidmVyc2lvbiI6MSwia2V5diI6MTIzLCJwcml2YWN5Ijp7Im9wdG91dCI6dHJ1ZX19")) + .withHeader("Cookie", equalTo("uid=AF-UID")) .withRequestBody(absent()) .willReturn(aResponse().withBody(jsonFrom("auction/adform/test-adform-bid-response-1.json")))); diff --git a/src/test/java/org/prebid/server/it/AdheseTest.java b/src/test/java/org/prebid/server/it/AdheseTest.java index 5c108aaff67..e1b8b21ff14 100644 --- a/src/test/java/org/prebid/server/it/AdheseTest.java +++ b/src/test/java/org/prebid/server/it/AdheseTest.java @@ -20,11 +20,10 @@ public class AdheseTest extends IntegrationTest { @Test public void openrtb2AuctionShouldRespondWithBidsFromAdhese() throws IOException, JSONException { - WIRE_MOCK_RULE.stubFor(WireMock.get(WireMock.urlPathEqualTo("/adhese-exchange/sl_adhese_prebid_demo_-" - + "leaderboard/ag55/cigent;brussels/tlall/xtconsentValue/xfhttp%3A%2F%2Fwww.example.com/xzifaId")) + WIRE_MOCK_RULE.stubFor(WireMock.post(WireMock.urlPathEqualTo("/adhese-exchange")) .withHeader("Accept", WireMock.equalTo("application/json")) .withHeader("Content-Type", WireMock.equalTo("application/json;charset=UTF-8")) - .withRequestBody(WireMock.absent()) + .withRequestBody(WireMock.equalToJson("{\"slots\":[{\"slotname\":\"_adhese_prebid_demo_-leaderboard\"}],\"parameters\":{\"ag\":[\"55\"],\"ci\":[\"gent\",\"brussels\"],\"tl\":[\"all\"],\"xt\":[\"consentValue\"],\"xf\":[\"http://www.example.com\"],\"xz\":[\"ifaId\"]}}")) .willReturn(WireMock.aResponse().withBody(jsonFrom("openrtb2/adhese/test-adhese-bid-response.json")))); // pre-bid cache diff --git a/src/test/java/org/prebid/server/it/AdkernelAdnTest.java b/src/test/java/org/prebid/server/it/AdkernelAdnTest.java index 8f143fafb68..2203ea3a99d 100644 --- a/src/test/java/org/prebid/server/it/AdkernelAdnTest.java +++ b/src/test/java/org/prebid/server/it/AdkernelAdnTest.java @@ -26,7 +26,7 @@ public class AdkernelAdnTest extends IntegrationTest { public void openrtb2AuctionShouldRespondWithBidsFromAdkerneladn() throws IOException, JSONException { // given // adkernelAdn bid response for imp 021 - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adkernelAdn-exchange")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adkernelAdn.tag.adkernel.com")) .withQueryParam("account", equalTo("101")) .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) .withHeader("Accept", equalTo("application/json")) @@ -36,7 +36,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromAdkerneladn() throws IOExcep jsonFrom("openrtb2/adkerneladn/test-adkerneladn-bid-response-1.json")))); // adkernelAdn bid response for imp 022 - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adkernelAdn-exchange")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adkernelAdn.tag.adkernel.com")) .withQueryParam("account", equalTo("102")) .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) .withHeader("Accept", equalTo("application/json")) diff --git a/src/test/java/org/prebid/server/it/AdopplerTest.java b/src/test/java/org/prebid/server/it/AdopplerTest.java index 8aa0a6601c6..4550bd6c8d5 100644 --- a/src/test/java/org/prebid/server/it/AdopplerTest.java +++ b/src/test/java/org/prebid/server/it/AdopplerTest.java @@ -25,9 +25,10 @@ public class AdopplerTest extends IntegrationTest { public void openrtb2AuctionShouldRespondWithBidsFromAdoppler() throws IOException, JSONException { // given // Adoppler bid response for imp 001 - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adoppler-exchange/processHeaderBid/unit1")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adoppler-exchange")) .withHeader("Accept", equalTo("application/json")) .withHeader("Content-Type", equalTo("application/json;charset=UTF-8")) + .withHeader("X-OpenRTB-Version", equalTo("2.5")) .withRequestBody(equalToJson(jsonFrom("openrtb2/adoppler/test-adoppler-bid-request-1.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/adoppler/test-adoppler-bid-response-1.json")))); diff --git a/src/test/java/org/prebid/server/it/AmxTest.java b/src/test/java/org/prebid/server/it/AmxTest.java new file mode 100644 index 00000000000..2ec0bb9fd15 --- /dev/null +++ b/src/test/java/org/prebid/server/it/AmxTest.java @@ -0,0 +1,60 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static io.restassured.RestAssured.given; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class AmxTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromAmx() throws IOException, JSONException { + // given + // Amx bid response for imp + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/amx-exchange")) + .withQueryParam("v", equalTo("pbs1.0")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/amx/test-amx-bid-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/amx/test-amx-bid-response.json")))); + + // pre-bid cache + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/cache")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/amx/test-cache-amx-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/amx/test-cache-amx-response.json")))); + + // when + final Response response = given(SPEC) + .header("Referer", "http://www.example.com") + .header("X-Forwarded-For", "193.168.244.1") + .header("User-Agent", "userAgent") + .header("Origin", "http://www.example.com") + // this uids cookie value stands for {"uids":{"amx":"AMX-UID"}} + .cookie("uids", "eyJ1aWRzIjp7ImFteCI6IkFNWC1VSUQifX0=") + .body(jsonFrom("openrtb2/amx/test-auction-amx-request.json")) + .post("/openrtb2/auction"); + + // then + final String expectedAuctionResponse = openrtbAuctionResponseFrom( + "openrtb2/amx/test-auction-amx-response.json", + response, singletonList("amx")); + + JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.NON_EXTENSIBLE); + } +} diff --git a/src/test/java/org/prebid/server/it/AppnexusVideoTest.java b/src/test/java/org/prebid/server/it/AppnexusVideoTest.java index 9a23e70aee1..3de4bd25d97 100644 --- a/src/test/java/org/prebid/server/it/AppnexusVideoTest.java +++ b/src/test/java/org/prebid/server/it/AppnexusVideoTest.java @@ -55,8 +55,7 @@ public void openrtb2VideoShouldRespondWithBidsFromAppnexus() throws IOException, .post("/openrtb2/video"); // then - // TODO remove "empty" when VideoRequest will proceed consentValue. - final String expectedAuctionResponse = jsonFrom("openrtb2/video/test-video-appnexus-response-empty.json"); + final String expectedAuctionResponse = jsonFrom("openrtb2/video/test-video-appnexus-response.json"); JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.NON_EXTENSIBLE); } diff --git a/src/test/java/org/prebid/server/it/GumgumTest.java b/src/test/java/org/prebid/server/it/GumgumTest.java index 76e13f5aeaa..16e53a7385c 100644 --- a/src/test/java/org/prebid/server/it/GumgumTest.java +++ b/src/test/java/org/prebid/server/it/GumgumTest.java @@ -11,6 +11,8 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -25,6 +27,8 @@ public void openrtb2AuctionShouldRespondWithBidsFromGumGum() throws IOException, // given // GumGum bid response for imp 001 and 002 WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/gumgum-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) .withRequestBody(equalToJson(jsonFrom("openrtb2/gumgum/test-gumgum-bid-request-1.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/gumgum/test-gumgum-bid-response-1.json")))); @@ -40,7 +44,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromGumGum() throws IOException, .header("User-Agent", "userAgent") .header("Origin", "http://www.example.com") // this uids cookie value stands for {"uids":{"gum":"GUM-UID"}} - .cookie("uids", "eyJ1aWRzIjp7Imd1bSI6IkdVTS1VSUQifX0=") + .cookie("uids", "eyJ1aWRzIjp7Imd1bWd1bSI6IkdVTS1VSUQifX0=") .body(jsonFrom("openrtb2/gumgum/test-auction-gumgum-request.json")) .post("/openrtb2/auction"); diff --git a/src/test/java/org/prebid/server/it/IntegrationTest.java b/src/test/java/org/prebid/server/it/IntegrationTest.java index fbc2e0c110d..5098ec932a8 100644 --- a/src/test/java/org/prebid/server/it/IntegrationTest.java +++ b/src/test/java/org/prebid/server/it/IntegrationTest.java @@ -52,6 +52,7 @@ public abstract class IntegrationTest extends VertxTest { public static final WireMockClassRule WIRE_MOCK_RULE = new WireMockClassRule(options() .port(WIREMOCK_PORT) .gzipDisabled(true) + .jettyStopTimeout(5000L) .extensions(IntegrationTest.CacheResponseTransformer.class)); @Rule diff --git a/src/test/java/org/prebid/server/it/NobidTest.java b/src/test/java/org/prebid/server/it/NobidTest.java new file mode 100644 index 00000000000..a9b31f52623 --- /dev/null +++ b/src/test/java/org/prebid/server/it/NobidTest.java @@ -0,0 +1,60 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static io.restassured.RestAssured.given; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class NobidTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromNobid() throws IOException, JSONException { + // given + // Nobid bid response for imp + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/nobid-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/nobid/test-nobid-bid-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/nobid/test-nobid-bid-response.json")))); + + // pre-bid cache + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/cache")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/nobid/test-cache-nobid-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/nobid/test-cache-nobid-response.json")))); + + // when + final Response response = given(SPEC) + .header("Referer", "http://www.example.com") + .header("X-Forwarded-For", "193.168.244.1") + .header("User-Agent", "userAgent") + .header("Origin", "http://www.example.com") + // this uids cookie value stands for {"uids":{"nobid":"NB-UID"}} + .cookie("uids", "eyJ1aWRzIjp7Im5vYmlkIjoiTkItVUlEIn19") + .body(jsonFrom("openrtb2/nobid/test-auction-nobid-request.json")) + .post("/openrtb2/auction"); + + // then + final String expectedAuctionResponse = openrtbAuctionResponseFrom( + "openrtb2/nobid/test-auction-nobid-response.json", + response, singletonList("nobid")); + + JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.NON_EXTENSIBLE); + } +} diff --git a/src/test/java/org/prebid/server/it/SilvermobTest.java b/src/test/java/org/prebid/server/it/SilvermobTest.java new file mode 100644 index 00000000000..6ce714dd722 --- /dev/null +++ b/src/test/java/org/prebid/server/it/SilvermobTest.java @@ -0,0 +1,63 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static io.restassured.RestAssured.given; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class SilvermobTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromSilvermob() throws IOException, JSONException { + // given + // Silvermob bid response for imp + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/silvermob-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=UTF-8")) + .withHeader("User-Agent", equalToIgnoreCase("test-user-agent")) + .withHeader("X-Forwarded-For", equalToIgnoreCase("123.123.123.123")) + .withHeader("x-openrtb-version", equalToIgnoreCase("2.5")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/silvermob/test-silvermob-bid-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/silvermob/test-silvermob-bid-response.json")))); + + // pre-bid cache + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/cache")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/silvermob/test-cache-silvermob-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/silvermob/test-cache-silvermob-response.json")))); + + // when + final Response response = given(SPEC) + .header("Referer", "http://www.example.com") + .header("X-Forwarded-For", "193.168.244.1") + .header("User-Agent", "userAgent") + .header("Origin", "http://www.example.com") + // this uids cookie value stands for {"uids":{"silvermob":"SM-UID"}} + .cookie("uids", "eyJ1aWRzIjp7InNpbHZlcm1vYiI6IlNNLVVJRCJ9fQ==") + .body(jsonFrom("openrtb2/silvermob/test-auction-silvermob-request.json")) + .post("/openrtb2/auction"); + + // then + final String expectedAuctionResponse = openrtbAuctionResponseFrom( + "openrtb2/silvermob/test-auction-silvermob-response.json", + response, singletonList("silvermob")); + + JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.NON_EXTENSIBLE); + } +} diff --git a/src/test/java/org/prebid/server/it/SmaatoTest.java b/src/test/java/org/prebid/server/it/SmaatoTest.java index 0ed182b2aac..a7d5d7ab579 100644 --- a/src/test/java/org/prebid/server/it/SmaatoTest.java +++ b/src/test/java/org/prebid/server/it/SmaatoTest.java @@ -24,7 +24,6 @@ public class SmaatoTest extends IntegrationTest { @Test public void openrtb2AuctionShouldRespondWithBidsFromSmaato() throws IOException, JSONException { // given - // SmaatoBidder bid response for imp 001 WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/smaato-exchange")) .withHeader("Accept", equalTo("application/json")) .withHeader("Content-Type", equalTo("application/json;charset=UTF-8")) diff --git a/src/test/java/org/prebid/server/it/SmartadserverTest.java b/src/test/java/org/prebid/server/it/SmartadserverTest.java index dcfc58f6b23..da9a567f618 100644 --- a/src/test/java/org/prebid/server/it/SmartadserverTest.java +++ b/src/test/java/org/prebid/server/it/SmartadserverTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -24,7 +25,8 @@ public class SmartadserverTest extends IntegrationTest { public void openrtb2AuctionShouldRespondWithBidsFromSmartadserver() throws IOException, JSONException { // given // Smartadserver bid response for imp 001 - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/smartadserver-exchange")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/smartadserver-exchange/api/bid")) + .withQueryParam("callerId", equalTo("5")) .withRequestBody(equalToJson(jsonFrom("openrtb2/smartadserver/test-smartadserver-bid-request-1.json"))) .willReturn(aResponse() .withBody(jsonFrom("openrtb2/smartadserver/test-smartadserver-bid-response-1.json")))); diff --git a/src/test/java/org/prebid/server/it/TelariaTest.java b/src/test/java/org/prebid/server/it/TelariaTest.java index 2662333773f..0243d2815c1 100644 --- a/src/test/java/org/prebid/server/it/TelariaTest.java +++ b/src/test/java/org/prebid/server/it/TelariaTest.java @@ -34,7 +34,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromTelaria() throws IOException .withHeader("x-openrtb-version", equalTo("2.5")) .withHeader("Accept-Encoding", equalTo("gzip")) .withHeader("Accept-Language", equalTo("en")) - .withHeader("Content-Length", equalTo("677")) + .withHeader("Content-Length", equalTo("633")) .withHeader("DNT", equalTo("2")) .withHeader("Host", equalTo("localhost:8090")) .withRequestBody(equalToJson(jsonFrom("openrtb2/telaria/test-telaria-bid-request-1.json"))) diff --git a/src/test/java/org/prebid/server/it/TripleliftTest.java b/src/test/java/org/prebid/server/it/TripleliftTest.java index f10a63e3787..d8016f3ae24 100644 --- a/src/test/java/org/prebid/server/it/TripleliftTest.java +++ b/src/test/java/org/prebid/server/it/TripleliftTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -24,6 +25,8 @@ public class TripleliftTest extends IntegrationTest { public void openrtb2AuctionShouldRespondWithBidsFromTriplelift() throws IOException, JSONException { // given WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/triplelift-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalTo("application/json;charset=UTF-8")) .withRequestBody(equalToJson(jsonFrom("openrtb2/triplelift/test-triplelift-bid-request.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/triplelift/test-triplelift-bid-response.json")))); @@ -47,8 +50,8 @@ public void openrtb2AuctionShouldRespondWithBidsFromTriplelift() throws IOExcept final String expectedAuctionResponse = openrtbAuctionResponseFrom( "openrtb2/triplelift/test-auction-triplelift-response.json", response, singletonList("triplelift")); - final String actualStr = response.asString(); - JSONAssert.assertEquals(expectedAuctionResponse, actualStr, JSONCompareMode.NON_EXTENSIBLE); + + JSONAssert.assertEquals(expectedAuctionResponse, response.asString(), JSONCompareMode.NON_EXTENSIBLE); } } diff --git a/src/test/java/org/prebid/server/it/VrtcalTest.java b/src/test/java/org/prebid/server/it/VrtcalTest.java index c4a2ab8975e..daf120dfed6 100644 --- a/src/test/java/org/prebid/server/it/VrtcalTest.java +++ b/src/test/java/org/prebid/server/it/VrtcalTest.java @@ -11,6 +11,8 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -25,6 +27,8 @@ public void openrtb2AuctionShouldRespondWithBidsFromVrtcal() throws IOException, // given // Vrtcal bid response for imp 001 WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/vrtcal-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=utf-8")) .withRequestBody(equalToJson(jsonFrom("openrtb2/vrtcal/test-vrtcal-bid-request-1.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/vrtcal/test-vrtcal-bid-response-1.json")))); diff --git a/src/test/java/org/prebid/server/it/YieldmoTest.java b/src/test/java/org/prebid/server/it/YieldmoTest.java index 9718ec40452..0e707cd5376 100644 --- a/src/test/java/org/prebid/server/it/YieldmoTest.java +++ b/src/test/java/org/prebid/server/it/YieldmoTest.java @@ -11,6 +11,8 @@ import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -25,6 +27,8 @@ public void openrtb2AuctionShouldRespondWithBidsFromYieldmo() throws IOException // given // Yieldmo bid response for imp 001 WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/yieldmo-exchange")) + .withHeader("Accept", equalTo("application/json")) + .withHeader("Content-Type", equalToIgnoreCase("application/json;charset=utf-8")) .withRequestBody(equalToJson(jsonFrom("openrtb2/yieldmo/test-yieldmo-bid-request-1.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/yieldmo/test-yieldmo-bid-response-1.json")))); diff --git a/src/test/java/org/prebid/server/util/JsonMergeUtilTest.java b/src/test/java/org/prebid/server/json/JsonMergerTest.java similarity index 94% rename from src/test/java/org/prebid/server/util/JsonMergeUtilTest.java rename to src/test/java/org/prebid/server/json/JsonMergerTest.java index b355617bfad..7ac8e4432f9 100644 --- a/src/test/java/org/prebid/server/util/JsonMergeUtilTest.java +++ b/src/test/java/org/prebid/server/json/JsonMergerTest.java @@ -1,4 +1,4 @@ -package org.prebid.server.util; +package org.prebid.server.json; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; @@ -11,13 +11,13 @@ import static org.assertj.core.api.Assertions.assertThat; -public class JsonMergeUtilTest extends VertxTest { +public class JsonMergerTest extends VertxTest { - private JsonMergeUtil target; + private JsonMerger target; @Before public void setUp() { - target = new JsonMergeUtil(jacksonMapper); + target = new JsonMerger(jacksonMapper); } @Test diff --git a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java index 943cf54f87f..7623c7e273c 100644 --- a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java +++ b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; @RunWith(VertxUnitRunner.class) public class ConditionalLoggerTest { @@ -44,6 +45,19 @@ public void tearDown(TestContext context) { vertx.close(context.asyncAssertSuccess()); } + @Test + public void infoWithKeyShouldCallLoggerWithExpectedCount() { + // when + for (int i = 0; i < 10; i++) { + conditionalLogger.infoWithKey("key", "Log Message1", 2); + conditionalLogger.infoWithKey("key", "Log Message2", 2); + } + + // then + verify(logger, times(10)).info("Log Message2"); + verifyNoMoreInteractions(logger); + } + @Test public void infoShouldCallLoggerWithExpectedCount() { // when diff --git a/src/test/java/org/prebid/server/metric/MetricsTest.java b/src/test/java/org/prebid/server/metric/MetricsTest.java index 0ddffed3dc7..630ac19da8f 100644 --- a/src/test/java/org/prebid/server/metric/MetricsTest.java +++ b/src/test/java/org/prebid/server/metric/MetricsTest.java @@ -82,7 +82,7 @@ public void forAccountShouldReturnAccountMetricsConfiguredWithAccount() { metrics.forAccount(ACCOUNT_ID).incCounter(MetricName.requests); // then - assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isOne(); } @Test @@ -102,7 +102,7 @@ public void forAdapterShouldReturnAdapterMetricsConfiguredWithAdapterType() { metrics.forAdapter(RUBICON).incCounter(MetricName.bids_received); // then - assertThat(metricRegistry.counter("adapter.rubicon.bids_received").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.bids_received").getCount()).isOne(); } @Test @@ -125,7 +125,7 @@ public void shouldReturnAdapterRequestTypeMetricsConfiguredWithAdapterType() { metrics.forAdapter(RUBICON).requestType(MetricName.openrtb2web).incCounter(MetricName.requests); // then - assertThat(metricRegistry.counter("adapter.rubicon.requests.type.openrtb2-web").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.requests.type.openrtb2-web").getCount()).isOne(); } @Test @@ -148,19 +148,20 @@ public void shouldReturnAdapterRequestMetricsConfiguredWithAdapterType() { metrics.forAdapter(RUBICON).request().incCounter(MetricName.gotbids); // then - assertThat(metricRegistry.counter("adapter.rubicon.requests.gotbids").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.requests.gotbids").getCount()).isOne(); } @Test public void shouldReturnSameAccountAdapterMetricsOnSuccessiveCalls() { - assertThat(metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON)) - .isSameAs(metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON)); + assertThat(metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON)) + .isSameAs(metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON)); } @Test public void shouldReturnAccountAdapterMetricsConfiguredWithCounterType() { verifyCreatesConfiguredCounterType(metrics -> metrics .forAccount(ACCOUNT_ID) + .adapter() .forAdapter(RUBICON) .incCounter(MetricName.bids_received)); } @@ -168,22 +169,23 @@ public void shouldReturnAccountAdapterMetricsConfiguredWithCounterType() { @Test public void shouldReturnAccountAdapterMetricsConfiguredWithAccountAndAdapterType() { // when - metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON).incCounter(MetricName.bids_received); + metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON).incCounter(MetricName.bids_received); // then - assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.bids_received").getCount()).isOne(); } @Test public void shouldReturnSameAccountAdapterRequestMetricsOnSuccessiveCalls() { - assertThat(metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON).request()) - .isSameAs(metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON).request()); + assertThat(metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON).request()) + .isSameAs(metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON).request()); } @Test public void shouldReturnAccountAdapterRequestMetricsConfiguredWithCounterType() { verifyCreatesConfiguredCounterType(metrics -> metrics .forAccount(ACCOUNT_ID) + .adapter() .forAdapter(RUBICON) .request() .incCounter(MetricName.gotbids)); @@ -192,10 +194,11 @@ public void shouldReturnAccountAdapterRequestMetricsConfiguredWithCounterType() @Test public void shouldReturnAccountAdapterRequestMetricsConfiguredWithAccountAndAdapterType() { // when - metrics.forAccount(ACCOUNT_ID).forAdapter(RUBICON).request().incCounter(MetricName.gotbids); + metrics.forAccount(ACCOUNT_ID).adapter().forAdapter(RUBICON).request().incCounter(MetricName.gotbids); // then - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.requests.gotbids").getCount()) + .isOne(); } @Test @@ -218,7 +221,7 @@ public void shouldReturnAccountRequestTypeMetricsConfiguredWithAccount() { metrics.forAccount(ACCOUNT_ID).requestType(MetricName.openrtb2web).incCounter(MetricName.requests); // then - assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isOne(); } @Test @@ -238,7 +241,7 @@ public void userSyncShouldReturnUserSyncMetricsConfiguredWithPrefix() { metrics.userSync().incCounter(MetricName.opt_outs); // then - assertThat(metricRegistry.counter("usersync.opt_outs").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.opt_outs").getCount()).isOne(); } @Test @@ -260,7 +263,7 @@ public void shouldReturnBidderUserSyncMetricsConfiguredWithBidder() { metrics.userSync().forBidder(RUBICON).incCounter(MetricName.sets); // then - assertThat(metricRegistry.counter("usersync.rubicon.sets").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.rubicon.sets").getCount()).isOne(); } @Test @@ -280,7 +283,7 @@ public void cookieSyncShouldReturnCookieSyncMetricsConfiguredWithPrefix() { metrics.cookieSync().incCounter(MetricName.gen); // then - assertThat(metricRegistry.counter("cookie_sync.gen").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync.gen").getCount()).isOne(); } @Test @@ -302,7 +305,7 @@ public void shouldReturnBidderCookieSyncMetricsConfiguredWithBidder() { metrics.cookieSync().forBidder(RUBICON).incCounter(MetricName.gen); // then - assertThat(metricRegistry.counter("cookie_sync.rubicon.gen").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync.rubicon.gen").getCount()).isOne(); } @Test @@ -324,7 +327,7 @@ public void forRequestTypeShouldReturnRequestStatusMetricsConfiguredWithRequestT metrics.forRequestType(MetricName.openrtb2web).incCounter(MetricName.ok); // then - assertThat(metricRegistry.counter("requests.ok.openrtb2-web").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("requests.ok.openrtb2-web").getCount()).isOne(); } @Test @@ -341,29 +344,17 @@ public void forCircuitBreakerShouldReturnCircuitBreakerMetricsConfiguredWithId() assertThat(metricRegistry.gauge("circuit-breaker.db.opened.count", () -> null).getValue()).isEqualTo(1L); } - @Test - public void updateSafariRequestsMetricShouldIncrementMetric() { - // when - metrics.updateSafariRequestsMetric(true); - metrics.updateSafariRequestsMetric(false); - - // then - assertThat(metricRegistry.counter("safari_requests").getCount()).isEqualTo(1); - } - @Test public void updateAppAndNoCookieAndImpsRequestedMetricsShouldIncrementMetrics() { // when - metrics.updateAppAndNoCookieAndImpsRequestedMetrics(true, false, false, 1); - metrics.updateAppAndNoCookieAndImpsRequestedMetrics(false, false, false, 2); - metrics.updateAppAndNoCookieAndImpsRequestedMetrics(false, false, true, 1); - metrics.updateAppAndNoCookieAndImpsRequestedMetrics(false, true, false, 1); + metrics.updateAppAndNoCookieAndImpsRequestedMetrics(true, false, 1); + metrics.updateAppAndNoCookieAndImpsRequestedMetrics(false, false, 2); + metrics.updateAppAndNoCookieAndImpsRequestedMetrics(false, true, 1); // then - assertThat(metricRegistry.counter("app_requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("no_cookie_requests").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("safari_no_cookie_requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("imps_requested").getCount()).isEqualTo(5); + assertThat(metricRegistry.counter("app_requests").getCount()).isOne(); + assertThat(metricRegistry.counter("no_cookie_requests").getCount()).isOne(); + assertThat(metricRegistry.counter("imps_requested").getCount()).isEqualTo(4); } @Test @@ -382,7 +373,7 @@ public void updateImpTypesMetricsByCountPerMediaTypeShouldIncrementMetrics() { // then assertThat(metricRegistry.counter("imps_banner").getCount()).isEqualTo(3); assertThat(metricRegistry.counter("imps_video").getCount()).isEqualTo(5); - assertThat(metricRegistry.counter("imps_native").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("imps_native").getCount()).isOne(); assertThat(metricRegistry.counter("imps_audio").getCount()).isEqualTo(4); } @@ -409,9 +400,9 @@ public void updateImpTypesMetricsByImpsShouldGroupCountByMediaTypeAndCallOverloa verify(metricsSpy).updateImpTypesMetrics(eq(expectedMap)); - assertThat(metricRegistry.counter("imps_banner").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("imps_banner").getCount()).isOne(); assertThat(metricRegistry.counter("imps_video").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("imps_native").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("imps_native").getCount()).isOne(); assertThat(metricRegistry.counter("imps_audio").getCount()).isEqualTo(2); } @@ -421,7 +412,7 @@ public void updateRequestTimeMetricShouldUpdateMetric() { metrics.updateRequestTimeMetric(456L); // then - assertThat(metricRegistry.timer("request_time").getCount()).isEqualTo(1); + assertThat(metricRegistry.timer("request_time").getCount()).isOne(); } @Test @@ -435,12 +426,12 @@ public void updateRequestTypeMetricShouldIncrementMetric() { metrics.updateRequestTypeMetric(MetricName.amp, MetricName.networkerr); // then - assertThat(metricRegistry.counter("requests.ok.openrtb2-web").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("requests.blacklisted_account.openrtb2-web").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("requests.blacklisted_app.openrtb2-app").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("requests.err.openrtb2-app").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("requests.badinput.amp").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("requests.networkerr.amp").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("requests.ok.openrtb2-web").getCount()).isOne(); + assertThat(metricRegistry.counter("requests.blacklisted_account.openrtb2-web").getCount()).isOne(); + assertThat(metricRegistry.counter("requests.blacklisted_app.openrtb2-app").getCount()).isOne(); + assertThat(metricRegistry.counter("requests.err.openrtb2-app").getCount()).isOne(); + assertThat(metricRegistry.counter("requests.badinput.amp").getCount()).isOne(); + assertThat(metricRegistry.counter("requests.networkerr.amp").getCount()).isOne(); } @Test @@ -458,8 +449,8 @@ public void updateAccountRequestMetricsShouldIncrementMetrics() { metrics.updateAccountRequestMetrics(ACCOUNT_ID, MetricName.openrtb2web); // then - assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isOne(); + assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isOne(); } @Test @@ -475,10 +466,10 @@ public void updateAdapterRequestTypeAndNoCookieMetricsShouldUpdateMetricsAsExpec // then assertThat(metricRegistry.counter("adapter.rubicon.requests.type.openrtb2-app").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.no_cookie_requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.requests.type.amp").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.type.openrtb2-app").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.type.amp").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.no_cookie_requests").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.rubicon.requests.type.amp").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.type.openrtb2-app").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.type.amp").getCount()).isOne(); } @Test @@ -492,10 +483,10 @@ public void updateAdapterResponseTimeShouldUpdateMetrics() { metrics.updateAdapterResponseTime(INVALID_BIDDER, ACCOUNT_ID, 500); // then - assertThat(metricRegistry.timer("adapter.rubicon.request_time").getCount()).isEqualTo(1); - assertThat(metricRegistry.timer("account.accountId.rubicon.request_time").getCount()).isEqualTo(1); + assertThat(metricRegistry.timer("adapter.rubicon.request_time").getCount()).isOne(); + assertThat(metricRegistry.timer("account.accountId.adapter.rubicon.request_time").getCount()).isOne(); assertThat(metricRegistry.timer("adapter.UNKNOWN.request_time").getCount()).isEqualTo(2); - assertThat(metricRegistry.timer("account.accountId.UNKNOWN.request_time").getCount()).isEqualTo(2); + assertThat(metricRegistry.timer("account.accountId.adapter.UNKNOWN.request_time").getCount()).isEqualTo(2); } @Test @@ -509,10 +500,10 @@ public void updateAdapterRequestNobidMetricsShouldIncrementMetrics() { metrics.updateAdapterRequestNobidMetrics(INVALID_BIDDER, ACCOUNT_ID); // then - assertThat(metricRegistry.counter("adapter.rubicon.requests.nobid").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.nobid").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.requests.nobid").getCount()).isOne(); + assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.requests.nobid").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.nobid").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("account.accountId.UNKNOWN.requests.nobid").getCount()).isEqualTo(2); + assertThat(metricRegistry.counter("account.accountId.adapter.UNKNOWN.requests.nobid").getCount()).isEqualTo(2); } @Test @@ -526,10 +517,12 @@ public void updateAdapterRequestGotbidsMetricsShouldIncrementMetrics() { metrics.updateAdapterRequestGotbidsMetrics(INVALID_BIDDER, ACCOUNT_ID); // then - assertThat(metricRegistry.counter("adapter.rubicon.requests.gotbids").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.requests.gotbids").getCount()).isOne(); + assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.requests.gotbids").getCount()) + .isOne(); assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.gotbids").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("account.accountId.UNKNOWN.requests.gotbids").getCount()).isEqualTo(2); + assertThat(metricRegistry.counter("account.accountId.adapter.UNKNOWN.requests.gotbids").getCount()) + .isEqualTo(2); } @Test @@ -545,15 +538,15 @@ public void updateAdapterBidMetricsShouldUpdateMetrics() { // then assertThat(metricRegistry.histogram("adapter.rubicon.prices").getCount()).isEqualTo(2); - assertThat(metricRegistry.histogram("account.accountId.rubicon.prices").getCount()).isEqualTo(2); + assertThat(metricRegistry.histogram("account.accountId.adapter.rubicon.prices").getCount()).isEqualTo(2); assertThat(metricRegistry.counter("adapter.rubicon.bids_received").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("adapter.rubicon.banner.adm_bids_received").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.video.nurl_bids_received").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.bids_received").getCount()).isEqualTo(2); + assertThat(metricRegistry.counter("adapter.rubicon.banner.adm_bids_received").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.rubicon.video.nurl_bids_received").getCount()).isOne(); assertThat(metricRegistry.histogram("adapter.UNKNOWN.prices").getCount()).isEqualTo(2); - assertThat(metricRegistry.histogram("account.accountId.UNKNOWN.prices").getCount()).isEqualTo(2); + assertThat(metricRegistry.histogram("account.accountId.adapter.UNKNOWN.prices").getCount()).isEqualTo(2); assertThat(metricRegistry.counter("adapter.UNKNOWN.bids_received").getCount()).isEqualTo(2); - assertThat(metricRegistry.counter("account.accountId.UNKNOWN.bids_received").getCount()).isEqualTo(2); + assertThat(metricRegistry.counter("account.accountId.adapter.UNKNOWN.bids_received").getCount()).isEqualTo(2); assertThat(metricRegistry.counter("adapter.UNKNOWN.banner.nurl_bids_received").getCount()).isEqualTo(2); } @@ -568,17 +561,47 @@ public void updateAdapterRequestErrorMetricShouldIncrementMetrics() { metrics.updateAdapterRequestErrorMetric(INVALID_BIDDER, MetricName.badinput); // then - assertThat(metricRegistry.counter("adapter.rubicon.requests.badinput").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.requests.badinput").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.UNKNOWN.requests.badinput").getCount()).isEqualTo(2); } + @Test + public void updateSizeValidationMetricsShouldIncrementMetrics() { + // given + given(bidderCatalog.isValidName(INVALID_BIDDER)).willReturn(false); + + // when + metrics.updateSizeValidationMetrics(RUBICON, ACCOUNT_ID, MetricName.err); + metrics.updateSizeValidationMetrics(INVALID_BIDDER, ACCOUNT_ID, MetricName.err); + + // then + assertThat(metricRegistry.counter("adapter.rubicon.response.validation.size.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.UNKNOWN.response.validation.size.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.response.validation.size.err").getCount()).isEqualTo(2); + } + + @Test + public void updateSecureValidationMetricsShouldIncrementMetrics() { + // given + given(bidderCatalog.isValidName(INVALID_BIDDER)).willReturn(false); + + // when + metrics.updateSecureValidationMetrics(RUBICON, ACCOUNT_ID, MetricName.err); + metrics.updateSecureValidationMetrics(INVALID_BIDDER, ACCOUNT_ID, MetricName.err); + + // then + assertThat(metricRegistry.counter("adapter.rubicon.response.validation.secure.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.UNKNOWN.response.validation.secure.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("account.accountId.response.validation.secure.err").getCount()).isEqualTo(2); + } + @Test public void updateCookieSyncRequestMetricShouldIncrementMetric() { // when metrics.updateCookieSyncRequestMetric(); // then - assertThat(metricRegistry.counter("cookie_sync_requests").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync_requests").getCount()).isOne(); } @Test @@ -587,7 +610,7 @@ public void updateUserSyncOptoutMetricShouldIncrementMetric() { metrics.updateUserSyncOptoutMetric(); // then - assertThat(metricRegistry.counter("usersync.opt_outs").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.opt_outs").getCount()).isOne(); } @Test @@ -596,7 +619,7 @@ public void updateUserSyncBadRequestMetricShouldIncrementMetric() { metrics.updateUserSyncBadRequestMetric(); // then - assertThat(metricRegistry.counter("usersync.bad_requests").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.bad_requests").getCount()).isOne(); } @Test @@ -605,7 +628,7 @@ public void updateUserSyncSetsMetricShouldIncrementMetric() { metrics.updateUserSyncSetsMetric(RUBICON); // then - assertThat(metricRegistry.counter("usersync.rubicon.sets").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.rubicon.sets").getCount()).isOne(); } @Test @@ -614,7 +637,7 @@ public void updateUserSyncTcfBlockedMetricShouldIncrementMetric() { metrics.updateUserSyncTcfBlockedMetric(RUBICON); // then - assertThat(metricRegistry.counter("usersync.rubicon.tcf.blocked").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("usersync.rubicon.tcf.blocked").getCount()).isOne(); } @Test @@ -628,7 +651,7 @@ public void updateCookieSyncTcfBlockedMetricShouldIncrementMetric() { metrics.updateCookieSyncTcfBlockedMetric(INVALID_BIDDER); // then - assertThat(metricRegistry.counter("cookie_sync.rubicon.tcf.blocked").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync.rubicon.tcf.blocked").getCount()).isOne(); assertThat(metricRegistry.counter("cookie_sync.UNKNOWN.tcf.blocked").getCount()).isEqualTo(2); } @@ -638,7 +661,7 @@ public void updateCookieSyncGenMetricShouldIncrementMetric() { metrics.updateCookieSyncGenMetric(RUBICON); // then - assertThat(metricRegistry.counter("cookie_sync.rubicon.gen").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync.rubicon.gen").getCount()).isOne(); } @Test @@ -647,30 +670,27 @@ public void updateCookieSyncMatchesMetricShouldIncrementMetric() { metrics.updateCookieSyncMatchesMetric(RUBICON); // then - assertThat(metricRegistry.counter("cookie_sync.rubicon.matches").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("cookie_sync.rubicon.matches").getCount()).isOne(); } @Test - public void updateAuctionTcfMetricShouldIncrementMetrics() { + public void updateAuctionTcfMetricsShouldIncrementMetrics() { // given given(bidderCatalog.isValidName(INVALID_BIDDER)).willReturn(false); - // when metrics.updateAuctionTcfMetrics(RUBICON, MetricName.openrtb2web, true, true, true, true); - metrics.updateAuctionTcfMetrics(INVALID_BIDDER, MetricName.openrtb2web, false, true, false, true); - metrics.updateAuctionTcfMetrics(INVALID_BIDDER, MetricName.openrtb2app, true, false, true, false); + metrics.updateAuctionTcfMetrics(INVALID_BIDDER, MetricName.openrtb2web, false, true, true, false); + metrics.updateAuctionTcfMetrics(INVALID_BIDDER, MetricName.openrtb2app, true, false, false, true); // then - assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.userid_removed").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.geo_masked").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.request_blocked").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.analytics_blocked").getCount()) - .isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-web.tcf.geo_masked").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-web.tcf.analytics_blocked").getCount()) - .isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-app.tcf.userid_removed").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-app.tcf.request_blocked").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.userid_removed").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.geo_masked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.analytics_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.request_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-web.tcf.geo_masked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-web.tcf.analytics_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-app.tcf.userid_removed").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.UNKNOWN.openrtb2-app.tcf.request_blocked").getCount()).isOne(); } @Test @@ -699,7 +719,7 @@ public void updatePrivacyCoppaMetricShouldIncrementMetric() { metrics.updatePrivacyCoppaMetric(); // then - assertThat(metricRegistry.counter("privacy.coppa").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.coppa").getCount()).isOne(); } @Test @@ -708,7 +728,7 @@ public void updatePrivacyLmtMetricShouldIncrementMetric() { metrics.updatePrivacyLmtMetric(); // then - assertThat(metricRegistry.counter("privacy.lmt").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.lmt").getCount()).isOne(); } @Test @@ -717,8 +737,8 @@ public void updatePrivacyCcpaMetricsShouldIncrementMetrics() { metrics.updatePrivacyCcpaMetrics(true, true); // then - assertThat(metricRegistry.counter("privacy.usp.specified").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("privacy.usp.opt-out").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.usp.specified").getCount()).isOne(); + assertThat(metricRegistry.counter("privacy.usp.opt-out").getCount()).isOne(); } @Test @@ -727,7 +747,7 @@ public void updatePrivacyTcfMissingMetricShouldIncrementMetric() { metrics.updatePrivacyTcfMissingMetric(); // then - assertThat(metricRegistry.counter("privacy.tcf.missing").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.missing").getCount()).isOne(); } @Test @@ -736,7 +756,16 @@ public void updatePrivacyTcfInvalidMetricShouldIncrementMetric() { metrics.updatePrivacyTcfInvalidMetric(); // then - assertThat(metricRegistry.counter("privacy.tcf.invalid").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.invalid").getCount()).isOne(); + } + + @Test + public void updatePrivacyTcfRequestsMetricShouldIncrementMetric() { + // when + metrics.updatePrivacyTcfRequestsMetric(1); + + // then + assertThat(metricRegistry.counter("privacy.tcf.v1.requests").getCount()).isOne(); } @Test @@ -747,9 +776,9 @@ public void updatePrivacyTcfGeoMetricShouldIncrementMetrics() { metrics.updatePrivacyTcfGeoMetric(2, false); // then - assertThat(metricRegistry.counter("privacy.tcf.v1.unknown-geo").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("privacy.tcf.v2.in-geo").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("privacy.tcf.v2.out-geo").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.v1.unknown-geo").getCount()).isOne(); + assertThat(metricRegistry.counter("privacy.tcf.v2.in-geo").getCount()).isOne(); + assertThat(metricRegistry.counter("privacy.tcf.v2.out-geo").getCount()).isOne(); } @Test @@ -758,7 +787,7 @@ public void updatePrivacyTcfVendorListMissingMetricShouldIncrementMetric() { metrics.updatePrivacyTcfVendorListMissingMetric(1); // then - assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.missing").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.missing").getCount()).isOne(); } @Test @@ -767,7 +796,7 @@ public void updatePrivacyTcfVendorListOkMetricShouldIncrementMetric() { metrics.updatePrivacyTcfVendorListOkMetric(1); // then - assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.ok").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.ok").getCount()).isOne(); } @Test @@ -776,7 +805,7 @@ public void updatePrivacyTcfVendorListErrorMetricShouldIncrementMetric() { metrics.updatePrivacyTcfVendorListErrorMetric(1); // then - assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("privacy.tcf.v1.vendorlist.err").getCount()).isOne(); } @Test @@ -801,13 +830,13 @@ public void shouldNotUpdateAccountMetricsIfVerbosityIsNone() { metrics.updateAdapterBidMetrics(RUBICON, ACCOUNT_ID, 1234L, true, "banner"); // then - assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isEqualTo(0); - assertThat(metricRegistry.timer("account.accountId.rubicon.request_time").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.nobid").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isEqualTo(0); - assertThat(metricRegistry.histogram("account.accountId.rubicon.prices").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isEqualTo(0); + assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isZero(); + assertThat(metricRegistry.timer("account.accountId.rubicon.request_time").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.requests.nobid").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isZero(); + assertThat(metricRegistry.histogram("account.accountId.rubicon.prices").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isZero(); } @Test @@ -823,13 +852,13 @@ public void shouldUpdateAccountRequestsMetricOnlyIfVerbosityIsBasic() { metrics.updateAdapterBidMetrics(RUBICON, ACCOUNT_ID, 1234L, true, "banner"); // then - assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isEqualTo(0); - assertThat(metricRegistry.timer("account.accountId.rubicon.request_time").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.nobid").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isEqualTo(0); - assertThat(metricRegistry.histogram("account.accountId.rubicon.prices").getCount()).isEqualTo(0); - assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isEqualTo(0); + assertThat(metricRegistry.counter("account.accountId.requests").getCount()).isOne(); + assertThat(metricRegistry.counter("account.accountId.requests.type.openrtb2-web").getCount()).isZero(); + assertThat(metricRegistry.timer("account.accountId.rubicon.request_time").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.requests.nobid").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.requests.gotbids").getCount()).isZero(); + assertThat(metricRegistry.histogram("account.accountId.rubicon.prices").getCount()).isZero(); + assertThat(metricRegistry.counter("account.accountId.rubicon.bids_received").getCount()).isZero(); } @Test @@ -838,7 +867,7 @@ public void shouldIncrementConnectionAcceptErrorsMetric() { metrics.updateConnectionAcceptErrors(); // then - assertThat(metricRegistry.counter("connection_accept_errors").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("connection_accept_errors").getCount()).isOne(); } @Test @@ -847,7 +876,7 @@ public void shouldUpdateDatabaseQueryTimeMetric() { metrics.updateDatabaseQueryTimeMetric(456L); // then - assertThat(metricRegistry.timer("db_query_time").getCount()).isEqualTo(1); + assertThat(metricRegistry.timer("db_query_time").getCount()).isOne(); } @Test @@ -893,8 +922,8 @@ public void shouldIncrementBothGeoLocationRequestsAndSuccessfulMetrics() { metrics.updateGeoLocationMetric(true); // then - assertThat(metricRegistry.counter("geolocation_requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("geolocation_successful").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("geolocation_requests").getCount()).isOne(); + assertThat(metricRegistry.counter("geolocation_successful").getCount()).isOne(); } @Test @@ -903,8 +932,8 @@ public void shouldIncrementBothGeoLocationRequestsAndFailMetrics() { metrics.updateGeoLocationMetric(false); // then - assertThat(metricRegistry.counter("geolocation_requests").getCount()).isEqualTo(1); - assertThat(metricRegistry.counter("geolocation_fail").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("geolocation_requests").getCount()).isOne(); + assertThat(metricRegistry.counter("geolocation_fail").getCount()).isOne(); } @Test @@ -915,9 +944,9 @@ public void shouldAlwaysIncrementGeoLocationRequestsMetricAndEitherSuccessfulOrF metrics.updateGeoLocationMetric(true); // then - assertThat(metricRegistry.counter("geolocation_requests").getCount()).isEqualTo(3); - assertThat(metricRegistry.counter("geolocation_fail").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("geolocation_fail").getCount()).isOne(); assertThat(metricRegistry.counter("geolocation_successful").getCount()).isEqualTo(2); + assertThat(metricRegistry.counter("geolocation_requests").getCount()).isEqualTo(3); } @Test @@ -926,7 +955,7 @@ public void shouldIncrementStoredRequestFoundMetric() { metrics.updateStoredRequestMetric(true); // then - assertThat(metricRegistry.counter("stored_requests_found").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("stored_requests_found").getCount()).isOne(); } @Test @@ -935,7 +964,7 @@ public void shouldIncrementStoredRequestMissingMetric() { metrics.updateStoredRequestMetric(false); // then - assertThat(metricRegistry.counter("stored_requests_missing").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("stored_requests_missing").getCount()).isOne(); } @Test @@ -944,7 +973,7 @@ public void shouldIncrementStoredImpFoundMetric() { metrics.updateStoredImpsMetric(true); // then - assertThat(metricRegistry.counter("stored_imps_found").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("stored_imps_found").getCount()).isOne(); } @Test @@ -953,7 +982,7 @@ public void shouldIncrementStoredImpMissingMetric() { metrics.updateStoredImpsMetric(false); // then - assertThat(metricRegistry.counter("stored_imps_missing").getCount()).isEqualTo(1); + assertThat(metricRegistry.counter("stored_imps_missing").getCount()).isOne(); } @Test @@ -963,7 +992,7 @@ public void shouldIncrementPrebidCacheRequestSuccessTimer() { // then assertThat(metricRegistry.timer("prebid_cache.requests.ok").getCount()).isEqualTo(1); - assertThat(metricRegistry.timer("account.accountId.prebid_cache.requests.ok").getCount()).isEqualTo(1); + assertThat(metricRegistry.timer("account.accountId.prebid_cache.requests.ok").getCount()).isOne(); } @Test @@ -973,7 +1002,7 @@ public void shouldIncrementPrebidCacheRequestFailedTimer() { // then assertThat(metricRegistry.timer("prebid_cache.requests.err").getCount()).isEqualTo(1); - assertThat(metricRegistry.timer("account.accountId.prebid_cache.requests.err").getCount()).isEqualTo(1); + assertThat(metricRegistry.timer("account.accountId.prebid_cache.requests.err").getCount()).isOne(); } @Test @@ -987,6 +1016,46 @@ public void shouldIncrementPrebidCacheCreativeSizeHistogram() { .isEqualTo(1); } + @Test + public void shouldCreateCurrencyRatesGaugeMetric() { + // when + metrics.createCurrencyRatesGauge(() -> true); + + // then + assertThat(metricRegistry.gauge("currency-rates.stale.count", () -> null).getValue()).isEqualTo(1L); + } + + @Test + public void updateSettingsCacheRefreshTimeShouldUpdateTimer() { + // when + metrics.updateSettingsCacheRefreshTime(MetricName.stored_request, MetricName.initialize, 123L); + + // then + assertThat(metricRegistry + .timer("settings.cache.stored-request.refresh.initialize.db_query_time") + .getCount()) + .isEqualTo(1); + } + + @Test + public void updateSettingsCacheRefreshErrorMetricShouldIncrementMetric() { + // when + metrics.updateSettingsCacheRefreshErrorMetric(MetricName.stored_request, MetricName.initialize); + + // then + assertThat(metricRegistry.counter("settings.cache.stored-request.refresh.initialize.err").getCount()) + .isEqualTo(1); + } + + @Test + public void updateSettingsCacheEventMetricShouldIncrementMetric() { + // when + metrics.updateSettingsCacheEventMetric(MetricName.account, MetricName.hit); + + // then + assertThat(metricRegistry.counter("settings.cache.account.hit").getCount()).isEqualTo(1); + } + private void verifyCreatesConfiguredCounterType(Consumer metricsConsumer) { final EnumMap> counterTypeClasses = new EnumMap<>(CounterType.class); counterTypeClasses.put(CounterType.counter, Counter.class); diff --git a/src/test/java/org/prebid/server/privacy/PrivacyExtractorTest.java b/src/test/java/org/prebid/server/privacy/PrivacyExtractorTest.java index 9fec35b41de..73fbe881bb1 100644 --- a/src/test/java/org/prebid/server/privacy/PrivacyExtractorTest.java +++ b/src/test/java/org/prebid/server/privacy/PrivacyExtractorTest.java @@ -17,6 +17,8 @@ import org.prebid.server.proto.request.CookieSyncRequest; import org.prebid.server.proto.request.PreBidRequest; +import java.util.ArrayList; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -37,7 +39,8 @@ public void setUp() { @Test public void shouldReturnGdprEmptyValueWhenRegsIsNull() { // given and when - final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().build()).getGdpr(); + final String gdpr = + privacyExtractor.validPrivacyFrom(BidRequest.builder().build(), new ArrayList<>()).getGdpr(); // then assertThat(gdpr).isEmpty(); @@ -47,7 +50,7 @@ public void shouldReturnGdprEmptyValueWhenRegsIsNull() { public void shouldReturnGdprEmptyValueWhenRegsExtIsNull() { // given and when final String gdpr = privacyExtractor.validPrivacyFrom( - BidRequest.builder().regs(Regs.of(null, null)).build()) + BidRequest.builder().regs(Regs.of(null, null)).build(), new ArrayList<>()) .getGdpr(); // then @@ -60,7 +63,8 @@ public void shouldReturnGdprEmptyValueWhenRegsExtGdprIsNoEqualsToOneOrZero() { final Regs regs = Regs.of(null, ExtRegs.of(2, null)); // when - final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr(); + final String gdpr = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr(); // then assertThat(gdpr).isEmpty(); @@ -72,7 +76,8 @@ public void shouldReturnGdprOneWhenExtRegsContainsGdprOne() { final Regs regs = Regs.of(null, ExtRegs.of(1, null)); // when - final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr(); + final String gdpr = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr(); // then assertThat(gdpr).isEqualTo("1"); @@ -84,7 +89,8 @@ public void shouldReturnGdprZeroWhenExtRegsContainsGdprZero() { final Regs regs = Regs.of(null, ExtRegs.of(0, null)); // when - final String gdpr = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getGdpr(); + final String gdpr = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()).getGdpr(); // then assertThat(gdpr).isEqualTo("0"); @@ -93,7 +99,8 @@ public void shouldReturnGdprZeroWhenExtRegsContainsGdprZero() { @Test public void shouldReturnConsentEmptyValueWhenExtUserIsNull() { // given and when - final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().build()).getConsentString(); + final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().build(), new ArrayList<>()) + .getConsentString(); // then assertThat(consent).isEmpty(); @@ -105,8 +112,9 @@ public void shouldReturnConsentEmptyValueWhenUserConsentIsNull() { final User user = User.builder().ext(ExtUser.builder().build()).build(); // when - final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build()) - .getConsentString(); + final String consent = + privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build(), new ArrayList<>()) + .getConsentString(); // then assertThat(consent).isEmpty(); @@ -118,23 +126,28 @@ public void shouldReturnConsentWhenUserContainsConsent() { final User user = User.builder().ext(ExtUser.builder().consent("consent").build()).build(); // when - final String consent = privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build()) - .getConsentString(); + final String consent = + privacyExtractor.validPrivacyFrom(BidRequest.builder().user(user).build(), new ArrayList<>()) + .getConsentString(); // then assertThat(consent).isEqualTo("consent"); } @Test - public void shouldReturnDefaultCcpaIfNotValid() { + public void shouldReturnDefaultCcpaWhenNotValidAndAddError() { // given final Regs regs = Regs.of(null, ExtRegs.of(null, "invalid")); + final ArrayList errors = new ArrayList<>(); // when - final Ccpa ccpa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCcpa(); + final Ccpa ccpa = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), errors).getCcpa(); // then assertThat(ccpa).isEqualTo(Ccpa.EMPTY); + assertThat(errors).containsOnly( + "CCPA consent invalid has invalid format: us_privacy must contain 4 characters"); } @Test @@ -143,7 +156,9 @@ public void shouldReturnDefaultCoppaIfNull() { final Regs regs = Regs.of(null, null); // when - final Integer coppa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCoppa(); + final Integer coppa = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()) + .getCoppa(); // then assertThat(coppa).isZero(); @@ -155,7 +170,9 @@ public void shouldReturnCoppaIfNotNull() { final Regs regs = Regs.of(42, null); // when - final Integer coppa = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build()).getCoppa(); + final Integer coppa = + privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).build(), new ArrayList<>()) + .getCoppa(); // then assertThat(coppa).isEqualTo(42); @@ -168,7 +185,8 @@ public void shouldReturnPrivacyWithParametersExtractedFromBidRequest() { final User user = User.builder().ext(ExtUser.builder().consent("consent").build()).build(); // when - final Privacy privacy = privacyExtractor.validPrivacyFrom(BidRequest.builder().regs(regs).user(user).build()); + final Privacy privacy = privacyExtractor + .validPrivacyFrom(BidRequest.builder().regs(regs).user(user).build(), new ArrayList<>()); // then assertThat(privacy).isEqualTo(Privacy.of("0", "consent", Ccpa.of("1Yn-"), 0)); diff --git a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java index 5b3721655c2..5a01db2a4d5 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -38,11 +38,13 @@ import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.prebid.server.assertion.FutureAssertion.assertThat; @@ -237,6 +239,8 @@ public void resolveTcfContextShouldConsiderPresenceOfConsentStringAsInScope() { assertThat(result.result().getConsent()).isNotNull(); verifyZeroInteractions(geoLocationService); + verify(metrics).updatePrivacyTcfRequestsMetric(1); + verify(metrics).updatePrivacyTcfGeoMetric(1, null); } @Test @@ -382,6 +386,23 @@ public void resolveTcfContextShouldIncrementInvalidConsentStringMetric() { verify(metrics).updatePrivacyTcfInvalidMetric(); } + @Test + public void resultForVendorIdsShouldNotSetTcfRequestsAndTcfGeoMetricsWhenConsentIsNotValid() { + // given + given(tcf2Service.permissionsFor(any(), any())).willReturn(Future.succeededFuture()); + + // when + tcfDefinerService.resultForVendorIds(singleton(1), TcfContext.builder() + .gdpr("1") + .consent(TCStringEmpty.create()) + .ipAddress("ip") + .build()); + + // then + verify(metrics, never()).updatePrivacyTcfRequestsMetric(anyInt()); + verify(metrics, never()).updatePrivacyTcfGeoMetric(anyInt(), any()); + } + @Test public void resultForVendorIdsShouldAllowAllWhenGdprIsZero() { // when diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java index f59fccb1b6c..13c50e18de0 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeEightStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +330,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java index ed9efb63fdc..0465bfce725 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeFiveStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +330,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java index 5e63f2f8a7a..d0f1795c292 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeFourStrategyTest { @@ -224,20 +225,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -249,34 +250,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -288,36 +292,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setMaskDeviceInfo(false); - privacyEnforcementAction.setRemoveUserIds(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -325,7 +332,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java index 8bbeefe1aa8..0856f237e12 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeNineStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,38 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +329,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java index e503d5521ee..d508b952c9b 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeOneStrategyTest { @@ -224,20 +225,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -249,34 +250,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); + assertThat(result).usingFieldByFieldElementComparator() + .isEqualTo(Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -288,29 +292,35 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } private static PrivacyEnforcementAction allowPurpose() { @@ -318,4 +328,8 @@ private static PrivacyEnforcementAction allowPurpose() { privacyEnforcementAction.setBlockPixelSync(false); return privacyEnforcementAction; } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowPurpose(); + } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java index 99a2002c45a..8d7b0806ce0 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeSevenStrategyTest { @@ -224,20 +225,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -249,34 +250,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -288,37 +292,40 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setBlockAnalyticsReport(false); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -328,7 +335,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java index f0a210d0770..c9dc22660ad 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeSixStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +330,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java index 064e4930702..d24a1b04544 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeTenStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,38 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +329,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java index 3301fc0744a..a233c3a78ed 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeThreeStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,36 +290,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -323,7 +330,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java index 695bc2db299..03f322df0b2 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class PurposeTwoStrategyTest { @@ -222,20 +223,20 @@ public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhe vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowAllPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowAllPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowAllPurpose()); + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); + final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), vendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowance() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -247,34 +248,37 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(fullEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } @Test - public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNaturalAllowanceForDowngraded() { + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); @@ -286,37 +290,39 @@ public void processTypePurposeStrategyShouldAllowOnlyPurposeWhenThereAreNoNatura VendorV2.empty(3)); final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); + + final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) .willReturn(vendorPermissions); given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(emptyList()); + .willReturn(excludedVendorPermissions); + + final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2, vendorPermissionWitGvl3); // when final Collection result = target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); + final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, + vendorPermissionWitGvl2); + + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); assertThat(result).usingFieldByFieldElementComparator().isEqualTo( Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, vendorPermissionsWithGvl, - emptyList(), true); + verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); + verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE, tcString, + singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); } - private static PrivacyEnforcementAction allowAllPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setBlockBidderRequest(false); - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); } private static PrivacyEnforcementAction allowPurpose() { @@ -326,7 +332,10 @@ private static PrivacyEnforcementAction allowPurpose() { } private static PrivacyEnforcementAction allowNatural() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); privacyEnforcementAction.setMaskDeviceInfo(false); return privacyEnforcementAction; diff --git a/src/test/java/org/prebid/server/settings/CachingApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/CachingApplicationSettingsTest.java index bc10de0852d..f57089c51be 100644 --- a/src/test/java/org/prebid/server/settings/CachingApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/CachingApplicationSettingsTest.java @@ -11,6 +11,8 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.Timeout; import org.prebid.server.execution.TimeoutFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.StoredResponseDataResult; @@ -43,6 +45,8 @@ public class CachingApplicationSettingsTest { @Mock private ApplicationSettings applicationSettings; + @Mock + private Metrics metrics; private CachingApplicationSettings cachingApplicationSettings; @@ -52,8 +56,14 @@ public class CachingApplicationSettingsTest { public void setUp() { timeout = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault())).create(500L); - cachingApplicationSettings = new CachingApplicationSettings(applicationSettings, new SettingsCache(360, 100), - new SettingsCache(360, 100), new SettingsCache(360, 100), 360, 100); + cachingApplicationSettings = new CachingApplicationSettings( + applicationSettings, + new SettingsCache(360, 100), + new SettingsCache(360, 100), + new SettingsCache(360, 100), + metrics, + 360, + 100); } @Test @@ -132,6 +142,22 @@ public void getAccountByIdShouldNotCacheNotPreBidException() { .hasMessage("error"); } + @Test + public void getAccountByIdShouldUpdateMetrics() { + // given + final Account account = Account.builder().id("accountId").priceGranularity("med").build(); + given(applicationSettings.getAccountById(eq("accountId"), same(timeout))) + .willReturn(Future.succeededFuture(account)); + + // when + cachingApplicationSettings.getAccountById("accountId", timeout); + cachingApplicationSettings.getAccountById("accountId", timeout); + + // then + verify(metrics).updateSettingsCacheEventMetric(eq(MetricName.account), eq(MetricName.miss)); + verify(metrics).updateSettingsCacheEventMetric(eq(MetricName.account), eq(MetricName.hit)); + } + @Test public void getAdUnitConfigByIdShouldReturnResultFromCacheOnSuccessiveCalls() { // given diff --git a/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java new file mode 100644 index 00000000000..be99c9a42ce --- /dev/null +++ b/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java @@ -0,0 +1,144 @@ +package org.prebid.server.settings; + +import io.vertx.core.Future; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.execution.Timeout; +import org.prebid.server.settings.model.Account; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.prebid.server.assertion.FutureAssertion.assertThat; + +public class EnrichingApplicationSettingsTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private ApplicationSettings delegate; + + private EnrichingApplicationSettings enrichingApplicationSettings; + + @Mock + private Timeout timeout; + + @Test + public void getAccountByIdShouldOmitMergingWhenDefaultAccountIsNull() { + // given + enrichingApplicationSettings = new EnrichingApplicationSettings(delegate, null); + + final Account returnedAccount = Account.builder().build(); + given(delegate.getAccountById(anyString(), any())).willReturn(Future.succeededFuture(returnedAccount)); + + // when + final Future accountFuture = enrichingApplicationSettings.getAccountById("123", timeout); + + // then + assertThat(accountFuture).isSucceeded(); + assertThat(accountFuture.result()).isSameAs(returnedAccount); + + verify(delegate).getAccountById(eq("123"), eq(timeout)); + } + + @Test + public void getAccountByIdShouldOmitMergingWhenDefaultAccountIsEmpty() { + // given + enrichingApplicationSettings = new EnrichingApplicationSettings( + delegate, + Account.builder() + .bannerCacheTtl(null) + .analyticsSamplingFactor(null) + .build()); + + final Account returnedAccount = Account.builder().build(); + given(delegate.getAccountById(anyString(), any())).willReturn(Future.succeededFuture(returnedAccount)); + + // when + final Future accountFuture = enrichingApplicationSettings.getAccountById("123", timeout); + + // then + assertThat(accountFuture).isSucceeded(); + assertThat(accountFuture.result()).isSameAs(returnedAccount); + + verify(delegate).getAccountById(eq("123"), eq(timeout)); + } + + @Test + public void getAccountByIdShouldMergeAccountWithDefaultAccount() { + // given + enrichingApplicationSettings = new EnrichingApplicationSettings( + delegate, + Account.builder() + .bannerCacheTtl(100) + .analyticsSamplingFactor(50) + .build()); + + given(delegate.getAccountById(anyString(), any())).willReturn(Future.succeededFuture(Account.builder() + .id("123") + .videoCacheTtl(200) + .enforceCcpa(true) + .build())); + + // when + final Future accountFuture = enrichingApplicationSettings.getAccountById("123", timeout); + + // then + assertThat(accountFuture).succeededWith(Account.builder() + .id("123") + .bannerCacheTtl(100) + .videoCacheTtl(200) + .enforceCcpa(true) + .analyticsSamplingFactor(50) + .build()); + } + + @Test + public void getAccountByIdShouldReturnDefaultAccountWhenDelegateFailed() { + // given + enrichingApplicationSettings = new EnrichingApplicationSettings( + delegate, + Account.builder() + .bannerCacheTtl(100) + .analyticsSamplingFactor(50) + .build()); + + given(delegate.getAccountById(anyString(), any())).willReturn(Future.failedFuture("Exception")); + + // when + final Future accountFuture = enrichingApplicationSettings.getAccountById("123", timeout); + + // then + assertThat(accountFuture).succeededWith(Account.builder() + .id("123") + .bannerCacheTtl(100) + .analyticsSamplingFactor(50) + .build()); + } + + @Test + public void getAccountByIdShouldPassOnFailureWhenDefaultAccountIsEmpty() { + // given + enrichingApplicationSettings = new EnrichingApplicationSettings( + delegate, + Account.builder() + .bannerCacheTtl(null) + .analyticsSamplingFactor(null) + .build()); + + given(delegate.getAccountById(anyString(), any())).willReturn(Future.failedFuture("Exception")); + + // when + final Future accountFuture = enrichingApplicationSettings.getAccountById("123", timeout); + + // then + assertThat(accountFuture).isFailed(); + } +} diff --git a/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java index 412ae6f402e..9581f3cbb5f 100644 --- a/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java @@ -10,7 +10,10 @@ import org.mockito.junit.MockitoRule; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountAnalyticsConfig; +import org.prebid.server.settings.model.AccountBidValidationConfig; import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.BidValidationEnforcement; +import org.prebid.server.settings.model.AccountStatus; import org.prebid.server.settings.model.EnabledForRequestType; import org.prebid.server.settings.model.EnforcePurpose; import org.prebid.server.settings.model.Purpose; @@ -104,7 +107,11 @@ public void getAccountByIdShouldReturnPresentAccount() { + "defaultIntegration: 'web'," + "analyticsConfig: {" + "auction-events: {amp: 'true'}" - + "}" + + "}," + + "bidValidations: {" + + "banner-creative-max-size: 'enforce'" + + "}," + + "status: 'active'" + "}" + "]")); @@ -140,6 +147,8 @@ public void getAccountByIdShouldReturnPresentAccount() { .truncateTargetAttr(20) .defaultIntegration("web") .analyticsConfig(AccountAnalyticsConfig.of(singletonMap("amp", true))) + .bidValidations(AccountBidValidationConfig.of(BidValidationEnforcement.enforce)) + .status(AccountStatus.active) .build()); } diff --git a/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java index dcea0d72709..2cf42d4bd65 100644 --- a/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java @@ -16,6 +16,7 @@ import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.StoredResponseDataResult; +import org.prebid.server.settings.proto.response.HttpAccountsResponse; import org.prebid.server.settings.proto.response.HttpFetcherResponse; import org.prebid.server.vertx.http.HttpClient; import org.prebid.server.vertx.http.model.HttpClientResponse; @@ -23,6 +24,7 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.Collections; import java.util.HashSet; import java.util.Map; @@ -94,13 +96,90 @@ public void creationShouldFailsOnInvalidVideoEndpoint() { } @Test - public void getAccountByIdShouldReturnEmptyResult() { + public void getAccountByIdShouldReturnFetchedAccount() throws JsonProcessingException { + // given + final Account account = Account.builder() + .id("someId") + .enforceCcpa(true) + .priceGranularity("testPriceGranularity") + .build(); + HttpAccountsResponse response = HttpAccountsResponse.of(Collections.singletonMap("someId", account)); + givenHttpClientReturnsResponse(200, mapper.writeValueAsString(response)); + + // when + final Future future = httpApplicationSettings.getAccountById("someId", timeout); + + // then + assertThat(future.succeeded()).isTrue(); + assertThat(future.result().getId()).isEqualTo("someId"); + assertThat(future.result().getEnforceCcpa()).isEqualTo(true); + assertThat(future.result().getPriceGranularity()).isEqualTo("testPriceGranularity"); + + verify(httpClient).get(eq("http://stored-requests?account-ids=[\"someId\"]"), any(), + anyLong()); + } + + @Test + public void getAccountByIdShouldReturnFaildedFutureIfResponseIsNotPresent() throws JsonProcessingException { + // given + HttpAccountsResponse response = HttpAccountsResponse.of(null); + givenHttpClientReturnsResponse(200, mapper.writeValueAsString(response)); + // when - final Future future = httpApplicationSettings.getAccountById(null, null); + final Future future = httpApplicationSettings.getAccountById("notFoundId", timeout); // then assertThat(future.failed()).isTrue(); - assertThat(future.cause()).isInstanceOf(PreBidException.class).hasMessage("Not supported"); + assertThat(future.cause()) + .isInstanceOf(PreBidException.class) + .hasMessage("Account with id : notFoundId not found"); + } + + @Test + public void getAccountByIdShouldReturnErrorIdAccountNotFound() throws JsonProcessingException { + // given + HttpAccountsResponse response = HttpAccountsResponse.of(Collections.emptyMap()); + givenHttpClientReturnsResponse(200, mapper.writeValueAsString(response)); + + // when + final Future future = httpApplicationSettings.getAccountById("notExistingId", timeout); + + // then + assertThat(future.failed()).isTrue(); + assertThat(future.cause()) + .isInstanceOf(PreBidException.class) + .hasMessage("Account with id : notExistingId not found"); + } + + @Test + public void getAccountByIdShouldReturnErrorIfResponseStatusIsDifferentFromOk() { + // given + givenHttpClientReturnsResponse(400, null); + + // when + final Future future = httpApplicationSettings.getAccountById("accountId", timeout); + + // then + assertThat(future.failed()).isTrue(); + assertThat(future.cause()) + .isInstanceOf(PreBidException.class) + .hasMessage("Error fetching accounts [accountId] via http: unexpected response status 400"); + } + + @Test + public void getAccountByIdShouldReturnErrorIfResponseHasInvalidStructure() { + // given + givenHttpClientReturnsResponse(200, "not valid response"); + + // when + final Future future = httpApplicationSettings.getAccountById("accountId", timeout); + + // then + assertThat(future.failed()).isTrue(); + assertThat(future.cause()) + .isInstanceOf(PreBidException.class) + .hasMessageContaining("Error fetching accounts [accountId] via http: " + + "failed to parse response: Failed to decode:"); } @Test diff --git a/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java index a54f21e9648..804791b7a9a 100644 --- a/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java @@ -24,7 +24,10 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountAnalyticsConfig; +import org.prebid.server.settings.model.AccountBidValidationConfig; import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountStatus; +import org.prebid.server.settings.model.BidValidationEnforcement; import org.prebid.server.settings.model.EnabledForRequestType; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.settings.model.StoredResponseDataResult; @@ -58,6 +61,12 @@ public class JdbcApplicationSettingsTest extends VertxTest { private static final String JDBC_URL = "jdbc:h2:mem:test"; + private static final String SELECT_ACCOUNT_QUERY = + "SELECT uuid, price_granularity, banner_cache_ttl, video_cache_ttl, " + + "events_enabled, enforce_ccpa, tcf_config, analytics_sampling_factor, truncate_target_attr, " + + "default_integration, analytics_config, bid_validations, status " + + "FROM accounts_account where uuid = %ACCOUNT_ID% LIMIT 1"; + private static final String SELECT_QUERY = "SELECT accountId, reqid, requestData, 'request' as dataType FROM stored_requests " + "WHERE reqid IN (%REQUEST_ID_LIST%) " @@ -78,15 +87,14 @@ public class JdbcApplicationSettingsTest extends VertxTest { + "SELECT accountId, impid, impData, 'imp' as dataType FROM stored_imps2 " + "WHERE impid IN (%IMP_ID_LIST%)"; - private static final String SELECT_FROM_ONE_COLUMN_TABLE_QUERY = - "SELECT reqid FROM one_column_table WHERE reqid IN " - + "(%REQUEST_ID_LIST%)"; + private static final String SELECT_FROM_ONE_COLUMN_TABLE_QUERY = "SELECT reqid FROM one_column_table " + + "WHERE reqid IN (%REQUEST_ID_LIST%)"; - private static final String SELECT_RESPONSE_QUERY = "SELECT responseId, responseData FROM stored_responses" - + " WHERE responseId IN (%RESPONSE_ID_LIST%)"; + private static final String SELECT_RESPONSE_QUERY = "SELECT responseId, responseData FROM stored_responses " + + "WHERE responseId IN (%RESPONSE_ID_LIST%)"; - private static final String SELECT_ONE_COLUMN_RESPONSE_QUERY = "SELECT responseId FROM stored_responses" - + " WHERE responseId IN (%RESPONSE_ID_LIST%)"; + private static final String SELECT_ONE_COLUMN_RESPONSE_QUERY = "SELECT responseId FROM stored_responses " + + "WHERE responseId IN (%RESPONSE_ID_LIST%)"; private static Connection connection; @@ -107,7 +115,8 @@ public static void beforeClass() throws SQLException { + "uuid varchar(40) NOT NULL, price_granularity varchar(6), granularityMultiplier numeric(9,3), " + "banner_cache_ttl INT, video_cache_ttl INT, events_enabled BIT, enforce_ccpa BIT, " + "tcf_config varchar(512), analytics_sampling_factor INT, truncate_target_attr INT, " - + "default_integration varchar(64), analytics_config varchar(512));"); + + "default_integration varchar(64), analytics_config varchar(512), bid_validations varchar(512), " + + "status varchar(25));"); connection.createStatement().execute("CREATE TABLE s2sconfig_config (id SERIAL PRIMARY KEY, uuid varchar(40) " + "NOT NULL, config varchar(512));"); connection.createStatement().execute("CREATE TABLE stored_requests (id SERIAL PRIMARY KEY, " @@ -125,10 +134,12 @@ public static void beforeClass() throws SQLException { + "varchar(40) NOT NULL);"); connection.createStatement().execute("insert into accounts_account " + "(uuid, price_granularity, banner_cache_ttl, video_cache_ttl, events_enabled, enforce_ccpa, " - + "tcf_config, analytics_sampling_factor, truncate_target_attr, default_integration, analytics_config) " + + "tcf_config, analytics_sampling_factor, truncate_target_attr, default_integration, analytics_config, " + + "bid_validations, status) " + "values ('1001','med', 100, 100, TRUE, TRUE, '{\"enabled\": true, " + "\"integration-enabled\": {\"amp\": true, \"app\": true, \"video\": true, \"web\": true}}', 1, 0, " - + "'web', '{\"auction-events\": {\"amp\": true}}');"); + + "'web', '{\"auction-events\": {\"amp\": true}}', '{\"banner-creative-max-size\": \"enforce\"}', " + + "'active');"); connection.createStatement().execute( "insert into s2sconfig_config (uuid, config) values ('adUnitConfigId', 'config');"); connection.createStatement().execute( @@ -163,7 +174,12 @@ public void setUp() { vertx = Vertx.vertx(); clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); timeout = new TimeoutFactory(clock).create(5000L); - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, SELECT_QUERY, SELECT_QUERY, + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_QUERY, + SELECT_QUERY, SELECT_RESPONSE_QUERY); } @@ -195,6 +211,8 @@ public void getAccountByIdShouldReturnAccountWithAllFieldsPopulated(TestContext .truncateTargetAttr(0) .defaultIntegration("web") .analyticsConfig(AccountAnalyticsConfig.of(singletonMap("amp", true))) + .bidValidations(AccountBidValidationConfig.of(BidValidationEnforcement.enforce)) + .status(AccountStatus.active) .build()); async.complete(); })); @@ -214,6 +232,26 @@ public void getAccountByIdShouldFailIfAccountNotFound(TestContext context) { })); } + @Test + public void getAccountByIdShouldTolerateAccountWithoutStatusDefined(TestContext context) throws SQLException { + // given + connection.createStatement() + .execute("insert into accounts_account (uuid, status) values ('1002', NULL);"); + + // when + final Future future = jdbcApplicationSettings.getAccountById("1002", timeout); + + // then + final Async async = context.async(); + future.setHandler(context.asyncAssertSuccess(account -> { + assertThat(account).isEqualTo(Account.builder() + .id("1002") + .status(null) + .build()); + async.complete(); + })); + } + @Test public void getAdUnitConfigByIdShouldReturnConfig(TestContext context) { // when @@ -304,8 +342,13 @@ public void getVideoStoredDataShouldReturnExpectedResult(TestContext context) { @Test public void getVideoStoredDataShouldReturnStoredRequests(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, SELECT_UNION_QUERY, - SELECT_UNION_QUERY, SELECT_RESPONSE_QUERY); + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_UNION_QUERY, + SELECT_UNION_QUERY, + SELECT_RESPONSE_QUERY); // when final Future storedRequestResultFuture = @@ -332,8 +375,13 @@ public void getVideoStoredDataShouldReturnStoredRequests(TestContext context) { @Test public void getStoredDataUnionSelectByIdShouldReturnStoredRequests(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, SELECT_UNION_QUERY, - SELECT_UNION_QUERY, SELECT_RESPONSE_QUERY); + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_UNION_QUERY, + SELECT_UNION_QUERY, + SELECT_RESPONSE_QUERY); // when final Future storedRequestResultFuture = @@ -360,8 +408,13 @@ public void getStoredDataUnionSelectByIdShouldReturnStoredRequests(TestContext c @Test public void getAmpStoredDataUnionSelectByIdShouldReturnStoredRequests(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, SELECT_UNION_QUERY, - SELECT_UNION_QUERY, SELECT_RESPONSE_QUERY); + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_UNION_QUERY, + SELECT_UNION_QUERY, + SELECT_RESPONSE_QUERY); // when final Future storedRequestResultFuture = @@ -430,8 +483,13 @@ public void getAmpStoredDataShouldReturnResultWithErrorIfNoStoredRequestFound(Te @Test public void getStoredDataShouldReturnErrorIfResultContainsLessColumnsThanExpected(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, - SELECT_FROM_ONE_COLUMN_TABLE_QUERY, SELECT_FROM_ONE_COLUMN_TABLE_QUERY, SELECT_RESPONSE_QUERY); + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_FROM_ONE_COLUMN_TABLE_QUERY, + SELECT_FROM_ONE_COLUMN_TABLE_QUERY, + SELECT_RESPONSE_QUERY); // when final Future storedRequestResultFuture = @@ -450,9 +508,13 @@ public void getStoredDataShouldReturnErrorIfResultContainsLessColumnsThanExpecte @Test public void getAmpStoredDataShouldReturnErrorIfResultContainsLessColumnsThanExpected(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, SELECT_FROM_ONE_COLUMN_TABLE_QUERY, - SELECT_FROM_ONE_COLUMN_TABLE_QUERY, SELECT_RESPONSE_QUERY); + SELECT_FROM_ONE_COLUMN_TABLE_QUERY, + SELECT_RESPONSE_QUERY); // when final Future storedRequestResultFuture = @@ -552,7 +614,12 @@ public void getStoredResponseShouldReturnResultWithErrorIfNotAllStoredResponsesW @Test public void getStoredResponseShouldReturnErrorIfResultContainsLessColumnsThanExpected(TestContext context) { // given - jdbcApplicationSettings = new JdbcApplicationSettings(jdbcClient(), jacksonMapper, SELECT_QUERY, SELECT_QUERY, + jdbcApplicationSettings = new JdbcApplicationSettings( + jdbcClient(), + jacksonMapper, + SELECT_ACCOUNT_QUERY, + SELECT_QUERY, + SELECT_QUERY, SELECT_ONE_COLUMN_RESPONSE_QUERY); // when diff --git a/src/test/java/org/prebid/server/settings/service/JdbcPeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/JdbcPeriodicRefreshServiceTest.java index 2110ecd80a1..5e49e305238 100644 --- a/src/test/java/org/prebid/server/settings/service/JdbcPeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/JdbcPeriodicRefreshServiceTest.java @@ -11,6 +11,8 @@ import org.mockito.junit.MockitoRule; import org.mockito.stubbing.Answer; import org.prebid.server.execution.TimeoutFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.CacheNotificationListener; import org.prebid.server.settings.model.StoredDataResult; import org.prebid.server.vertx.jdbc.JdbcClient; @@ -24,7 +26,6 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; @@ -37,21 +38,22 @@ public class JdbcPeriodicRefreshServiceTest { - private static TimeoutFactory timeoutFactory = new TimeoutFactory( - Clock.fixed(Instant.now(), ZoneId.systemDefault())); - @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private CacheNotificationListener cacheNotificationListener; @Mock + private Vertx vertx; + @Mock private JdbcClient jdbcClient; + private final Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + private final TimeoutFactory timeoutFactory = new TimeoutFactory(clock); @Mock - private Vertx vertx; + private Metrics metrics; - private Map expectedRequests = singletonMap("id1", "value1"); - private Map expectedImps = singletonMap("id2", "value2"); + private final Map expectedRequests = singletonMap("id1", "value1"); + private final Map expectedImps = singletonMap("id2", "value2"); @Before public void setUp() { @@ -66,31 +68,10 @@ public void setUp() { .willReturn(Future.succeededFuture(updateResult)); } - @Test - public void creationShouldFailOnNullArgumentsAndBlankQuery() { - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - null, null, null, 0, null, null, null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, null, null, 0, null, null, null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, null, 0, null, null, null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, jdbcClient, 0, null, null, null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, jdbcClient, 0, "init_query", null, null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, jdbcClient, 0, "init_query", "update_query", null, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, jdbcClient, 0, " ", null, timeoutFactory, 0)); - assertThatNullPointerException().isThrownBy(() -> createAndInitService( - cacheNotificationListener, vertx, jdbcClient, 0, "init_query", " ", timeoutFactory, 0)); - } - @Test public void shouldCallSaveWithExpectedParameters() { // when - createAndInitService(cacheNotificationListener, vertx, jdbcClient, 1000, - "init_query", "update_query", timeoutFactory, 2000); + createAndInitService(1000); // then verify(cacheNotificationListener).save(expectedRequests, expectedImps); @@ -103,8 +84,7 @@ public void shouldCallInvalidateAndSaveWithExpectedParameters() { .willAnswer(withSelfAndPassObjectToHandler(1L)); // when - createAndInitService(cacheNotificationListener, vertx, jdbcClient, 1000, - "init_query", "update_query", timeoutFactory, 2000); + createAndInitService(1000); // then verify(cacheNotificationListener).save(expectedRequests, expectedImps); @@ -119,8 +99,7 @@ public void initializeShouldMakeOneInitialRequestAndTwoScheduledRequestsWithPara .willAnswer(withSelfAndPassObjectToHandler(1L, 2L)); // when - createAndInitService(cacheNotificationListener, vertx, jdbcClient, 1000, - "init_query", "update_query", timeoutFactory, 2000); + createAndInitService(1000); // then verify(jdbcClient).executeQuery(eq("init_query"), eq(emptyList()), any(), any()); @@ -130,21 +109,54 @@ public void initializeShouldMakeOneInitialRequestAndTwoScheduledRequestsWithPara @Test public void initializeShouldMakeOnlyOneInitialRequestIfRefreshPeriodIsNegative() { // when - createAndInitService(cacheNotificationListener, vertx, jdbcClient, -1, - "init_query", "update_query", timeoutFactory, 2000); + createAndInitService(-1); // then verify(vertx, never()).setPeriodic(anyLong(), any()); verify(jdbcClient).executeQuery(anyString(), anyList(), any(), any()); } - private static void createAndInitService(CacheNotificationListener cacheNotificationListener, - Vertx vertx, JdbcClient jdbcClient, long refresh, - String query, String updateQuery, - TimeoutFactory timeoutFactory, long timeout) { - final JdbcPeriodicRefreshService jdbcPeriodicRefreshService = - new JdbcPeriodicRefreshService(cacheNotificationListener, vertx, jdbcClient, refresh, - query, updateQuery, timeoutFactory, timeout); + @Test + public void shouldUpdateTimerMetric() { + // when + createAndInitService(1000); + + // then + verify(metrics).updateSettingsCacheRefreshTime( + eq(MetricName.stored_request), eq(MetricName.initialize), anyLong()); + } + + @Test + public void shouldUpdateTimerAndErrorMetric() { + // given + given(jdbcClient.executeQuery(eq("init_query"), anyList(), any(), any())) + .willReturn(Future.failedFuture("Query error")); + + // when + createAndInitService(1000); + + // then + verify(metrics).updateSettingsCacheRefreshTime( + eq(MetricName.stored_request), eq(MetricName.initialize), anyLong()); + verify(metrics).updateSettingsCacheRefreshErrorMetric( + eq(MetricName.stored_request), eq(MetricName.initialize)); + } + + private void createAndInitService(long refresh) { + + final JdbcPeriodicRefreshService jdbcPeriodicRefreshService = new JdbcPeriodicRefreshService( + "init_query", + "update_query", + refresh, + 2000, + MetricName.stored_request, + cacheNotificationListener, + vertx, + jdbcClient, + timeoutFactory, + metrics, + clock); + jdbcPeriodicRefreshService.initialize(); } diff --git a/src/test/java/org/prebid/server/util/HttpUtilTest.java b/src/test/java/org/prebid/server/util/HttpUtilTest.java index 77242c33ca2..889b402b4f6 100644 --- a/src/test/java/org/prebid/server/util/HttpUtilTest.java +++ b/src/test/java/org/prebid/server/util/HttpUtilTest.java @@ -26,16 +26,6 @@ public class HttpUtilTest { @Mock private RoutingContext routingContext; - @Test - public void isSafariShouldReturnTrue() { - assertThat(HttpUtil.isSafari("Useragent with Safari browser and AppleWebKit built-in.")).isTrue(); - } - - @Test - public void isSafariShouldReturnFalse() { - assertThat(HttpUtil.isSafari("Useragent with Safari browser but Chromium forked by.")).isFalse(); - } - @Test public void validateUrlShouldFailOnInvalidUrl() { // when and then diff --git a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java index 5f2331643c7..9a7478ffaeb 100644 --- a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java @@ -45,10 +45,10 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRegs; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchain; import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting; import org.prebid.server.proto.openrtb.ext.request.ExtSite; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserDigiTrust; import org.prebid.server.proto.openrtb.ext.request.ExtUserEid; import org.prebid.server.proto.openrtb.ext.request.ExtUserEidUid; import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid; @@ -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 @@ -1245,7 +1291,6 @@ public void validateShouldReturnValidationMessageWhenPrebidBuyerIdsContainsNoVal .user(User.builder() .ext(ExtUser.builder() .prebid(ExtUserPrebid.of(emptyMap())) - .digitrust(ExtUserDigiTrust.of(null, null, 0)) .build()) .build()) .build(); @@ -1506,7 +1551,6 @@ public void validateShouldReturnValidationMessageWhenPrebidBuyerIdsContainsUnkno .user(User.builder() .ext(ExtUser.builder() .prebid(ExtUserPrebid.of(singletonMap("unknown-bidder", "42"))) - .digitrust(ExtUserDigiTrust.of(null, null, 0)) .build()) .build()) .build(); @@ -1530,7 +1574,6 @@ public void validateShouldNotReturnAnyErrorInValidationResultWhenPrebidBuyerIdIs .user(User.builder() .ext(ExtUser.builder() .prebid(ExtUserPrebid.of(singletonMap("unknown-bidder", "42"))) - .digitrust(ExtUserDigiTrust.of(null, null, 0)) .build()) .build()) .build(); @@ -1549,7 +1592,6 @@ public void validateShouldNotReturnAnyErrorInValidationResultWhenPrebidBuyerIdIs .user(User.builder() .ext(ExtUser.builder() .prebid(ExtUserPrebid.of(singletonMap("rubicon", "42"))) - .digitrust(ExtUserDigiTrust.of(null, null, 0)) .build()) .build()) .build(); @@ -1561,25 +1603,6 @@ public void validateShouldNotReturnAnyErrorInValidationResultWhenPrebidBuyerIdIs assertThat(result.getErrors()).isEmpty(); } - @Test - public void validateShouldReturnValidationMessageWhenDigiTrustPrefNotEqualZero() { - // given; - final BidRequest bidRequest = validBidRequestBuilder() - .user(User.builder() - .ext(ExtUser.builder() - .digitrust(ExtUserDigiTrust.of(null, null, 1)) - .build()) - .build()) - .build(); - - // when - final ValidationResult result = requestValidator.validate(bidRequest); - - // then - assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.user contains a digitrust object that is not valid"); - } - @Test public void validateShouldReturnValidationMessageWhenEidsIsEmpty() { // given @@ -1677,7 +1700,7 @@ public void validateShouldReturnValidationMessageWhenEidUidIdIsMissing() { } @Test - public void validateShouldReturnValidationMessageWhenEidSourceIsNotUnique() { + public void validateShouldReturnErrorWhenEidSourceIsNotUnique() { // given final BidRequest bidRequest = validBidRequestBuilder() .user(User.builder() @@ -1695,8 +1718,7 @@ public void validateShouldReturnValidationMessageWhenEidSourceIsNotUnique() { final ValidationResult result = requestValidator.validate(bidRequest); // then - assertThat(result.getErrors()).hasSize(1) - .containsOnly("request.user.ext.eids must contain unique sources"); + assertThat(result.getErrors()).containsExactly("request.user.ext.eids must contain unique sources"); } @Test @@ -1772,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 @@ -1785,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 @@ -2466,6 +2488,27 @@ public void validateShouldEmptyValidationMessagesWhenBidderIsKnownAliasForCoreBi assertThat(result.getErrors()).isEmpty(); } + @Test + public void validateShouldReturnValidationMessageWhenMultipleSchainsForSameBidder() { + // given + final BidRequest bidRequest = validBidRequestBuilder() + .ext(ExtRequest.of( + ExtRequestPrebid.builder() + .schains(asList( + ExtRequestPrebidSchain.of(asList("bidder1", "bidder2"), null), + ExtRequestPrebidSchain.of(asList("bidder2", "bidder3"), null))) + .build())) + .build(); + + // when + final ValidationResult result = requestValidator.validate(bidRequest); + + // then + assertThat(result.getErrors()) + .containsOnly("request.ext.prebid.schains contains multiple schains for bidder bidder2; " + + "it must contain no more than one per bidder."); + } + @Test public void validateShouldReturnValidationMessageWhenRequestHaveDuplicatedImpIds() { // given @@ -2521,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/java/org/prebid/server/validation/ResponseBidValidatorTest.java b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java index 77e2e38aa58..61428c1e16e 100644 --- a/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/ResponseBidValidatorTest.java @@ -1,86 +1,142 @@ package org.prebid.server.validation; +import com.iab.openrtb.request.Banner; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Format; +import com.iab.openrtb.request.Imp; import com.iab.openrtb.response.Bid; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.BidderAliases; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountBidValidationConfig; import org.prebid.server.validation.model.ValidationResult; import java.math.BigDecimal; import java.util.function.Function; +import static java.util.Collections.singletonList; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.prebid.server.settings.model.BidValidationEnforcement.enforce; +import static org.prebid.server.settings.model.BidValidationEnforcement.skip; +import static org.prebid.server.settings.model.BidValidationEnforcement.warn; -public class ResponseBidValidatorTest { +public class ResponseBidValidatorTest extends VertxTest { + + private static final String BIDDER_NAME = "bidder"; + private static final String ACCOUNT_ID = "account"; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private Metrics metrics; private ResponseBidValidator responseBidValidator; + @Mock + private BidderAliases bidderAliases; + @Before public void setUp() { - responseBidValidator = new ResponseBidValidator(); + responseBidValidator = new ResponseBidValidator(enforce, enforce, metrics); + + given(bidderAliases.resolveBidder(anyString())).willReturn(BIDDER_NAME); } @Test public void validateShouldFailedIfBidderBidCurrencyIsIncorrect() { assertThatIllegalArgumentException().isThrownBy(() -> - responseBidValidator.validate(BidderBid.of( - Bid.builder() - .id("bidId1") - .impid("impId1") - .crid("crid1") - .price(BigDecimal.ONE).build(), - null, - "USDD"))); + responseBidValidator.validate( + BidderBid.of( + Bid.builder() + .id("bidId1") + .impid("impId1") + .crid("crid1") + .price(BigDecimal.ONE) + .build(), + null, + "USDD"), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases)); } @Test - public void validateShouldFailedIfMissingBid() { - final ValidationResult result = responseBidValidator.validate(BidderBid.of(null, null, "USD")); + public void validateShouldFailIfMissingBid() { + // when + final ValidationResult result = responseBidValidator.validate( + BidderBid.of(null, null, "USD"), BIDDER_NAME, givenAuctionContext(), bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Empty bid object submitted."); + // then + assertThat(result.getErrors()).containsOnly("Empty bid object submitted."); } @Test - public void validateShouldFailedIfBidHasNoId() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.id(null))); + public void validateShouldFailIfBidHasNoId() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.id(null)), BIDDER_NAME, givenAuctionContext(), bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Bid missing required field 'id'"); + // then + assertThat(result.getErrors()).containsOnly("Bid missing required field 'id'"); } @Test - public void validateShouldFailedIfBidHasNoImpId() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.impid(null))); + public void validateShouldFailIfBidHasNoImpId() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.impid(null)), BIDDER_NAME, givenAuctionContext(), bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Bid \"bidId1\" missing required field 'impid'"); + // then + assertThat(result.getErrors()).containsOnly("Bid \"bidId1\" missing required field 'impid'"); } @Test - public void validateShouldFailedIfBidHasNoPrice() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.price(null))); + public void validateShouldFailIfBidHasNoPrice() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.price(null)), BIDDER_NAME, givenAuctionContext(), bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Bid \"bidId1\" does not contain a 'price'"); + // then + assertThat(result.getErrors()).hasSize(1).containsOnly("Bid \"bidId1\" does not contain a 'price'"); } @Test - public void validateShouldFailedIfBidHasNegativePrice() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.price( - BigDecimal.valueOf(-1)))); + public void validateShouldFailIfBidHasNegativePrice() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.price(BigDecimal.valueOf(-1))), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Bid \"bidId1\" `price `has negative value"); + // then + assertThat(result.getErrors()).hasSize(1).containsOnly("Bid \"bidId1\" `price `has negative value"); } @Test public void validateShouldFailedIfNonDealBidHasZeroPrice() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.price( - BigDecimal.valueOf(0)))); + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.price(BigDecimal.valueOf(0))), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); assertThat(result.getErrors()).hasSize(1) .containsOnly("Non deal bid \"bidId1\" has 0 price"); @@ -88,37 +144,346 @@ public void validateShouldFailedIfNonDealBidHasZeroPrice() { @Test public void validateShouldSuccessForDealZeroPriceBid() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.price( - BigDecimal.valueOf(0)).dealid("dealId"))); + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.price(BigDecimal.valueOf(0)).dealid("dealId")), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); assertThat(result.hasErrors()).isFalse(); } @Test - public void validateShouldFailedIfBidHasNoCrid() { - final ValidationResult result = responseBidValidator.validate(givenBid(builder -> builder.crid(null))); + public void validateShouldFailIfBidHasNoCrid() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.crid(null)), BIDDER_NAME, givenAuctionContext(), bidderAliases); - assertThat(result.getErrors()).hasSize(1) - .containsOnly("Bid \"bidId1\" missing creative ID"); + // then + assertThat(result.getErrors()).containsOnly("Bid \"bidId1\" missing creative ID"); + } + + @Test + public void validateShouldFailIfBannerBidHasNoWidthAndHeight() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.w(null).h(null)), BIDDER_NAME, givenAuctionContext(), bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has 'w' and 'h' that are not valid. Bid dimensions: 'nullxnull'"); + } + + @Test + public void validateShouldFailIfBannerBidWidthIsGreaterThanImposedByImp() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.w(150).h(150)), BIDDER_NAME, givenAuctionContext(), bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has 'w' and 'h' that are not valid. Bid dimensions: '150x150'"); + } + + @Test + public void validateShouldFailIfBannerBidHeightIsGreaterThanImposedByImp() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.w(50).h(250)), BIDDER_NAME, givenAuctionContext(), bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has 'w' and 'h' that are not valid. Bid dimensions: '50x250'"); + } + + @Test + public void validateShouldReturnSuccessIfNonBannerBidHasAnySize() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(BidType.video, builder -> builder.w(3).h(3)), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + } + + @Test + public void validateShouldReturnSuccessIfBannerBidHasInvalidSizeButAccountDoesNotEnforceValidation() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.w(150).h(150)), + BIDDER_NAME, + givenAuctionContext( + givenAccount(builder -> builder.bidValidations(AccountBidValidationConfig.of(skip)))), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + } + + @Test + public void validateShouldFailIfBidHasNoCorrespondingImp() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.impid("nonExistentsImpid")), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has no corresponding imp in request"); + } + + @Test + public void validateShouldFailIfBidHasInsecureMarkerInCreativeInSecureContext() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has insecure creative but should be in secure context"); + } + + @Test + public void validateShouldFailIfBidHasInsecureEncodedMarkerInCreativeInSecureContext() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("http%3A//site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has insecure creative but should be in secure context"); + } + + @Test + public void validateShouldFailIfBidHasNoSecureMarkersInCreativeInSecureContext() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("//site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + assertThat(result.getErrors()) + .containsOnly("Bid \"bidId1\" has insecure creative but should be in secure context"); + } + + @Test + public void validateShouldReturnSuccessIfBidHasInsecureCreativeInInsecureContext() { + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); } @Test public void validateShouldReturnSuccessfulResultForValidBid() { - final ValidationResult result = responseBidValidator.validate(givenBid(identity())); + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(identity()), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + } + @Test + public void validateShouldReturnSuccessIfBannerSizeValidationNotEnabled() { + // given + responseBidValidator = new ResponseBidValidator(skip, enforce, metrics); + + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(identity()), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + } + + @Test + public void validateShouldReturnSuccessWithWarningIfBannerSizeEnforcementIsWarn() { + // given + responseBidValidator = new ResponseBidValidator(warn, enforce, metrics); + + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.w(null).h(null)), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + assertThat(result.getWarnings()) + .containsOnly("Bid \"bidId1\" has 'w' and 'h' that are not valid. Bid dimensions: 'nullxnull'"); + } + + @Test + public void validateShouldReturnSuccessIfSecureMarkupValidationNotEnabled() { + // given + responseBidValidator = new ResponseBidValidator(enforce, skip, metrics); + + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then assertThat(result.hasErrors()).isFalse(); } - private static BidderBid givenBid(Function bidCustomizer, BidType mediaType) { + @Test + public void validateShouldReturnSuccessWithWarningIfSecureMarkupEnforcementIsWarn() { + // given + responseBidValidator = new ResponseBidValidator(enforce, warn, metrics); + + // when + final ValidationResult result = responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + assertThat(result.hasErrors()).isFalse(); + assertThat(result.getWarnings()) + .containsOnly("Bid \"bidId1\" has insecure creative but should be in secure context"); + } + + @Test + public void validateShouldIncrementSizeValidationErrMetrics() { + // when + responseBidValidator.validate( + givenBid(builder -> builder.w(150).h(200)), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + verify(metrics).updateSizeValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); + } + + @Test + public void validateShouldIncrementSizeValidationWarnMetrics() { + // given + responseBidValidator = new ResponseBidValidator(warn, warn, metrics); + + // when + responseBidValidator.validate( + givenBid(builder -> builder.w(150).h(200)), + BIDDER_NAME, + givenAuctionContext(), + bidderAliases); + + // then + verify(metrics).updateSizeValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.warn); + } + + @Test + public void validateShouldIncrementSecureValidationErrMetrics() { + // when + responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + verify(metrics).updateSecureValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.err); + } + + @Test + public void validateShouldIncrementSecureValidationWarnMetrics() { + // given + responseBidValidator = new ResponseBidValidator(warn, warn, metrics); + + // when + responseBidValidator.validate( + givenBid(builder -> builder.adm("http://site.com/creative.jpg")), + BIDDER_NAME, + givenAuctionContext(givenBidRequest(builder -> builder.secure(1))), + bidderAliases); + + // then + verify(metrics).updateSecureValidationMetrics(BIDDER_NAME, ACCOUNT_ID, MetricName.warn); + } + + private static BidderBid givenBid(Function bidCustomizer) { + return givenBid(BidType.banner, bidCustomizer); + } + + private static BidderBid givenBid(BidType type, Function bidCustomizer) { final Bid.BidBuilder bidBuilder = Bid.builder() .id("bidId1") .impid("impId1") .crid("crid1") + .w(1) + .h(1) + .adm("https://site.com/creative.jpg") .price(BigDecimal.ONE); - return BidderBid.of(bidCustomizer.apply(bidBuilder).build(), mediaType, "USD"); + + return BidderBid.of(bidCustomizer.apply(bidBuilder).build(), type, "USD"); } - private static BidderBid givenBid(Function bidCustomizer) { - return givenBid(bidCustomizer, null); + private static AuctionContext givenAuctionContext(BidRequest bidRequest, Account account) { + return AuctionContext.builder() + .account(account) + .bidRequest(bidRequest) + .build(); + } + + private static AuctionContext givenAuctionContext(BidRequest bidRequest) { + return givenAuctionContext(bidRequest, givenAccount()); + } + + private static AuctionContext givenAuctionContext(Account account) { + return givenAuctionContext(givenBidRequest(identity()), account); + } + + private static AuctionContext givenAuctionContext() { + return givenAuctionContext(givenBidRequest(identity()), givenAccount()); + } + + private static BidRequest givenBidRequest(Function impCustomizer) { + final Imp.ImpBuilder impBuilder = Imp.builder() + .id("impId1") + .banner(Banner.builder() + .format(singletonList(Format.builder().w(100).h(200).build())) + .build()); + + return BidRequest.builder() + .imp(singletonList(impCustomizer.apply(impBuilder).build())) + .build(); + } + + private static Account givenAccount() { + return givenAccount(identity()); + } + + private static Account givenAccount(Function accountCustomizer) { + return accountCustomizer.apply(Account.builder().id(ACCOUNT_ID)).build(); } } diff --git a/src/test/resources/org/prebid/server/it/amp/test-appnexus-bid-request.json b/src/test/resources/org/prebid/server/it/amp/test-appnexus-bid-request.json index c60367a8448..b4c9ca2056b 100644 --- a/src/test/resources/org/prebid/server/it/amp/test-appnexus-bid-request.json +++ b/src/test/resources/org/prebid/server/it/amp/test-appnexus-bid-request.json @@ -26,7 +26,7 @@ } ], "site": { - "domain": "example.com", + "domain": "google.com", "page": "https://google.com", "publisher": { "id": "accountId" diff --git a/src/test/resources/org/prebid/server/it/amp/test-rubicon-bid-request.json b/src/test/resources/org/prebid/server/it/amp/test-rubicon-bid-request.json index 980408f6332..c515f2c3f85 100644 --- a/src/test/resources/org/prebid/server/it/amp/test-rubicon-bid-request.json +++ b/src/test/resources/org/prebid/server/it/amp/test-rubicon-bid-request.json @@ -38,7 +38,7 @@ } ], "site": { - "domain": "example.com", + "domain": "google.com", "page": "https://google.com", "publisher": { "ext": { diff --git a/src/test/resources/org/prebid/server/it/auction/adform/test-auction-adform-request.json b/src/test/resources/org/prebid/server/it/auction/adform/test-auction-adform-request.json index 0413c04ffe4..0bfb9328530 100644 --- a/src/test/resources/org/prebid/server/it/auction/adform/test-auction-adform-request.json +++ b/src/test/resources/org/prebid/server/it/auction/adform/test-auction-adform-request.json @@ -36,12 +36,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-request.json b/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-request.json index 2e66d02422d..23578e05f89 100644 --- a/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-request.json +++ b/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-request.json @@ -50,12 +50,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-response.json b/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-response.json index fec15683456..5fd988bf1ac 100644 --- a/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-response.json +++ b/src/test/resources/org/prebid/server/it/auction/conversant/test-auction-conversant-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ conversant.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode10\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250,\"pos\":28},\"displaymanager\":\"prebid-s2s\",\"displaymanagerver\":\"1.0.1\",\"tagid\":\"tagId1\",\"bidfloor\":7.32,\"secure\":42}],\"site\":{\"id\":\"siteId1\",\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"mobile\":64},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"CV-UID\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode10\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250,\"pos\":28},\"displaymanager\":\"prebid-s2s\",\"displaymanagerver\":\"1.0.1\",\"tagid\":\"tagId1\",\"bidfloor\":7.32,\"secure\":42}],\"site\":{\"id\":\"siteId1\",\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"mobile\":64},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"CV-UID\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId10\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode10\",\"price\":5.78,\"adm\":\"adm10\",\"crid\":\"crid10\",\"dealid\":\"dealId10\",\"w\":300,\"h\":250}],\"seat\":\"seatId10\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/conversant/test-conversant-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/conversant/test-conversant-bid-request-1.json index a7bedd264ce..685c6e840e7 100644 --- a/src/test/resources/org/prebid/server/it/auction/conversant/test-conversant-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/conversant/test-conversant-bid-request-1.json @@ -38,12 +38,7 @@ "user": { "buyeruid": "CV-UID", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-request.json b/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-request.json index 23849c265da..c87e2cd5420 100644 --- a/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-request.json +++ b/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-request.json @@ -61,12 +61,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-response.json b/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-response.json index 33df9613410..64127f4e822 100644 --- a/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-response.json +++ b/src/test/resources/org/prebid/server/it/auction/districtm/test-auction-districtm-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ appnexus.exchange_uri }}?member_id=member1", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode4\",\"video\":{\"mimes\":[\"mimes\"],\"minduration\":20,\"maxduration\":60,\"protocols\":[1],\"w\":300,\"h\":250,\"startdelay\":5,\"playbackmethod\":[1]},\"tagid\":\"invCode1\",\"bidfloor\":1.0,\"ext\":{\"appnexus\":{\"placement_id\":9848285,\"keywords\":\"k1=v1,k1=v2\",\"traffic_source_code\":\"trafficSourceCode1\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"id\":\"12345\",\"buyeruid\":\"12345\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode4\",\"video\":{\"mimes\":[\"mimes\"],\"minduration\":20,\"maxduration\":60,\"protocols\":[1],\"w\":300,\"h\":250,\"startdelay\":5,\"playbackmethod\":[1]},\"tagid\":\"invCode1\",\"bidfloor\":1.0,\"ext\":{\"appnexus\":{\"placement_id\":9848285,\"keywords\":\"k1=v1,k1=v2\",\"traffic_source_code\":\"trafficSourceCode1\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"id\":\"12345\",\"buyeruid\":\"12345\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId4\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode4\",\"price\":5.78,\"adm\":\"adm4\",\"crid\":\"crid4\",\"dealid\":\"dealId4\",\"w\":300,\"h\":250,\"ext\":{\"appnexus\":{\"bid_ad_type\":1}}}],\"seat\":\"seatId4\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/districtm/test-districtm-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/districtm/test-districtm-bid-request-1.json index 5acca6e19ea..18f60cc35b3 100644 --- a/src/test/resources/org/prebid/server/it/auction/districtm/test-districtm-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/districtm/test-districtm-bid-request-1.json @@ -46,12 +46,7 @@ "id": "12345", "buyeruid": "12345", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-request.json b/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-request.json index 1df884d2720..c3ba263fd57 100644 --- a/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-request.json +++ b/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-request.json @@ -35,12 +35,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-response.json b/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-response.json index 398840b9c3e..474aaa7c4cb 100644 --- a/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-response.json +++ b/src/test/resources/org/prebid/server/it/auction/ix/test-auction-ix-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ ix.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode7\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"adUnitCode7\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"486\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"IE-UID\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode7\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"adUnitCode7\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"486\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"IE-UID\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId7\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode7\",\"price\":5.78,\"adm\":\"adm7\",\"crid\":\"crid7\",\"dealid\":\"dealId7\",\"w\":300,\"h\":250}],\"seat\":\"seatId7\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/ix/test-ix-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/ix/test-ix-bid-request-1.json index 311b3bd37fd..5e6667648fe 100644 --- a/src/test/resources/org/prebid/server/it/auction/ix/test-ix-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/ix/test-ix-bid-request-1.json @@ -34,12 +34,7 @@ "user": { "buyeruid": "IE-UID", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-request.json b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-request.json index c05eadda14c..eb81d8988b4 100644 --- a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-request.json +++ b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-request.json @@ -35,12 +35,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-response.json b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-response.json index 2ba37a52718..563979c2a74 100644 --- a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-response.json +++ b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-auction-lifestreet-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ lifestreet.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode8\",\"banner\":{\"w\":300,\"h\":250},\"tagid\":\"slot.tag1\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"LS-UID\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode8\",\"banner\":{\"w\":300,\"h\":250},\"tagid\":\"slot.tag1\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"LS-UID\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId8\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode8\",\"price\":5.78,\"adm\":\"adm8\",\"crid\":\"crid8\",\"dealid\":\"dealId8\",\"w\":300,\"h\":250}],\"seat\":\"seatId8\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-lifestreet-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-lifestreet-bid-request-1.json index b6351131bbe..fd784a16554 100644 --- a/src/test/resources/org/prebid/server/it/auction/lifestreet/test-lifestreet-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/lifestreet/test-lifestreet-bid-request-1.json @@ -25,12 +25,7 @@ "user": { "buyeruid": "LS-UID", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-request.json b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-request.json index 2befcd7a7e4..9f66e808813 100644 --- a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-request.json +++ b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-request.json @@ -36,12 +36,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-response.json b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-response.json index e8c3ab7e747..b092e81019c 100644 --- a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-response.json +++ b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-auction-pubmatic-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ pubmatic.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode9\",\"banner\":{\"format\":[{\"w\":200,\"h\":150}],\"w\":300,\"h\":250},\"tagid\":\"slot9\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId9\",\"domain\":\"example.com\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"PM-UID\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode9\",\"banner\":{\"format\":[{\"w\":200,\"h\":150}],\"w\":300,\"h\":250},\"tagid\":\"slot9\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId9\",\"domain\":\"example.com\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"PM-UID\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId9\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode9\",\"price\":5.78,\"adm\":\"adm9\",\"crid\":\"crid9\",\"dealid\":\"dealId9\",\"w\":300,\"h\":250}],\"seat\":\"seatId9\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-pubmatic-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-pubmatic-bid-request-1.json index f42d82b05d8..dfe1ffb7858 100644 --- a/src/test/resources/org/prebid/server/it/auction/pubmatic/test-pubmatic-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/pubmatic/test-pubmatic-bid-request-1.json @@ -35,12 +35,7 @@ "user": { "buyeruid": "PM-UID", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-request.json b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-request.json index ef099a0f325..d06c351a753 100644 --- a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-request.json +++ b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-request.json @@ -37,12 +37,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-response.json b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-response.json index 1cc5f52f719..08ec48a4ed4 100644 --- a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-response.json +++ b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-auction-pulsepoint-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ pulsepoint.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode6\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"456\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"123\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"PP-UID\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode6\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"456\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"123\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"PP-UID\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId6\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode6\",\"price\":5.78,\"adm\":\"adm6\",\"crid\":\"crid6\",\"dealid\":\"dealId6\",\"w\":300,\"h\":250}],\"seat\":\"seatId6\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-pulsepoint-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-pulsepoint-bid-request-1.json index 04973cac104..4157690bb13 100644 --- a/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-pulsepoint-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/pulsepoint/test-pulsepoint-bid-request-1.json @@ -34,12 +34,7 @@ "user": { "buyeruid": "PP-UID", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-appnexus-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-appnexus-bid-request-1.json index 5acca6e19ea..18f60cc35b3 100644 --- a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-appnexus-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-appnexus-bid-request-1.json @@ -46,12 +46,7 @@ "id": "12345", "buyeruid": "12345", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-request.json b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-request.json index 0ec5c77e558..74c8b18ab48 100644 --- a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-request.json +++ b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-request.json @@ -158,12 +158,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-response.json b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-response.json index 0d599bcb5e4..0fe317b0ee1 100644 --- a/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-response.json +++ b/src/test/resources/org/prebid/server/it/auction/rubicon_appnexus/test-auction-rubicon-appnexus-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ appnexus.exchange_uri }}?member_id=member1", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode4\",\"video\":{\"mimes\":[\"mimes\"],\"minduration\":20,\"maxduration\":60,\"protocols\":[1],\"w\":300,\"h\":250,\"startdelay\":5,\"playbackmethod\":[1]},\"tagid\":\"invCode1\",\"bidfloor\":1.0,\"ext\":{\"appnexus\":{\"placement_id\":9848285,\"keywords\":\"k1=v1,k1=v2\",\"traffic_source_code\":\"trafficSourceCode1\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"id\":\"12345\",\"buyeruid\":\"12345\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode4\",\"video\":{\"mimes\":[\"mimes\"],\"minduration\":20,\"maxduration\":60,\"protocols\":[1],\"w\":300,\"h\":250,\"startdelay\":5,\"playbackmethod\":[1]},\"tagid\":\"invCode1\",\"bidfloor\":1.0,\"ext\":{\"appnexus\":{\"placement_id\":9848285,\"keywords\":\"k1=v1,k1=v2\",\"traffic_source_code\":\"trafficSourceCode1\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"id\":\"12345\",\"buyeruid\":\"12345\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId4\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode4\",\"price\":5.78,\"adm\":\"adm4\",\"crid\":\"crid4\",\"dealid\":\"dealId4\",\"w\":300,\"h\":250,\"ext\":{\"appnexus\":{\"bid_ad_type\":1}}}],\"seat\":\"seatId4\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-request.json b/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-request.json index 930ae4cceb0..2a7a016a8f4 100644 --- a/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-request.json +++ b/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-request.json @@ -50,12 +50,7 @@ }, "user": { "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-response.json b/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-response.json index b08299832c8..18e143da014 100644 --- a/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-response.json +++ b/src/test/resources/org/prebid/server/it/auction/sovrn/test-auction-sovrn-response.json @@ -9,7 +9,7 @@ "debug": [ { "request_uri": "{{ sovrn.exchange_uri }}", - "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode11\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"tagId1\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"990011\",\"ext\":{\"consent\":\"consent1\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":1}}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", + "request_body": "{\"id\":\"tid\",\"imp\":[{\"id\":\"adUnitCode11\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":300,\"h\":250},\"tagid\":\"tagId1\"}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\"},\"device\":{\"ua\":\"userAgent\",\"dnt\":10,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"990011\",\"ext\":{\"consent\":\"consent1\"}},\"at\":1,\"tmax\":5000,\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":1}}}", "response_body": "{\"id\":\"bidResponseId11\",\"seatbid\":[{\"bid\":[{\"impid\":\"adUnitCode11\",\"price\":5.78,\"adm\":\"adm11\",\"crid\":\"crid11\",\"dealid\":\"dealId11\",\"w\":300,\"h\":250}],\"seat\":\"seatId11\",\"group\":0}]}", "status_code": 200 } diff --git a/src/test/resources/org/prebid/server/it/auction/sovrn/test-sovrn-bid-request-1.json b/src/test/resources/org/prebid/server/it/auction/sovrn/test-sovrn-bid-request-1.json index 67edaac40d7..1ebe7176ae6 100644 --- a/src/test/resources/org/prebid/server/it/auction/sovrn/test-sovrn-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/auction/sovrn/test-sovrn-bid-request-1.json @@ -31,12 +31,7 @@ "user": { "buyeruid": "990011", "ext": { - "consent": "consent1", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 1 - } + "consent": "consent1" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adform/test-auction-adform-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adform/test-auction-adform-request.json index e88886add30..a067a8cfdf9 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adform/test-auction-adform-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adform/test-auction-adform-request.json @@ -68,11 +68,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adgeneration/test-auction-adgeneration-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adgeneration/test-auction-adgeneration-request.json index f64a5a78722..77280d99817 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adgeneration/test-auction-adgeneration-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adgeneration/test-auction-adgeneration-request.json @@ -76,12 +76,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adhese/test-auction-adhese-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adhese/test-auction-adhese-request.json index 3073a20f66b..b6654b2c812 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adhese/test-auction-adhese-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adhese/test-auction-adhese-request.json @@ -86,12 +86,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-adkernel-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-adkernel-bid-request.json index 9a0d61c74cb..13eac22e83d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-adkernel-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-adkernel-bid-request.json @@ -31,12 +31,7 @@ "user": { "buyeruid": "AK-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-auction-adkernel-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-auction-adkernel-request.json index 181833b7898..78d11b91cb3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-auction-adkernel-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adkernel/test-auction-adkernel-request.json @@ -70,12 +70,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-1.json index cd339c41a1a..c72ef604770 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-1.json @@ -33,12 +33,7 @@ "user": { "buyeruid": "AK-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-2.json index 961361f1c38..c7438410403 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-adkerneladn-bid-request-2.json @@ -30,12 +30,7 @@ "user": { "buyeruid": "AK-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-auction-adkerneladn-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-auction-adkerneladn-request.json index b583dabf956..218846f482b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-auction-adkerneladn-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adkerneladn/test-auction-adkerneladn-request.json @@ -81,12 +81,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-1.json index df169930104..fffdc0d462a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "AD-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-2.json index b65b6c6d8ff..46343b73e4a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adman/test-adman-bid-request-2.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "AD-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -88,4 +83,4 @@ } } } -} \ No newline at end of file +} 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 aac9638c266..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" + } + } } } } @@ -55,12 +63,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -90,4 +93,4 @@ "auctiontimestamp": 1000 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-admixer-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-admixer-bid-request.json index 2eca793ae38..d913ab94f75 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-admixer-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-admixer-bid-request.json @@ -64,12 +64,7 @@ "user": { "buyeruid": "AD-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-auction-admixer-request.json b/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-auction-admixer-request.json index ea1a4d3eb51..55d16a5aecb 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-auction-admixer-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/admixer/test-auction-admixer-request.json @@ -78,12 +78,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 eef8d0cef8e..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" + } + } } } } @@ -79,11 +83,6 @@ "user": { "buyeruid": "AO-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -92,4 +91,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} 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 567301ea3a0..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" + } + } } } } @@ -105,11 +109,6 @@ "user": { "buyeruid": "AO-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -176,4 +175,4 @@ "auctiontimestamp": 1000 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-adoppler-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-adoppler-bid-request-1.json index a6d57161046..516c7b029d8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-adoppler-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-adoppler-bid-request-1.json @@ -9,7 +9,8 @@ }, "ext": { "bidder": { - "adunit": "unit1" + "adunit": "unit1", + "client": "testClient" } } } @@ -35,12 +36,7 @@ "user": { "buyeruid": "AP-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -84,4 +80,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-auction-adoppler-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-auction-adoppler-request.json index cf8621f7d95..798268fa198 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-auction-adoppler-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adoppler/test-auction-adoppler-request.json @@ -9,7 +9,8 @@ }, "ext": { "adoppler": { - "adunit": "unit1" + "adunit": "unit1", + "client": "testClient" } } } @@ -68,12 +69,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -81,4 +77,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-adpone-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-adpone-bid-request.json index 8d461e8bb05..0bd175eb819 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-adpone-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-adpone-bid-request.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "AP-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-auction-adpone-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-auction-adpone-request.json index f2ab294b189..7323cac2769 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-auction-adpone-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adpone/test-auction-adpone-request.json @@ -62,12 +62,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-adtarget-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-adtarget-bid-request-1.json index 5feb88fc600..dd5c18952c8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-adtarget-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-adtarget-bid-request-1.json @@ -43,12 +43,7 @@ "user": { "buyeruid": "AD-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -92,4 +87,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-auction-adtarget-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-auction-adtarget-request.json index b3f96954db2..e541d4818b3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-auction-adtarget-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adtarget/test-auction-adtarget-request.json @@ -65,12 +65,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -78,4 +73,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-adtelligent-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-adtelligent-bid-request-1.json index 59a7c4cba4e..c5624a8178b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-adtelligent-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-adtelligent-bid-request-1.json @@ -43,12 +43,7 @@ "user": { "buyeruid": "AT-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-auction-adtelligent-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-auction-adtelligent-request.json index 8f8ac568361..4cbb5cedbe7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-auction-adtelligent-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adtelligent/test-auction-adtelligent-request.json @@ -65,12 +65,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-advangelists-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-advangelists-bid-request.json index e1f0195bbb3..917caf35825 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-advangelists-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-advangelists-bid-request.json @@ -38,12 +38,7 @@ "user": { "buyeruid": "AV-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-auction-advangelists-request.json b/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-auction-advangelists-request.json index 00c7c183f0f..ae7d1bebaae 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-auction-advangelists-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/advangelists/test-auction-advangelists-request.json @@ -70,12 +70,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-1.json index a22c9678a27..a8d33c18829 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-1.json @@ -1,99 +1,93 @@ { - "id":"tid", - "imp":[ + "id": "tid", + "imp": [ { - "id":"impId001", - "banner":{ - "format":[ + "id": "impId001", + "banner": { + "format": [ { - "w":300, - "h":250 + "w": 300, + "h": 250 } ] }, - "tagid":"test" + "tagid": "test" } ], - "site":{ - "domain":"example.com", - "page":"http://www.example.com", - "publisher":{ - "id":"publisherId" + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId" }, - "ext":{ - "amp":0 + "ext": { + "amp": 0 } }, - "device":{ - "ua":"userAgent", - "dnt":2, - "ip":"193.168.244.1", - "pxratio":4.2, - "language":"en", - "ifa":"ifaId" + "device": { + "ua": "userAgent", + "dnt": 2, + "ip": "193.168.244.1", + "pxratio": 4.2, + "language": "en", + "ifa": "ifaId" }, - "user":{ - "buyeruid":"AJA-UID", - "ext":{ - "consent":"consentValue", - "digitrust":{ - "id":"id", - "keyv":123, - "pref":0 - } + "user": { + "buyeruid": "AJA-UID", + "ext": { + "consent": "consentValue" } }, - "at":1, - "tmax":5000, - "cur":[ + "at": 1, + "tmax": 5000, + "cur": [ "USD" ], - "source":{ - "fd":1, - "tid":"tid" + "source": { + "fd": 1, + "tid": "tid" }, - "regs":{ - "ext":{ - "gdpr":0 + "regs": { + "ext": { + "gdpr": 0 } }, - "ext":{ - "prebid":{ - "currency":{ - "rates":{ - "EUR":{ - "USD":1.2406 + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.2406 }, - "USD":{ - "EUR":0.811 + "USD": { + "EUR": 0.811 } } }, - "targeting":{ - "pricegranularity":{ - "precision":2, - "ranges":[ + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ { - "max":20, - "increment":0.1 + "max": 20, + "increment": 0.1 } ] }, - "includewinners":true, - "includebidderkeys":true + "includewinners": true, + "includebidderkeys": true }, - "cache":{ - "bids":{ - + "cache": { + "bids": { }, - "vastxml":{ - "ttlseconds":120 + "vastxml": { + "ttlseconds": 120 } }, - "auctiontimestamp":1000, + "auctiontimestamp": 1000, "channel": { "name": "web" } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-2.json index 8dffa5581ca..7dd8fe7d4ef 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-aja-bid-request-2.json @@ -1,102 +1,96 @@ { - "id":"tid", - "imp":[ + "id": "tid", + "imp": [ { - "id":"impId002", - "video":{ - "mimes":[ + "id": "impId002", + "video": { + "mimes": [ "video/mp4" ], - "protocols":[ + "protocols": [ 2, 5 ], - "w":1024, - "h":576 + "w": 1024, + "h": 576 }, - "tagid":"aja-test" + "tagid": "aja-test" } ], - "site":{ - "domain":"example.com", - "page":"http://www.example.com", - "publisher":{ - "id":"publisherId" + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId" }, - "ext":{ - "amp":0 + "ext": { + "amp": 0 } }, - "device":{ - "ua":"userAgent", - "dnt":2, - "ip":"193.168.244.1", - "pxratio":4.2, - "language":"en", - "ifa":"ifaId" + "device": { + "ua": "userAgent", + "dnt": 2, + "ip": "193.168.244.1", + "pxratio": 4.2, + "language": "en", + "ifa": "ifaId" }, - "user":{ - "buyeruid":"AJA-UID", - "ext":{ - "consent":"consentValue", - "digitrust":{ - "id":"id", - "keyv":123, - "pref":0 - } + "user": { + "buyeruid": "AJA-UID", + "ext": { + "consent": "consentValue" } }, - "at":1, - "tmax":5000, - "cur":[ + "at": 1, + "tmax": 5000, + "cur": [ "USD" ], - "source":{ - "fd":1, - "tid":"tid" + "source": { + "fd": 1, + "tid": "tid" }, - "regs":{ - "ext":{ - "gdpr":0 + "regs": { + "ext": { + "gdpr": 0 } }, - "ext":{ - "prebid":{ - "currency":{ - "rates":{ - "EUR":{ - "USD":1.2406 + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.2406 }, - "USD":{ - "EUR":0.811 + "USD": { + "EUR": 0.811 } } }, - "targeting":{ - "pricegranularity":{ - "precision":2, - "ranges":[ + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ { - "max":20, - "increment":0.1 + "max": 20, + "increment": 0.1 } ] }, - "includewinners":true, - "includebidderkeys":true + "includewinners": true, + "includebidderkeys": true }, - "cache":{ - "bids":{ - + "cache": { + "bids": { }, - "vastxml":{ - "ttlseconds":120 + "vastxml": { + "ttlseconds": 120 } }, - "auctiontimestamp":1000, + "auctiontimestamp": 1000, "channel": { "name": "web" } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-request.json b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-request.json index 8461aa2d80e..bc0813c5e24 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-request.json @@ -91,12 +91,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -104,4 +99,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-response.json b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-response.json index b53a2378636..e1bb13841f5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-auction-aja-response.json @@ -1,102 +1,102 @@ { - "id":"tid", - "seatbid":[ + "id": "tid", + "seatbid": [ { - "bid":[ + "bid": [ { - "id":"bid002", - "impid":"impId002", - "price":9.99, - "adomain":[ + "id": "bid002", + "impid": "impId002", + "price": 9.99, + "adomain": [ "psacentral.org" ], - "cid":"cid002", - "crid":"crid002", - "w":300, - "h":250, + "cid": "cid002", + "crid": "crid002", + "w": 300, + "h": 250, "exp": 120, - "ext":{ - "prebid":{ - "type":"video", - "targeting":{ - "hb_cache_path_aja":"{{ cache.path }}", - "hb_cache_host_aja":"{{ cache.host }}", - "hb_bidder_aja":"aja", - "hb_cache_id":"e7965b2e-0aa3-4252-a22c-580ed010e619", - "hb_cache_id_aja":"e7965b2e-0aa3-4252-a22c-580ed010e619", - "hb_pb_aja":"9.90", - "hb_pb":"9.90", - "hb_uuid_aja":"44a52b06-b29f-4819-a05f-db36b9e7b8fc", - "hb_cache_path":"{{ cache.path }}", - "hb_size_aja":"300x250", - "hb_uuid":"44a52b06-b29f-4819-a05f-db36b9e7b8fc", - "hb_size":"300x250", - "hb_bidder":"aja", - "hb_cache_host":"{{ cache.host }}" + "ext": { + "prebid": { + "type": "video", + "targeting": { + "hb_cache_path_aja": "{{ cache.path }}", + "hb_cache_host_aja": "{{ cache.host }}", + "hb_bidder_aja": "aja", + "hb_cache_id": "e7965b2e-0aa3-4252-a22c-580ed010e619", + "hb_cache_id_aja": "e7965b2e-0aa3-4252-a22c-580ed010e619", + "hb_pb_aja": "9.90", + "hb_pb": "9.90", + "hb_uuid_aja": "44a52b06-b29f-4819-a05f-db36b9e7b8fc", + "hb_cache_path": "{{ cache.path }}", + "hb_size_aja": "300x250", + "hb_uuid": "44a52b06-b29f-4819-a05f-db36b9e7b8fc", + "hb_size": "300x250", + "hb_bidder": "aja", + "hb_cache_host": "{{ cache.host }}" }, - "cache":{ - "bids":{ - "url":"{{ cache.resource_url }}e7965b2e-0aa3-4252-a22c-580ed010e619", - "cacheId":"e7965b2e-0aa3-4252-a22c-580ed010e619" + "cache": { + "bids": { + "url": "{{ cache.resource_url }}e7965b2e-0aa3-4252-a22c-580ed010e619", + "cacheId": "e7965b2e-0aa3-4252-a22c-580ed010e619" }, - "vastXml":{ - "url":"{{ cache.resource_url }}44a52b06-b29f-4819-a05f-db36b9e7b8fc", - "cacheId":"44a52b06-b29f-4819-a05f-db36b9e7b8fc" + "vastXml": { + "url": "{{ cache.resource_url }}44a52b06-b29f-4819-a05f-db36b9e7b8fc", + "cacheId": "44a52b06-b29f-4819-a05f-db36b9e7b8fc" } } } } }, { - "id":"bid001", - "impid":"impId001", - "price":3.33, - "adm":"adm001", - "adid":"adid001", - "cid":"cid001", - "crid":"crid001", - "w":300, - "h":250, - "ext":{ - "prebid":{ - "type":"banner", - "targeting":{ - "hb_pb_aja":"3.30", - "hb_pb":"3.30", - "hb_cache_path_aja":"{{ cache.path }}", - "hb_cache_path":"{{ cache.path }}", - "hb_size_aja":"300x250", - "hb_size":"300x250", - "hb_cache_host_aja":"{{ cache.host }}", - "hb_bidder_aja":"aja", - "hb_bidder":"aja", - "hb_cache_id":"f0ab9105-cb21-4e59-b433-70f5ad6671cb", - "hb_cache_host":"{{ cache.host }}", - "hb_cache_id_aja":"f0ab9105-cb21-4e59-b433-70f5ad6671cb" + "id": "bid001", + "impid": "impId001", + "price": 3.33, + "adm": "adm001", + "adid": "adid001", + "cid": "cid001", + "crid": "crid001", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner", + "targeting": { + "hb_pb_aja": "3.30", + "hb_pb": "3.30", + "hb_cache_path_aja": "{{ cache.path }}", + "hb_cache_path": "{{ cache.path }}", + "hb_size_aja": "300x250", + "hb_size": "300x250", + "hb_cache_host_aja": "{{ cache.host }}", + "hb_bidder_aja": "aja", + "hb_bidder": "aja", + "hb_cache_id": "f0ab9105-cb21-4e59-b433-70f5ad6671cb", + "hb_cache_host": "{{ cache.host }}", + "hb_cache_id_aja": "f0ab9105-cb21-4e59-b433-70f5ad6671cb" }, - "cache":{ - "bids":{ - "url":"{{ cache.resource_url }}f0ab9105-cb21-4e59-b433-70f5ad6671cb", - "cacheId":"f0ab9105-cb21-4e59-b433-70f5ad6671cb" + "cache": { + "bids": { + "url": "{{ cache.resource_url }}f0ab9105-cb21-4e59-b433-70f5ad6671cb", + "cacheId": "f0ab9105-cb21-4e59-b433-70f5ad6671cb" } } } } } ], - "seat":"aja", - "group":0 + "seat": "aja", + "group": 0 } ], - "cur":"USD", - "ext":{ - "responsetimemillis":{ - "cache":"{{ cache.response_time_ms }}", - "aja":"{{ aja.response_time_ms }}" + "cur": "USD", + "ext": { + "responsetimemillis": { + "cache": "{{ cache.response_time_ms }}", + "aja": "{{ aja.response_time_ms }}" }, - "tmaxrequest":5000, - "prebid":{ - "auctiontimestamp":1000 + "tmaxrequest": 5000, + "prebid": { + "auctiontimestamp": 1000 } } } \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-cache-aja-request.json b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-cache-aja-request.json index 5bb0050c74f..9d4855a6222 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/aja/test-cache-aja-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/aja/test-cache-aja-request.json @@ -1,38 +1,38 @@ { - "puts":[ + "puts": [ { - "type":"json", - "value":{ - "id":"bid001", - "impid":"impId001", - "price":3.33, - "adm":"adm001", - "adid":"adid001", - "cid":"cid001", - "crid":"crid001", - "w":300, - "h":250 + "type": "json", + "value": { + "id": "bid001", + "impid": "impId001", + "price": 3.33, + "adm": "adm001", + "adid": "adid001", + "cid": "cid001", + "crid": "crid001", + "w": 300, + "h": 250 } }, { - "type":"json", - "value":{ - "id":"bid002", - "impid":"impId002", - "price":9.99, - "adomain":[ + "type": "json", + "value": { + "id": "bid002", + "impid": "impId002", + "price": 9.99, + "adomain": [ "psacentral.org" ], - "cid":"cid002", - "crid":"crid002", - "w":300, - "h":250 + "cid": "cid002", + "crid": "crid002", + "w": 300, + "h": 250 } }, { - "type":"xml", - "value":"prebid.org wrapper", - "expiry":120 + "type": "xml", + "value": "prebid.org wrapper", + "expiry": 120 } ] } \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-request.json new file mode 100644 index 00000000000..3c064c54c7c --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-request.json @@ -0,0 +1,79 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + }, + "tagid": "testAdUnitId", + "ext" : { + "bidder" : { + "tagId" : "testTagId", + "adUnitId" : "testAdUnitId" + } + } + } + ], + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "testTagId" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "buyeruid": "AMX-UID" + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000, + "channel": { + "name": "web" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-response.json new file mode 100644 index 00000000000..3f362e2f62d --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-amx-bid-response.json @@ -0,0 +1,24 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "adm": "00:00:15", + "nurl": "https://example.com/nurl", + "cid": "8048", + "ext": { + "himp": ["https://example.com/imp-tracker/pixel.gif?param=1¶m2=2"], + "startdelay": 0 + } + } + ], + "type": "video" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-request.json new file mode 100644 index 00000000000..f38e58bc429 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-request.json @@ -0,0 +1,66 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + }, + "ext": { + "amx": { + "tagId": "testTagId", + "adUnitId": "testAdUnitId" + } + }, + "tagid" : "testAdUnitId" + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "publisher": { + "id": "testTagId" + } + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + } + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000 + } + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-response.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-response.json new file mode 100644 index 00000000000..6945a023670 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-auction-amx-response.json @@ -0,0 +1,61 @@ +{ + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "testid", + "impid": "testimpid", + "price": 0.01, + "adid": "2068416", + "cid": "8048", + "crid": "24080", + "adm": "00:00:15", + "nurl": "", + "ext": { + "prebid": { + "type": "video", + "targeting": { + "hb_pb": "0.00", + "hb_cache_id_amx": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_bidder_amx": "amx", + "hb_bidder": "amx", + "hb_cache_id": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_pb_amx": "0.00", + "hb_cache_host": "{{ cache.host }}", + "hb_cache_host_amx": "{{ cache.host }}", + "hb_cache_path": "{{ cache.path }}", + "hb_cache_path_amx": "{{ cache.path }}" + }, + "cache": { + "bids": { + "url": "{{ cache.resource_url }}3c0769d8-0dd9-465c-8bf3-f570605ba698", + "cacheId": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + } + }, + "bidder": { + "himp": [ + "https://example.com/imp-tracker/pixel.gif?param=1¶m2=2" + ], + "startdelay": 0 + } + } + } + ], + "seat": "amx", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "amx": "{{ amx.response_time_ms }}", + "cache": "{{ cache.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 1000 + }, + "tmaxrequest": 1000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-request.json new file mode 100644 index 00000000000..5764e471148 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-request.json @@ -0,0 +1,21 @@ +{ + "puts": [ + { + "type": "json", + "value": { + "id": "testid", + "price": 0.01, + "nurl" : "", + "adm": "00:00:15", + "adid": "2068416", + "cid": "8048", + "crid": "24080", + "impid": "testimpid", + "ext": { + "himp": ["https://example.com/imp-tracker/pixel.gif?param=1¶m2=2"], + "startdelay": 0 + } + } + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-response.json b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-response.json new file mode 100644 index 00000000000..c0100536be1 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/amx/test-cache-amx-response.json @@ -0,0 +1,7 @@ +{ + "responses": [ + { + "uuid": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-1.json index 7764d9a5dca..8b3218be6fc 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-1.json @@ -40,12 +40,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-2.json index 1908c31cef2..4e5fc9dfbc8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-applogy-bid-request-2.json @@ -41,12 +41,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-auction-applogy-request.json b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-auction-applogy-request.json index 641d7062bae..8579656425c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-auction-applogy-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/applogy/test-auction-applogy-request.json @@ -81,12 +81,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 c5033bf45c5..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" + } + } } } } @@ -43,10 +47,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -69,12 +69,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 b3514e3d759..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 @@ -68,7 +68,7 @@ "avocet": [ { "uri": "{{ avocet.exchange_uri }}", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"placement\":\"5ea9601ac865f911007f1b6a\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"AV-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"placement\":\"5ea9601ac865f911007f1b6a\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"AV-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"7706636740145184841\",\"impid\":\"test-imp-banner-id\",\"price\":0.5,\"adid\":\"29681110\",\"adm\":\"some-test-ad\",\"adomain\":[\"advertsite.com\"],\"cid\":\"772\",\"crid\":\"29681110\",\"h\":576,\"w\":1024,\"api\":1,\"ext\":{\"avocet\":{\"duration\":30}}}]}]}", "status": 200 } @@ -90,8 +90,12 @@ "h": 400 }, "ext": { - "avocet": { - "placement": "5ea9601ac865f911007f1b6a" + "prebid": { + "bidder": { + "avocet": { + "placement": "5ea9601ac865f911007f1b6a" + } + } } } } @@ -116,11 +120,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -141,10 +140,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-avocet-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-avocet-bid-request-1.json index 9b30034e435..db3cb3b5396 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-avocet-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/avocet/test-avocet-bid-request-1.json @@ -39,13 +39,8 @@ "ifa": "ifaId" }, "user": { - "buyeruid" : "AV-UID", + "buyeruid": "AV-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -66,10 +61,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -95,4 +86,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-auction-beachfront-request.json b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-auction-beachfront-request.json index 623d78352da..ba9e0eef62a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-auction-beachfront-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-auction-beachfront-request.json @@ -93,12 +93,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-1.json index 98ece0b09ac..3e10aa922ed 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "BF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-2.json index f5443122e4e..c412f204683 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beachfront/test-beachfront-bid-request-2.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "BF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 279bf8e18fd..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" + } + } } } } @@ -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", @@ -47,10 +51,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -73,11 +73,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, 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 8e4cb308a7e..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 @@ -50,7 +50,7 @@ "beintoo": [ { "uri": "{{ beintoo.exchange_uri }}", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"uuid\",\"banner\":{\"format\":[],\"w\":300,\"h\":250},\"tagid\":\"25251\",\"secure\":0}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"Android Chrome/60\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"BT-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"uuid\",\"banner\":{\"format\":[],\"w\":300,\"h\":250},\"tagid\":\"25251\",\"secure\":0}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"Android Chrome/60\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"BT-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"some_test_auction\",\"seatbid\":[{\"seat\":\"12356\",\"bid\":[{\"id\":\"uuid\",\"adm\":\"
\",\"impid\":\"uuid\",\"ttl\":300,\"crid\":\"94395500\",\"w\":300,\"price\":2.942808,\"adid\":\"94395500\",\"h\":250}]}],\"cur\":\"USD\"}", "status": 200 } @@ -78,8 +78,12 @@ ] }, "ext": { - "beintoo": { - "tagid": "25251" + "prebid": { + "bidder": { + "beintoo": { + "tagid": "25251" + } + } } } } @@ -104,11 +108,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -129,10 +128,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-beintoo-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-beintoo-bid-request.json index 23914b4b674..7948350ea6e 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-beintoo-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/beintoo/test-beintoo-bid-request.json @@ -31,14 +31,9 @@ "ifa": "ifaId" }, "user": { - "buyeruid" : "BT-UID", - "ext" : { - "consent" : "consentValue", - "digitrust" : { - "id" : "id", - "keyv" : 123, - "pref" : 0 - } + "buyeruid": "BT-UID", + "ext": { + "consent": "consentValue" } }, "at": 1, @@ -58,10 +53,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -87,4 +78,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-auction-brightroll-request.json b/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-auction-brightroll-request.json index 04f41fb4842..f9c81a3f876 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-auction-brightroll-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-auction-brightroll-request.json @@ -64,12 +64,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-brightroll-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-brightroll-bid-request-1.json index ebdeef8734e..71c0a0389fe 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-brightroll-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/brightroll/test-brightroll-bid-request-1.json @@ -52,11 +52,6 @@ "user": { "buyeruid": "BR-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/consumable/test-auction-consumable-request.json b/src/test/resources/org/prebid/server/it/openrtb2/consumable/test-auction-consumable-request.json index 88fb155b0ab..3d4151a6b86 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/consumable/test-auction-consumable-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/consumable/test-auction-consumable-request.json @@ -65,12 +65,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-auction-conversant-request.json b/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-auction-conversant-request.json index 62ae5f8e6d5..efe8225764f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-auction-conversant-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-auction-conversant-request.json @@ -67,12 +67,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-conversant-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-conversant-bid-request.json index a206472e3aa..653a4b9d51e 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-conversant-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/conversant/alias/test-conversant-bid-request.json @@ -14,7 +14,7 @@ "h": 600 }, "displaymanager": "prebid-s2s", - "displaymanagerver": "1.0.1", + "displaymanagerver": "2.0.0", "ext": { "bidder": { "site_id": "siteId2" @@ -44,12 +44,7 @@ "user": { "buyeruid": "CV-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -96,4 +91,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-auction-conversant-request.json b/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-auction-conversant-request.json index 22bfe67895f..3ceceeb9a61 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-auction-conversant-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-auction-conversant-request.json @@ -79,12 +79,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-conversant-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-conversant-bid-request.json index 2cf39df5f37..bf1337d38fc 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-conversant-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/conversant/test-conversant-bid-request.json @@ -14,7 +14,7 @@ "h": 600 }, "displaymanager": "prebid-s2s", - "displaymanagerver": "1.0.1", + "displaymanagerver": "2.0.0", "ext": { "bidder": { "site_id": "siteId" @@ -31,7 +31,7 @@ "h": 600 }, "displaymanager": "prebid-s2s", - "displaymanagerver": "1.0.1", + "displaymanagerver": "2.0.0", "ext": { "bidder": { "site_id": "siteId" @@ -61,11 +61,6 @@ "user": { "buyeruid": "CV-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -110,4 +105,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-auction-cpmstar-request.json b/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-auction-cpmstar-request.json index 47bc7bde079..68f0b7c91d3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-auction-cpmstar-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-auction-cpmstar-request.json @@ -83,12 +83,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-cpmstar-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-cpmstar-bid-request-1.json index b3d811045e6..a07e42f638d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-cpmstar-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/cpmstar/test-cpmstar-bid-request-1.json @@ -56,12 +56,7 @@ "user": { "buyeruid": "CS-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 ea69921948e..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 + } + } } } } @@ -81,12 +89,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 2f25dda7869..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 @@ -103,13 +103,13 @@ "datablocks": [ { "uri": "{{ datablocks.exchange_uri }}?sid=2", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId002\",\"video\":{\"mimes\":[\"video/mp4\"],\"w\":300,\"h\":250,\"pos\":1},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":2}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"DB-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId002\",\"video\":{\"mimes\":[\"video/mp4\"],\"w\":300,\"h\":250,\"pos\":1},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":2}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"DB-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"bid002\",\"impid\":\"impId002\",\"price\":9.99,\"crid\":\"crid002\",\"cid\":\"cid002\",\"adomain\":[\"psacentral.org\"],\"h\":250,\"w\":300}],\"seat\":\"datablocks\"}]}", "status": 200 }, { "uri": "{{ datablocks.exchange_uri }}?sid=1", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId001\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}]},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":1}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"DB-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId001\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}]},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":1}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"DB-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"bid001\",\"impid\":\"impId001\",\"price\":7.77,\"adid\":\"adid001\",\"crid\":\"crid001\",\"cid\":\"cid001\",\"adm\":\"adm001\",\"h\":250,\"w\":300}],\"seat\":\"datablocks\"}]}", "status": 200 } @@ -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 + } + } } } } @@ -173,12 +181,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-1.json index fda6ba6af81..d83e947f165 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "DB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-2.json index a6ed8c70eeb..0352ce9e783 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/datablocks/test-datablocks-bid-request-2.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "DB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-auction-deepintent-request.json b/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-auction-deepintent-request.json index dfc241aab42..e7507d0e050 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-auction-deepintent-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-auction-deepintent-request.json @@ -12,7 +12,7 @@ "tagid" : "possibleTagId", "ext": { "deepintent": { - "TagID": "possibleTagId" + "tagId": "possibleTagId" } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-deepintent-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-deepintent-bid-request.json index f8460a68d84..74055235383 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-deepintent-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/deepintent/test-deepintent-bid-request.json @@ -12,7 +12,7 @@ "tagid" : "possibleTagId", "ext": { "bidder": { - "TagID": "possibleTagId" + "tagId": "possibleTagId" } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-auction-dmx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-auction-dmx-request.json index 01774409106..356d83d4fa6 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-auction-dmx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-auction-dmx-request.json @@ -74,14 +74,20 @@ "fd": 1, "tid": "tid" }, + "user": { "ext": { "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "eids": [ + { + "source": "adserver.org", + "uids": [ + { + "id": "id" + } + ] + } + ] } }, "regs": { @@ -123,4 +129,4 @@ "auctiontimestamp": 1000 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-dmx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-dmx-bid-request.json index df6f7d0fe44..444460c60f3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-dmx-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/dmx/test-dmx-bid-request.json @@ -71,11 +71,16 @@ "buyeruid": "DM-UID", "ext": { "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "eids": [ + { + "source": "adserver.org", + "uids": [ + { + "id": "id" + } + ] + } + ] } }, "at": 1, @@ -129,4 +134,4 @@ } } } -} \ No newline at end of file +} 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 7c9afda64d7..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", @@ -47,10 +51,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -73,11 +73,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, 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 9c5c067a286..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 @@ -50,7 +50,7 @@ "emx_digital": [ { "uri": "{{ emx_digital.exchange_uri }}?t=1000&ts=2060541160", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"uuid\",\"banner\":{\"format\":[],\"w\":300,\"h\":250},\"tagid\":\"25251\",\"secure\":0}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"Android Chrome/60\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"uuid\",\"banner\":{\"format\":[],\"w\":300,\"h\":250},\"tagid\":\"25251\",\"secure\":0,\"ext\":{\"bidder\":{\"tagid\":\"25251\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"Android Chrome/60\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"some_test_auction\",\"seatbid\":[{\"seat\":\"12356\",\"bid\":[{\"id\":\"uuid\",\"adm\":\"
\",\"impid\":\"uuid\",\"ttl\":300,\"crid\":\"94395500\",\"w\":300,\"price\":2.942808,\"adid\":\"94395500\",\"h\":250}]}],\"cur\":\"USD\"}", "status": 200 } @@ -78,8 +78,12 @@ ] }, "ext": { - "emx_digital": { - "tagid": "25251" + "prebid": { + "bidder": { + "emx_digital": { + "tagid": "25251" + } + } } } } @@ -104,11 +108,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -129,10 +128,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-emxdigital-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-emxdigital-bid-request.json index cb4802104a4..8b819c1bb3e 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-emxdigital-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/emxdigital/test-emxdigital-bid-request.json @@ -46,10 +46,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-auction-engagebdr-request.json b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-auction-engagebdr-request.json index 22d183ef720..ea9428f01b7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-auction-engagebdr-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-auction-engagebdr-request.json @@ -74,12 +74,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-1.json index ea0cb1d65d6..1126f469c70 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-1.json @@ -35,12 +35,7 @@ "user": { "buyeruid": "EG-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-2.json index 1317bdc2578..6ffe58b473f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/engagebdr/test-engagebdr-bid-request-2.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "EG-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json b/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json index aae85c34bbc..f9217cad610 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/eplanning/test-auction-eplanning-request.json @@ -63,12 +63,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-auction-facebook-request.json b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-auction-facebook-request.json index 3af581f91a6..c0133c4cab9 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-auction-facebook-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-auction-facebook-request.json @@ -91,12 +91,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-1.json index 92524585708..b65befa9ae0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-1.json @@ -26,12 +26,7 @@ "user": { "buyeruid": "FB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-2.json index 9a182f31169..91e8b766567 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-2.json @@ -30,12 +30,7 @@ "user": { "buyeruid": "FB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-3.json b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-3.json index 1a2952a99d4..6ebeb687006 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-3.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/facebook/test-facebook-bid-request-3.json @@ -26,11 +26,6 @@ "user": { "buyeruid": "FB-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/gamma/test-auction-gamma-request.json b/src/test/resources/org/prebid/server/it/openrtb2/gamma/test-auction-gamma-request.json index ed18110b4b9..79472812715 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/gamma/test-auction-gamma-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/gamma/test-auction-gamma-request.json @@ -69,11 +69,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-auction-gamoshi-request.json b/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-auction-gamoshi-request.json index cbb9986d15f..ddcdd5b3e2c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-auction-gamoshi-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-auction-gamoshi-request.json @@ -81,12 +81,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-gamoshi-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-gamoshi-bid-request-1.json index a7d7e0f525b..e1af7fe1880 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-gamoshi-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/gamoshi/test-gamoshi-bid-request-1.json @@ -60,11 +60,6 @@ "user": { "buyeruid": "GM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/grid/test-auction-grid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/grid/test-auction-grid-request.json index e5dca974590..6ea2e260623 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/grid/test-auction-grid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/grid/test-auction-grid-request.json @@ -62,12 +62,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/grid/test-grid-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/grid/test-grid-bid-request-1.json index ed007d4c297..59837eb72e4 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/grid/test-grid-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/grid/test-grid-bid-request-1.json @@ -39,11 +39,6 @@ "user": { "buyeruid": "GRID-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-auction-gumgum-request.json b/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-auction-gumgum-request.json index 0f7bd5cd45e..380e5d38f4b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-auction-gumgum-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-auction-gumgum-request.json @@ -103,13 +103,9 @@ } }, "user": { + "buyeruid" : "GUM-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -117,4 +113,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-gumgum-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-gumgum-bid-request-1.json index 62062dd5d81..7ff815c64da 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-gumgum-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/gumgum/test-gumgum-bid-request-1.json @@ -82,13 +82,9 @@ "ifa": "ifaId" }, "user": { + "buyeruid" : "GUM-UID", "ext": { - "consent" : "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -132,4 +128,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-auction-improvedigital-request.json b/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-auction-improvedigital-request.json index 203fcea22a9..d511bb16b6f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-auction-improvedigital-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-auction-improvedigital-request.json @@ -66,12 +66,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-improvedigital-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-improvedigital-bid-request-1.json index a10b790fcad..09055b884ef 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-improvedigital-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/improvedigital/test-improvedigital-bid-request-1.json @@ -43,11 +43,6 @@ "user": { "buyeruid": "ID-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/invibes/test-auction-invibes-request.json b/src/test/resources/org/prebid/server/it/openrtb2/invibes/test-auction-invibes-request.json index 77da3269f4f..ac19fc93b16 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/invibes/test-auction-invibes-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/invibes/test-auction-invibes-request.json @@ -53,12 +53,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-request.json index 0a033505885..6166d299b78 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-request.json @@ -13,9 +13,7 @@ "w": 600, "h": 480 } - ], - "w": 300, - "h": 250 + ] }, "ext": { "ix": { @@ -68,12 +66,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-1.json index 8cb6f275e4f..ca28223533b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-1.json @@ -13,7 +13,6 @@ "w": 300, "h": 250 }, - "tagid": "impId6", "ext": { "bidder": { "siteId": "10002" @@ -42,11 +41,6 @@ "user": { "buyeruid": "IE-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-2.json index f0c314f2f37..47cfe63d4bb 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request-2.json @@ -13,7 +13,6 @@ "w": 600, "h": 480 }, - "tagid": "impId6", "ext": { "bidder": { "siteId": "10002" @@ -42,11 +41,6 @@ "user": { "buyeruid": "IE-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-auction-kidoz-request.json b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-auction-kidoz-request.json index 5f14035ebc2..c23f9b8cbab 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-auction-kidoz-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-auction-kidoz-request.json @@ -93,12 +93,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-1.json index 18ea5c6ace0..4642e4ee4c5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "KZ-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-2.json index 163ea170416..1001ba1ae91 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kidoz/test-kidoz-bid-request-2.json @@ -43,12 +43,7 @@ "user": { "buyeruid": "KZ-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 2120605003c..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" + } + } } } } @@ -43,10 +47,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -69,12 +69,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 e0660e9e4e3..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 @@ -62,7 +62,7 @@ "kubient": [ { "uri": "{{ kubient.exchange_uri }}", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"zoneid\":\"9042\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"zoneid\":\"9042\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"7706636740145184841\",\"impid\":\"test-imp-banner-id\",\"price\":0.5,\"adid\":\"29681110\",\"adm\":\"some-test-ad\",\"adomain\":[\"advertsite.com\"],\"cid\":\"772\",\"crid\":\"29681110\",\"h\":576,\"w\":1024}]}]}", "status": 200 } @@ -84,8 +84,12 @@ "h": 400 }, "ext": { - "kubient": { - "zoneid":"9042" + "prebid": { + "bidder": { + "kubient": { + "zoneid": "9042" + } + } } } } @@ -110,11 +114,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -135,10 +134,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-kubient-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-kubient-bid-request-1.json index 22bb47d2aec..30fc51dafc9 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-kubient-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/kubient/test-kubient-bid-request-1.json @@ -40,11 +40,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -65,10 +60,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-auction-lifestreet-request.json b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-auction-lifestreet-request.json index ce8c289d1b2..6088fe8c279 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-auction-lifestreet-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-auction-lifestreet-request.json @@ -79,12 +79,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-1.json index d09c7d601b3..4a2a5c7ad2f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-1.json @@ -36,11 +36,6 @@ "user": { "buyeruid": "LS-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-2.json index 2be68d0431f..ba3bfcd9393 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lifestreet/test-lifestreet-bid-request-2.json @@ -39,11 +39,6 @@ "user": { "buyeruid": "LS-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-auction-lockerdome-request.json b/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-auction-lockerdome-request.json index 7a914dac341..873ee9b8631 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-auction-lockerdome-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-auction-lockerdome-request.json @@ -62,12 +62,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-lockerdome-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-lockerdome-bid-request.json index dff9878bef9..76ec286f60b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-lockerdome-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lockerdome/test-lockerdome-bid-request.json @@ -39,11 +39,6 @@ "user": { "buyeruid": "LD-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-auction-logicad-request.json b/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-auction-logicad-request.json index af7b053eeae..19c76cc4529 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-auction-logicad-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-auction-logicad-request.json @@ -69,12 +69,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-logicad-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-logicad-bid-request.json index 1d60d5783ab..9e7a534c4f9 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-logicad-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/logicad/test-logicad-bid-request.json @@ -41,12 +41,7 @@ "user": { "buyeruid": "LC-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -90,4 +85,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-auction-lunamedia-request.json b/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-auction-lunamedia-request.json index 38d001f51c7..f3322195185 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-auction-lunamedia-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-auction-lunamedia-request.json @@ -70,12 +70,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -83,4 +78,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-lunamedia-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-lunamedia-bid-request.json index d49a9b375c4..4547b4f6393 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-lunamedia-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/lunamedia/test-lunamedia-bid-request.json @@ -36,14 +36,9 @@ "ifa": "ifaId" }, "user": { - "buyeruid" : "LM-UID", + "buyeruid": "LM-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -87,4 +82,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-auction-marsmedia-request.json b/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-auction-marsmedia-request.json index 2db8cc2a6df..fb335dd6c69 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-auction-marsmedia-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-auction-marsmedia-request.json @@ -77,12 +77,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-marsmedia-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-marsmedia-bid-request-1.json index 1e2bf2a5aac..4a0c0913910 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-marsmedia-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/marsmedia/test-marsmedia-bid-request-1.json @@ -56,11 +56,6 @@ "user": { "buyeruid": "MM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-auction-mgid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-auction-mgid-request.json index 7691fce4060..8b93a756fe1 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-auction-mgid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-auction-mgid-request.json @@ -69,12 +69,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-mgid-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-mgid-bid-request.json index be9a8a5654e..f301e145230 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-mgid-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/mgid/test-mgid-bid-request.json @@ -49,12 +49,7 @@ "user": { "buyeruid": "MGID-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-auction-mobilefuse-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-auction-mobilefuse-request.json index 6b0cae4bf6f..326188434f0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-auction-mobilefuse-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-auction-mobilefuse-request.json @@ -50,12 +50,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-mobilefuse-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-mobilefuse-bid-request.json index 225a0e57171..31b5c38798c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-mobilefuse-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/mobilefuse/test-mobilefuse-bid-request.json @@ -35,12 +35,7 @@ "user": { "buyeruid": "MF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -94,4 +89,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-auction-nanointeractive-request.json b/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-auction-nanointeractive-request.json index 4399365e153..4a55209e63a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-auction-nanointeractive-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-auction-nanointeractive-request.json @@ -100,12 +100,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-nanointeractive-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-nanointeractive-bid-request-1.json index 2cbb8090222..2934f223725 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-nanointeractive-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/nanointeractive/test-nanointeractive-bid-request-1.json @@ -72,12 +72,7 @@ "user": { "buyeruid": "NI-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-auction-ninthdecimal-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-auction-ninthdecimal-request.json index dae0737f694..d83bea50931 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-auction-ninthdecimal-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-auction-ninthdecimal-request.json @@ -70,12 +70,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-ninthdecimal-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-ninthdecimal-bid-request.json index c262def4797..bc2e18078f7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-ninthdecimal-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ninthdecimal/test-ninthdecimal-bid-request.json @@ -38,12 +38,7 @@ "user": { "buyeruid": "ND-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -87,4 +82,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-request.json new file mode 100644 index 00000000000..69a50977e20 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-request.json @@ -0,0 +1,63 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "banner": { + "w": 320, + "h": 250 + }, + "ext": { + "nobid": { + "siteId": 23, + "placementId": 25 + } + }, + "tagid": "impId021" + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "publisher": { + "id": "publisherId" + } + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + } + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000 + } + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-response.json new file mode 100644 index 00000000000..4e20fc3207b --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-auction-nobid-response.json @@ -0,0 +1,53 @@ +{ + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "testid", + "impid": "testimpid", + "price": 0.01, + "adid": "2068416", + "cid": "8048", + "crid": "24080", + "ext": { + "prebid": { + "type": "banner", + "targeting": { + "hb_pb": "0.00", + "hb_cache_id_nobid": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_bidder_nobid": "nobid", + "hb_bidder": "nobid", + "hb_cache_id": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_pb_nobid": "0.00", + "hb_cache_host": "{{ cache.host }}", + "hb_cache_host_nobid": "{{ cache.host }}", + "hb_cache_path": "{{ cache.path }}", + "hb_cache_path_nobid": "{{ cache.path }}" + }, + "cache": { + "bids": { + "url": "{{ cache.resource_url }}3c0769d8-0dd9-465c-8bf3-f570605ba698", + "cacheId": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + } + } + } + } + ], + "seat": "nobid", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "nobid": "{{ nobid.response_time_ms }}", + "cache": "{{ cache.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 1000 + }, + "tmaxrequest": 1000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-request.json new file mode 100644 index 00000000000..ca8e3ab2f6a --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-request.json @@ -0,0 +1,15 @@ +{ + "puts": [ + { + "type": "json", + "value": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-response.json new file mode 100644 index 00000000000..c0100536be1 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-cache-nobid-response.json @@ -0,0 +1,7 @@ +{ + "responses": [ + { + "uuid": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-request.json new file mode 100644 index 00000000000..e0868c03cee --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-request.json @@ -0,0 +1,76 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "banner": { + "w": 320, + "h": 250 + }, + "tagid": "impId021", + "ext": { + "bidder": { + "siteId" : 23, + "placementId" : 25 + } + } + } + ], + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "buyeruid": "NB-UID" + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000, + "channel": { + "name": "web" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-response.json new file mode 100644 index 00000000000..ca4e6ee1db4 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/nobid/test-nobid-bid-response.json @@ -0,0 +1,18 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ], + "type": "banner" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-auction-openx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-auction-openx-request.json index da5a20c5084..0c573d26446 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-auction-openx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-auction-openx-request.json @@ -135,12 +135,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-1.json index c765d75a28f..bd909861224 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-1.json @@ -63,11 +63,6 @@ "user": { "buyeruid": "OX-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-2.json index c12f7c9fb77..de95eb016a6 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-2.json @@ -41,11 +41,6 @@ "user": { "buyeruid": "OX-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-3.json b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-3.json index 4b19cfe27a3..c5a2e86a49e 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-3.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/openx/test-openx-bid-request-3.json @@ -41,11 +41,6 @@ "user": { "buyeruid":"OX-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-auction-orbidder-request.json b/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-auction-orbidder-request.json index ae6c601f277..583a41dedde 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-auction-orbidder-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-auction-orbidder-request.json @@ -50,12 +50,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -87,4 +82,4 @@ "auctiontimestamp": 1000 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-orbidder-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-orbidder-bid-request.json index ec68a1df402..78558170715 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-orbidder-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/orbidder/test-orbidder-bid-request.json @@ -42,12 +42,7 @@ "user": { "buyeruid": "OB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -91,4 +86,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-request.json b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-request.json index 2799068aef5..3917bd648d5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-auction-pubmatic-request.json @@ -103,12 +103,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-request-1.json index 4c77a806ed2..d4a5120aaf7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubmatic/test-pubmatic-bid-request-1.json @@ -54,11 +54,6 @@ "user": { "buyeruid": "PM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-auction-pubnative-request.json b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-auction-pubnative-request.json index ce019eb761f..25141633587 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-auction-pubnative-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-auction-pubnative-request.json @@ -93,12 +93,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-1.json index c737950f3f4..4a4f9d894fe 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-1.json @@ -43,12 +43,7 @@ "user": { "buyeruid": "PN-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-2.json index 400020985b1..62e1d9d9061 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-2.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "PN-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-3.json b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-3.json index c39bbdcc5c9..6a9591a6ce6 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-3.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pubnative/test-pubnative-bid-request-3.json @@ -37,12 +37,7 @@ "user": { "buyeruid": "PN-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-auction-pulsepoint-request.json b/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-auction-pulsepoint-request.json index dffa0f808f3..254d3589220 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-auction-pulsepoint-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-auction-pulsepoint-request.json @@ -86,12 +86,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-pulsepoint-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-pulsepoint-bid-request-1.json index ec4ef146bd1..270c71276cb 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-pulsepoint-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/pulsepoint/test-pulsepoint-bid-request-1.json @@ -65,11 +65,6 @@ "user": { "buyeruid": "PP-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-auction-rhythmone-request.json b/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-auction-rhythmone-request.json index 8725408d37d..7efe918e2bb 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-auction-rhythmone-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-auction-rhythmone-request.json @@ -81,12 +81,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-rhythmone-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-rhythmone-bid-request-1.json index be9655aa778..299b60feba6 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-rhythmone-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rhythmone/test-rhythmone-bid-request-1.json @@ -60,11 +60,6 @@ "user": { "buyeruid": "RO-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-auction-rtbhouse-request.json b/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-auction-rtbhouse-request.json index 388106a0b1f..82b15a0bac1 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-auction-rtbhouse-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-auction-rtbhouse-request.json @@ -63,12 +63,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-rtbhouse-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-rtbhouse-bid-request-1.json index a7e8ecc0fda..c6f6e47a07c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-rtbhouse-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/rtbhouse/test-rtbhouse-bid-request-1.json @@ -37,12 +37,7 @@ "user": { "buyeruid": "RTBH-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 b65c278477a..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 + } + } } } }, @@ -278,10 +292,6 @@ "user": { "ext": { "consent": "BOEFEAyOEFEAyAHABDENAIgAAAB9vABAASA", - "digitrust": { - "id": "id", - "keyv": 123 - }, "eids": [ { "source": "adserver.org", 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 a7cea58a85a..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 + } + } } } }, @@ -628,11 +644,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "BOEFEAyOEFEAyAHABDENAIgAAAB9vABAASA", "eids": [ { 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 f85ecebe767..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 + } + } } } } @@ -57,10 +61,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -93,11 +93,6 @@ ] } ], - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, 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 0237cfc3df6..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 + } + } } } } @@ -121,11 +125,6 @@ ] } ], - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -151,10 +150,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-request.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-request.json new file mode 100644 index 00000000000..bbbde910291 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-request.json @@ -0,0 +1,61 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "banner": { + "w": 320, + "h": 250 + }, + "ext": { + "silvermob": { + "zoneid": "testZoneId", + "host": "testHostValue" + } + }, + "tagid": "impId021" + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123" + }, + "site": { + "publisher": { + "id": "publisherId" + } + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + } + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000 + } + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-response.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-response.json new file mode 100644 index 00000000000..de2439c7f86 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-auction-silvermob-response.json @@ -0,0 +1,53 @@ +{ + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "testid", + "impid": "testimpid", + "price": 0.01, + "adid": "2068416", + "cid": "8048", + "crid": "24080", + "ext": { + "prebid": { + "type": "banner", + "targeting": { + "hb_pb": "0.00", + "hb_cache_id_silvermob": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_bidder_silvermob": "silvermob", + "hb_bidder": "silvermob", + "hb_cache_id": "3c0769d8-0dd9-465c-8bf3-f570605ba698", + "hb_pb_silvermob": "0.00", + "hb_cache_host": "{{ cache.host }}", + "hb_cache_host_silvermob": "{{ cache.host }}", + "hb_cache_path": "{{ cache.path }}", + "hb_cache_path_silvermob": "{{ cache.path }}" + }, + "cache": { + "bids": { + "url": "{{ cache.resource_url }}3c0769d8-0dd9-465c-8bf3-f570605ba698", + "cacheId": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + } + } + } + } + ], + "seat": "silvermob", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": "{{ silvermob.response_time_ms }}", + "cache": "{{ cache.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 1000 + }, + "tmaxrequest": 1000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-request.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-request.json new file mode 100644 index 00000000000..ca8e3ab2f6a --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-request.json @@ -0,0 +1,15 @@ +{ + "puts": [ + { + "type": "json", + "value": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-response.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-response.json new file mode 100644 index 00000000000..c0100536be1 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-cache-silvermob-response.json @@ -0,0 +1,7 @@ +{ + "responses": [ + { + "uuid": "3c0769d8-0dd9-465c-8bf3-f570605ba698" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-request.json new file mode 100644 index 00000000000..d713ab3ff08 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-request.json @@ -0,0 +1,74 @@ +{ + "id": "some-request-id", + "imp": [ + { + "id": "testimpid", + "banner": { + "w": 320, + "h": 250 + }, + "tagid": "impId021", + "ext": { + "bidder": { + "zoneid": "testZoneId", + "host": "testHostValue" + } + } + } + ], + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123" + }, + "user": { + "buyeruid": "SM-UID" + }, + "at": 1, + "tmax": 1000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true + }, + "cache": { + "bids": {} + }, + "auctiontimestamp": 1000, + "channel": { + "name": "web" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-response.json new file mode 100644 index 00000000000..ca4e6ee1db4 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/silvermob/test-silvermob-bid-response.json @@ -0,0 +1,18 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ], + "type": "banner" + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smaato/test-smaato-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/smaato/test-smaato-bid-request.json index 994b0c9a1aa..0f5f286397f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smaato/test-smaato-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smaato/test-smaato-bid-request.json @@ -17,7 +17,6 @@ } ], "site": { - "domain": "example.com", "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", "publisher": { "id": "11000" 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 7de3994683c..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 + } + } } } } @@ -46,10 +50,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -72,12 +72,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 00a73c254e1..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 @@ -61,8 +61,8 @@ ], "smartadserver": [ { - "uri": "{{ smartadserver.exchange_uri }}?callerId=5", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"siteId\":1,\"pageId\":2,\"formatId\":3,\"networkId\":73}}}],\"site\":{\"publisher\":{\"id\":\"73\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"SA-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "uri": "{{ smartadserver.exchange_uri }}/api/bid?callerId=5", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-banner-id\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}],\"w\":500,\"h\":400},\"ext\":{\"bidder\":{\"siteId\":1,\"pageId\":2,\"formatId\":3,\"networkId\":73}}}],\"site\":{\"publisher\":{\"id\":\"73\"}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"SA-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"7706636740145184841\",\"impid\":\"test-imp-banner-id\",\"price\":0.5,\"adid\":\"29681110\",\"adm\":\"some-test-ad\",\"adomain\":[\"advertsite.com\"],\"cid\":\"772\",\"crid\":\"29681110\",\"h\":576,\"w\":1024}]}]}", "status": 200 } @@ -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 + } + } } } } @@ -113,11 +117,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -138,10 +137,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-smartadserver-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-smartadserver-bid-request-1.json index 115e58fa4b0..d44e20c3b68 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-smartadserver-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smartadserver/test-smartadserver-bid-request-1.json @@ -39,11 +39,6 @@ "user": { "buyeruid" : "SA-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -64,10 +59,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -93,4 +84,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-auction-smartrtb-request.json b/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-auction-smartrtb-request.json index f6caca91bf3..2c6355f4429 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-auction-smartrtb-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-auction-smartrtb-request.json @@ -98,12 +98,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-smartrtb-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-smartrtb-bid-request.json index 841e7745379..e52187cc0b5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-smartrtb-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/smartrtb/test-smartrtb-bid-request.json @@ -107,12 +107,7 @@ "tmax": 5000, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-auction-somoaudience-request.json b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-auction-somoaudience-request.json index a7cdb7df279..7234ec3354a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-auction-somoaudience-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-auction-somoaudience-request.json @@ -116,12 +116,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-1.json index 077c83ddce5..acc41e62f2b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-1.json @@ -47,11 +47,6 @@ "user": { "buyeruid": "SM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-2.json index d23c70e4ebf..78dae5aebaf 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-2.json @@ -38,11 +38,6 @@ "user": { "buyeruid": "SM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-3.json b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-3.json index bca7fa68205..1abac39190a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-3.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/somoaudience/test-somoaudience-bid-request-3.json @@ -31,11 +31,6 @@ "user": { "buyeruid": "SM-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-auction-sonobi-request.json b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-auction-sonobi-request.json index 4f8dddb44df..652df4c2d79 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-auction-sonobi-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-auction-sonobi-request.json @@ -77,12 +77,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-1.json index 51ed94815c8..51b5e0c2b3a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "SB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-2.json index bbb3df7405e..a4ac76b8a32 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sonobi/test-sonobi-bid-request-2.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "SB-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-auction-sovrn-request.json b/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-auction-sovrn-request.json index 77b496bb41e..3c9e555f284 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-auction-sovrn-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-auction-sovrn-request.json @@ -64,12 +64,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-sovrn-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-sovrn-bid-request-1.json index 3e333fb2f90..ae826e0b486 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-sovrn-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/sovrn/test-sovrn-bid-request-1.json @@ -42,12 +42,7 @@ "user": { "buyeruid": "990011", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-auction-synacormedia-request.json b/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-auction-synacormedia-request.json index 839add5a448..635962ae61b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-auction-synacormedia-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-auction-synacormedia-request.json @@ -80,12 +80,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-synacormedia-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-synacormedia-bid-request.json index ddb69425960..5468f52bc99 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-synacormedia-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/synacormedia/test-synacormedia-bid-request.json @@ -59,12 +59,7 @@ "user": { "buyeruid": "SCM-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-auction-tappx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-auction-tappx-request.json index 47492a49d71..a0dc82ec832 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-auction-tappx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-auction-tappx-request.json @@ -65,11 +65,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-tappx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-tappx-bid-request.json index 89ee69285d3..df98570852c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-tappx-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tappx/test-tappx-bid-request.json @@ -43,11 +43,6 @@ "user": { "buyeruid": "TX-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-auction-telaria-request.json b/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-auction-telaria-request.json index f98e514064f..2699198f3e3 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-auction-telaria-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-auction-telaria-request.json @@ -91,12 +91,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-telaria-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-telaria-bid-request-1.json index 3fb7054b871..f55efa396a0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-telaria-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/telaria/test-telaria-bid-request-1.json @@ -44,12 +44,7 @@ "user": { "buyeruid": "TL-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-auction-triplelift-request.json b/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-auction-triplelift-request.json index 8a2deca46c7..8a0ce522449 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-auction-triplelift-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-auction-triplelift-request.json @@ -62,11 +62,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-triplelift-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-triplelift-bid-request.json index 5eb728e4cd9..493d9e71ecc 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-triplelift-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/triplelift/test-triplelift-bid-request.json @@ -40,11 +40,6 @@ "user": { "buyeruid": "TL-UID", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, 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 7653b419c44..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" + } + } } } } @@ -36,10 +40,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, @@ -62,11 +62,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, 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 00a6d4ec1c0..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 @@ -63,7 +63,7 @@ "triplelift_native": [ { "uri": "{{ triplelift_native.exchange_uri }}", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-id\",\"native\":{\"request\":\"{\\\"ver\\\":\\\"1.1\\\",\\\"context\\\":1,\\\"contextsubtype\\\":11,\\\"plcmttype\\\":4,\\\"plcmtcnt\\\":1,\\\"assets\\\":[{\\\"id\\\":0,\\\"required\\\":1,\\\"title\\\":{\\\"len\\\":500}}]}\"},\"tagid\":\"foo\",\"ext\":{\"bidder\":{\"inventoryCode\":\"foo\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"test\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"T\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"aliases\":{\"appnexusAlias\":\"appnexus\",\"conversantAlias\":\"conversant\"},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"test-imp-id\",\"native\":{\"request\":\"{\\\"ver\\\":\\\"1.1\\\",\\\"context\\\":1,\\\"contextsubtype\\\":11,\\\"plcmttype\\\":4,\\\"plcmtcnt\\\":1,\\\"assets\\\":[{\\\"id\\\":0,\\\"required\\\":1,\\\"title\\\":{\\\"len\\\":500}}]}\"},\"tagid\":\"foo\",\"ext\":{\"bidder\":{\"inventoryCode\":\"foo\"}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"test\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"T\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"test-request-id\",\"seatbid\":[{\"seat\":\"958\",\"bid\":[{\"id\":\"7706636740145184841\",\"impid\":\"test-imp-id\",\"price\":0.5,\"adid\":\"29681110\",\"adm\":\"some-test-ad\",\"adomain\":[\"triplelift.com\"],\"iurl\":\"http://nym1-ib.adnxs.com/cr?id=29681110\",\"cid\":\"958\",\"crid\":\"29681110\",\"h\":250,\"w\":300}]}],\"bidid\":\"5778926625248726496\",\"cur\":\"USD\"}", "status": 200 } @@ -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" + } + } } } } @@ -104,11 +108,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -129,10 +128,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-triplelift-native-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-triplelift-native-bid-request.json index 439b6474237..d4a22534d32 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-triplelift-native-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tripleliftnative/test-triplelift-native-bid-request.json @@ -35,11 +35,6 @@ "user": { "buyeruid": "T", "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -60,10 +55,6 @@ "ext": { "prebid": { "debug": 1, - "aliases": { - "appnexusAlias": "appnexus", - "conversantAlias": "conversant" - }, "targeting": { "pricegranularity": { "precision": 2, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-auction-ttx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-auction-ttx-request.json index 0e6d21179aa..548d95c6df0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-auction-ttx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-auction-ttx-request.json @@ -64,12 +64,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -77,4 +72,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-ttx-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-ttx-bid-request-1.json index 95ee04bedbb..f82be234b00 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-ttx-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ttx/test-ttx-bid-request-1.json @@ -20,7 +20,6 @@ } ], "site": { - "id": "site-id", "domain": "example.com", "page": "http://www.example.com", "publisher": { @@ -41,12 +40,7 @@ "user": { "buyeruid": "TTX-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, @@ -90,4 +84,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-auction-ucfunnel-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-auction-ucfunnel-request.json index 8317dba4939..331e6857f42 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-auction-ucfunnel-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-auction-ucfunnel-request.json @@ -73,12 +73,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-ucfunnel-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-ucfunnel-bid-request.json index 6eb2dec557c..62747e3cc7c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-ucfunnel-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ucfunnel/test-ucfunnel-bid-request.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "UF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-auction-unruly-request.json b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-auction-unruly-request.json index 0805dd611c1..085bca4c46f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-auction-unruly-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-auction-unruly-request.json @@ -78,12 +78,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-1.json index f7a7377760f..b3fcaf9705e 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-1.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "UR-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-2.json index 974bf822a1e..96889f14e2b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/unruly/test-unruly-bid-request-2.json @@ -39,12 +39,7 @@ "user": { "buyeruid": "UR-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-auction-valueimpression-request.json b/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-auction-valueimpression-request.json index c8226ede61c..55afe5c957c 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-auction-valueimpression-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-auction-valueimpression-request.json @@ -92,12 +92,7 @@ "user": { "buyeruid": "VI-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-valueimpression-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-valueimpression-bid-request-1.json index ec42d77106c..a59a418916d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-valueimpression-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/valueimpression/test-valueimpression-bid-request-1.json @@ -58,12 +58,7 @@ "user": { "buyeruid": "VI-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-auction-verizonmedia-request.json b/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-auction-verizonmedia-request.json index af571db17df..a7ea628e575 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-auction-verizonmedia-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-auction-verizonmedia-request.json @@ -63,12 +63,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-verizonmedia-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-verizonmedia-bid-request-1.json index ce7df815bbc..50ae26dd237 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-verizonmedia-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/verizonmedia/test-verizonmedia-bid-request-1.json @@ -43,12 +43,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-1.json index af712452338..b17816f4159 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-1.json @@ -233,8 +233,8 @@ } ], "site": { - "domain": "example.com", - "page": "prebid.com", + "domain": "prebid.com", + "page": "https://prebid.com", "content": { "episode": 6, "title": "episodeName", @@ -248,17 +248,29 @@ } }, "device": { - "ua": "userAgent", - "ip": "193.168.244.0" + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "dnt": 33, + "lmt": 44, + "ip": "123.145.167.10", + "devicetype": 1, + "os": "mac os", + "h": 480, + "w": 640, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" }, "user": { + "buyeruid": "appnexus", "yob": 1991, "gender": "F", - "keywords": "Hotels, Travelling" - }, - "regs": { + "keywords": "Hotels, Travelling", "ext": { - "us_privacy": "1YNN" + "consent": "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA" } }, "at": 1, @@ -266,6 +278,12 @@ "cur": [ "USD" ], + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1YNN" + } + }, "ext": { "prebid": { "targeting": { @@ -279,11 +297,23 @@ "precision": 2 }, "includewinners": true, - "includebidderkeys": true + "includebidderkeys": true, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "with_category": true + } }, "cache": { "vastxml": {} + }, + "channel": { + "name": "web" } + }, + "appnexus": { + "include_brand_category": true, + "brand_category_uniqueness": true } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-2.json index 991c4ddf098..7fad685a3cd 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-bid-request-2.json @@ -26,8 +26,8 @@ } ], "site": { - "domain": "example.com", - "page": "prebid.com", + "domain": "prebid.com", + "page": "https://prebid.com", "content": { "episode": 6, "title": "episodeName", @@ -41,17 +41,29 @@ } }, "device": { - "ua": "userAgent", - "ip": "193.168.244.0" + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "dnt": 33, + "lmt": 44, + "ip": "123.145.167.10", + "devicetype": 1, + "os": "mac os", + "h": 480, + "w": 640, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" }, "user": { + "buyeruid": "appnexus", "yob": 1991, "gender": "F", - "keywords": "Hotels, Travelling" - }, - "regs": { + "keywords": "Hotels, Travelling", "ext": { - "us_privacy": "1YNN" + "consent": "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA" } }, "at": 1, @@ -59,6 +71,12 @@ "cur": [ "USD" ], + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1YNN" + } + }, "ext": { "prebid": { "targeting": { @@ -72,11 +90,23 @@ "precision": 2 }, "includewinners": true, - "includebidderkeys": true + "includebidderkeys": true, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "with_category": true + } }, "cache": { "vastxml": {} + }, + "channel": { + "name": "web" } + }, + "appnexus": { + "include_brand_category": true, + "brand_category_uniqueness": true } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-request.json b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-request.json index 8c7abb10755..f1d90701038 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-request.json @@ -20,16 +20,11 @@ ] }, "site": { - "page": "prebid.com" + "page": "https://prebid.com" }, "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" - }, - "gdpr": { - "consentrequired": false, - "consentstring": "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA" + "ext": { + "consent": "BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA" }, "yob": 1991, "gender": "F", @@ -37,11 +32,12 @@ }, "regs": { "ext": { + "gdpr": 1, "us_privacy": "1YNN" } }, "tmax": 5000, - "device11": { + "device": { "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", "ip": "123.145.167.10", "devicetype": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-response-empty.json b/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-response-empty.json deleted file mode 100644 index b773ce30155..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/video/test-video-appnexus-response-empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "adPods": [] -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/visx/test-auction-visx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/visx/test-auction-visx-request.json index a39a7602e77..a31f4b23855 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/visx/test-auction-visx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/visx/test-auction-visx-request.json @@ -70,12 +70,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/visx/test-visx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/visx/test-visx-bid-request.json index fcee8a54f73..acd4d80c313 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/visx/test-visx-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/visx/test-visx-bid-request.json @@ -47,12 +47,7 @@ "user": { "buyeruid": "VISX-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-auction-vrtcal-request.json b/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-auction-vrtcal-request.json index c29f63cbb11..6e65e13fb4a 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-auction-vrtcal-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-auction-vrtcal-request.json @@ -59,12 +59,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-vrtcal-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-vrtcal-bid-request-1.json index 4437d138db1..7a9dede97f5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-vrtcal-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/vrtcal/test-vrtcal-bid-request-1.json @@ -30,12 +30,7 @@ "user": { "buyeruid": "VR-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-auction-yeahmobi-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-auction-yeahmobi-request.json index ee088e56217..354d2be8365 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-auction-yeahmobi-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-auction-yeahmobi-request.json @@ -53,12 +53,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { @@ -100,4 +95,4 @@ "auctiontimestamp": 1000 } } -} \ No newline at end of file +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-yeahmobi-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-yeahmobi-bid-request.json index 4ff2144a2bc..b94c172d60b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-yeahmobi-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yeahmobi/test-yeahmobi-bid-request.json @@ -44,12 +44,7 @@ "user": { "buyeruid": "YM-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 1b1f1059fed..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" + } + } } } } @@ -82,11 +86,6 @@ }, "user": { "ext": { - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - }, "consent": "consentValue" } }, @@ -95,4 +94,4 @@ "gdpr": 0 } } -} \ No newline at end of file +} 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 4b56af12471..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" + } + } } } }, @@ -147,7 +151,6 @@ } }, "site": { - "domain": "example.com", "ext": { "amp": 0 }, @@ -164,12 +167,7 @@ "tmax": 5000, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-auction-yieldmo-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-auction-yieldmo-request.json index 25cfb2c200d..5dd12d1e5b5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-auction-yieldmo-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-auction-yieldmo-request.json @@ -62,12 +62,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-yieldmo-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-yieldmo-bid-request-1.json index 2592271d1c0..456dec57676 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-yieldmo-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldmo/test-yieldmo-bid-request-1.json @@ -37,12 +37,7 @@ "user": { "buyeruid": "YM-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-auction-yieldone-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-auction-yieldone-request.json index ce0f887b9df..dae78d61fbe 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-auction-yieldone-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-auction-yieldone-request.json @@ -62,12 +62,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-yieldone-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-yieldone-bid-request.json index 97ac27767a4..2dd1d05c05b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-yieldone-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/yieldone/test-yieldone-bid-request.json @@ -41,12 +41,7 @@ "user": { "buyeruid": "YD-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, 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 0f1f2f26a0c..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 + } + } } } } @@ -91,12 +99,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "regs": { 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 36ff7f81ffa..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 @@ -103,13 +103,13 @@ "zeroclickfraud": [ { "uri": "{{ zeroclickfraud.exchange_uri }}?sid=2", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId002\",\"video\":{\"mimes\":[\"video/mp4\"],\"w\":300,\"h\":250,\"pos\":1},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":2}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"ZF-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"currency\":{\"rates\":{\"EUR\":{\"USD\":1.2406},\"USD\":{\"EUR\":0.811}}},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId002\",\"video\":{\"mimes\":[\"video/mp4\"],\"w\":300,\"h\":250,\"pos\":1},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":2}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"ZF-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"currency\":{\"rates\":{\"EUR\":{\"USD\":1.2406},\"USD\":{\"EUR\":0.811}}},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"bid002\",\"impid\":\"impId002\",\"price\":9.99,\"crid\":\"crid002\",\"cid\":\"cid002\",\"adomain\":[\"psacentral.org\"],\"h\":250,\"w\":300}],\"seat\":\"zeroclickfraud\"}]}", "status": 200 }, { "uri": "{{ zeroclickfraud.exchange_uri }}?sid=1", - "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId001\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}]},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":1}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"ZF-UID\",\"ext\":{\"consent\":\"consentValue\",\"digitrust\":{\"id\":\"id\",\"keyv\":123,\"pref\":0}}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"currency\":{\"rates\":{\"EUR\":{\"USD\":1.2406},\"USD\":{\"EUR\":0.811}}},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", + "requestbody": "{\"id\":\"tid\",\"imp\":[{\"id\":\"impId001\",\"banner\":{\"format\":[{\"w\":300,\"h\":250}]},\"ext\":{\"bidder\":{\"host\":\"localhost:8090\",\"sourceId\":1}}}],\"site\":{\"domain\":\"example.com\",\"page\":\"http://www.example.com\",\"publisher\":{\"id\":\"publisherId\"},\"ext\":{\"amp\":0}},\"device\":{\"ua\":\"userAgent\",\"dnt\":2,\"ip\":\"193.168.244.1\",\"pxratio\":4.2,\"language\":\"en\",\"ifa\":\"ifaId\"},\"user\":{\"buyeruid\":\"ZF-UID\",\"ext\":{\"consent\":\"consentValue\"}},\"at\":1,\"tmax\":5000,\"cur\":[\"USD\"],\"source\":{\"fd\":1,\"tid\":\"tid\"},\"regs\":{\"ext\":{\"gdpr\":0}},\"ext\":{\"prebid\":{\"debug\":1,\"currency\":{\"rates\":{\"EUR\":{\"USD\":1.2406},\"USD\":{\"EUR\":0.811}}},\"targeting\":{\"pricegranularity\":{\"precision\":2,\"ranges\":[{\"max\":20,\"increment\":0.1}]},\"includewinners\":true,\"includebidderkeys\":true},\"cache\":{\"bids\":{},\"vastxml\":{\"ttlseconds\":120}},\"auctiontimestamp\":1000,\"channel\":{\"name\":\"web\"}}}}", "responsebody": "{\"id\":\"tid\",\"seatbid\":[{\"bid\":[{\"id\":\"bid001\",\"impid\":\"impId001\",\"price\":7.77,\"adid\":\"adid001\",\"crid\":\"crid001\",\"cid\":\"cid001\",\"adm\":\"adm001\",\"h\":250,\"w\":300}],\"seat\":\"zeroclickfraud\"}]}", "status": 200 } @@ -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 + } + } } } } @@ -173,12 +181,7 @@ }, "user": { "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-1.json index e60f0063394..d578b675833 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-1.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "ZF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-2.json index 3129406631f..871c2f75cd1 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/zeroclickfraud/test-zeroclickfraud-bid-request-2.json @@ -40,12 +40,7 @@ "user": { "buyeruid": "ZF-UID", "ext": { - "consent": "consentValue", - "digitrust": { - "id": "id", - "keyv": 123, - "pref": 0 - } + "consent": "consentValue" } }, "at": 1, diff --git a/src/test/resources/org/prebid/server/it/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 2107574be47..d36d21f2664 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -15,7 +15,7 @@ adapters.adhese.endpoint=http://localhost:8090/adhese-exchange adapters.adhese.pbs-enforces-gdpr=true adapters.adhese.usersync.url=//adhese-usersync adapters.adkerneladn.enabled=true -adapters.adkerneladn.endpoint=http://localhost:8090/adkernelAdn-exchange?account= +adapters.adkerneladn.endpoint=http://localhost:8090/adkernelAdn.{{Host}}?account={{PublisherID}} adapters.adkerneladn.pbs-enforces-gdpr=true adapters.adkerneladn.usersync.url=//adkernelAdn-usersync adapters.adkernel.enabled=true @@ -62,6 +62,10 @@ adapters.aja.enabled=true adapters.aja.endpoint=http://localhost:8090/aja adapters.aja.pbs-enforces-gdpr=true adapters.aja.usersync.url=//v1/sync/ssp?ssp= +adapters.amx.enabled=true +adapters.amx.endpoint=http://localhost:8090/amx-exchange +adapters.amx.pbs-enforces-gdpr=true +adapters.amx.usersync.url=//amx-usersync adapters.appnexus.enabled=true adapters.appnexus.endpoint=http://localhost:8090/appnexus-exchange adapters.appnexus.pbs-enforces-gdpr=true @@ -103,6 +107,7 @@ adapters.consumable.usersync.url=//consumable-usersync adapters.conversant.enabled=true adapters.conversant.endpoint=http://localhost:8090/conversant-exchange adapters.conversant.pbs-enforces-gdpr=true +adapters.conversant.generate-bid-id=false adapters.conversant.usersync.url=//conversant-usersync adapters.datablocks.enabled=true adapters.datablocks.endpoint=http://{{Host}}/datablocks-exchange?sid={{SourceId}} @@ -210,6 +215,10 @@ adapters.ninthdecimal.enabled=true adapters.ninthdecimal.endpoint=http://localhost:8090/ninthdecimal-exchange?pubid= adapters.ninthdecimal.pbs-enforces-gdpr=true adapters.ninthdecimal.usersync.url=//ninthdecimal-usersync +adapters.nobid.enabled=true +adapters.nobid.endpoint=http://localhost:8090/nobid-exchange?pubid= +adapters.nobid.pbs-enforces-gdpr=true +adapters.nobid.usersync.url=//nobid-usersync adapters.openx.enabled=true adapters.openx.endpoint=http://localhost:8090/openx-exchange adapters.openx.pbs-enforces-gdpr=true @@ -280,6 +289,10 @@ adapters.sharethrough.enabled=true adapters.sharethrough.endpoint=http://localhost:8090/sharethrough-exchange adapters.sharethrough.pbs-enforces-gdpr=true adapters.sharethrough.usersync.url=//sharethrough-usersync +adapters.silvermob.enabled=true +adapters.silvermob.endpoint=http://localhost:8090/silvermob-exchange +adapters.silvermob.pbs-enforces-gdpr=true +adapters.silvermob.usersync.url=//silvermob-usersync adapters.tappx.enabled=true adapters.tappx.endpoint=http:// adapters.tappx.pbs-enforces-gdpr=true @@ -366,7 +379,7 @@ default-timeout-ms=2000 timeout-adjustment-ms=0 auction.default-timeout-ms=2000 auction.timeout-adjustment-ms=0 -auction.id-generator-type=none +auction.generate-source-tid=false currency-converter.external-rates.enabled=true currency-converter.external-rates.url=http://localhost:8090/currency-rates amp.default-timeout-ms=2000